---
title: "MediaConductor"
description: "Domain facade for scripted media plus docs Audible (radio-host TTS), Visual walkthroughs, and Agent NODUS — SHA-keyed enrichment with background first-load generation."
locale: "en"
---
# MediaConductor

MediaConductor is Ring Platform's **domain facade** for multi-step media generation. It does not reimplement providers — it compiles prompts and delegates to `VideoConductor`, `ImageConductor`, `AudioConductor`, and `TextConductor`. Its most visible surface is **docs article media**: every docs page can offer **Audible** (radio-host narration → TTS), **Visual** (optional member+ walkthrough video), and **Agent** (minimized NODUS at `/docs/.../nodus.json`, indexed from `/llms.txt`).

> **Info**
> Use **Founder** / **Developer** tabs in the docs sidebar to filter this page. Founders see the article-header buttons and cost controls; developers see enrichment modules, SHA cache contracts, `after()` background work, and verified routes.

## What it powers

| Surface | Entry | Output |
|---------|-------|--------|
| Docs Audible | Header **Audible** → `generateDocNarration` → `ensureTtsAudio` | Radio-host `audible-text` → TTS (`tts-audio`), public, SHA-cached |
| Docs Visual | Header **Visual** → `generateDocWalkthrough` | Summary TTS + optional draft video (video = member+) |
| Docs Agent | Header **Create** / **Agent** → `ensureLlmText` | Minimized `docs_article` NODUS (`llm-text`) + clipboard |
| Agent index | `/llms.txt` | Markdown index linking each article's `/nodus.json` |
| Background enrich | RSC `after(ensureDocsArticleEnrichmentBackground)` | Audible + Agent warmed on first article load when missing |
| Scripted media | `MediaConductor.generateScript` / `generateClip` | Per-clip video from a `ScriptedVideoGenerationRequest` |

### For founders

## Why this matters for your clone

- **Docs that talk** — the article header **Audible** button plays a warm radio-host script (cold open, gist, pitfalls, next step) — not a robotic dump of every heading and table. Expand **Show transcript** beside the player when you want the spoken text.
- **Pay once, reuse forever** — first generation stores `audible-text` + MP3 keyed by a full content SHA-256. Later visits reuse the file. Editing the MDX changes the SHA, so narration regenerates (with an align pass that weaves new facts into the prior script).
- **Ready while you read** — on first open, the server schedules background enrichment. Buttons show **Generating…** and the client polls every 2s until Audible and Agent are ready.
- **Agent-friendly** — `/llms.txt` lists docs; each page exposes `/nodus.json`. The **Create** button builds the payload once; **Agent** (+ copy) copies JSON or the absolute NODUS URL when ready.
- **Cost-controlled Visual** — Audible is public; **Visual** video requires a signed-in **member+** role so heavy video spend stays bounded.
- **One media brain** — the same conductor drives scripted marketing clips and docs walkthroughs via your configured image / video / audio / text providers.

  
- **[Wallet](/docs/features/wallet.md)** — Member roles that unlock Visual video walkthroughs.

  
- **[VideoConductor](/docs/features/video-conductor.md)** — The scripted-video engine MediaConductor delegates to.

  
- **[News](/docs/features/news.md)** — NewsConductor — sibling facade for article media enrichment.

> **Tip**
> Look at the top-right of any docs article: **Audible** · **Visual** · **Create** / **Agent**. If you just opened a page for the first time, wait for Generating… to finish — Play will then hit the cache.

### For developers

## Architecture

`lib/media/conductor/media-conductor.ts` exports the `MediaConductor` object. Docs **Audible** / **Agent** prefer the enrichment SSOT in `lib/docs/docs-article-enrichment.ts` (TextConductor radio-host → AudioConductor TTS → Filebase). Walkthrough **Visual** still goes through `MediaConductor.generateDocWalkthrough` and mirrors into `generated_docs_media`.

### Modules

| Module | Path | Role |
|--------|------|------|
| MediaConductor | `lib/media/conductor/media-conductor.ts` | `generateClip`, `generateScript`, `generateDocWalkthrough` |
| Docs enrichment SSOT | `lib/docs/docs-article-enrichment.ts` | `ensureAudibleText`, `ensureTtsAudio`, `ensureLlmText`, `getDocsArticleMediaStatus`, `ensureDocsArticleEnrichmentBackground` |
| Docs media actions | `app/_actions/docs-media.ts` | `getDocMediaStatus`, `ensureDocMediaBackground`, `generateDocNarration`, `createOrGetDocAgentNodus`, `generateDocWalkthrough` |
| Article chrome | `components/docs/docs-article-shell.tsx` | Loads status; `after()` schedules background enrich when `shouldEnrich` |
| Header buttons | `components/docs/docs-article-media-actions.tsx` | Audible / Visual / Agent UI; 2s poll while generating |
| Legacy URL mirror | `lib/docs/docs-media-cache.ts` | `generated_docs_media` rows (short hash mirror for walkthrough / narration URLs) |
| Agent NODUS API | `app/api/docs/nodus/[locale]/[[...slug]]/route.ts` | Public JSON via `next.config.mjs` rewrites |
| LLM index | `app/llms.txt/route.ts` | `/llms.txt` curated Markdown index |
| Schema | `data/migrations/045_docs_media_and_enrichment.sql` | `generated_docs_media` + `docs_article_enrichment` (applied local + prod) |
| Delegates | `lib/video`, `lib/images`, `lib/audio`, `lib/text` | Video / Image / Audio / Text conductors |

### Docs media flow

```mermaid
sequenceDiagram
  participant Shell as docs-article-shell RSC
  participant After as Next.js after()
  participant UI as DocsArticleMediaActions
  participant SA as docs-media actions
  participant EN as docs-article-enrichment
  participant TC as TextConductor
  participant AC as AudioConductor
  participant DB as docs_article_enrichment

  Shell->>EN: getDocsArticleMediaStatus
  alt shouldEnrich
    Shell->>After: ensureDocsArticleEnrichmentBackground
    After->>EN: ensureTtsAudio then ensureLlmText
  end
  UI->>SA: poll getDocMediaStatus every 2s
  SA->>EN: status snapshot
  Note over UI: Audible click if still missing
  UI->>SA: generateDocNarration
  SA->>EN: ensureTtsAudio
  EN->>TC: radio-host or ALIGN_EXISTING_AUDIBLE
  TC-->>EN: audibleText
  EN->>AC: synthesize objectKey docs/sha256.mp3
  AC-->>EN: audioUrl
  EN->>DB: save enrichment + invalidate history
  SA-->>UI: audioUrl + status
```

### SHA-256 cache contract

- **Collections:** `docs_article_enrichment` (audible-text / tts-audio / llm-text + `history`) and `generated_docs_media` (URL mirror for narration/walkthrough) via `db()` — registered in `BackendSelector` + `PostgreSQLAdapter`.
- **Content SHA:** `hashArticleContentSha256(locale, slug, plain)` = full `sha256(\`${locale}:${slug.join('/')}:${plain}\`)`. Ready state requires `contentSha256` match on enrichment + `ttsAudio.contentSha256`.
- **Row id:** `docs-enrich-{locale}-{sha256(slug).slice(0,24)}` (stable per locale/slug; content SHA lives in the document body).
- **TTS objectKey:** `{AUDIO_STORAGE_PREFIX||generated/audio}/docs/{fullSha256}.mp3` — reuse only when URL, objectKey, and SHA all match; otherwise regenerate.
- **SHA change:** prior `ttsAudio` / `audibleText` / `llmText` pushed to `history.audibles` / `history.llmTexts` with `status: 'invalidated'`. New audible uses TextConductor `TASK_MODE: ALIGN_EXISTING_AUDIBLE` when a prior script exists.
- **Agent path:** `ensureLlmText` builds minimized NODUS (`object_type: docs_article`, `source_content_hash`). Public URLs: `/docs//nodus.json` and `/:locale/docs/.../nodus.json` → rewrite → API route.
- **Index:** `GET /llms.txt` lists absolute `/nodus.json` links (DEFAULT_LOCALE scan).

### Article header UX (verified)

| Control | Missing | Generating | Ready |
|---------|---------|------------|-------|
| **Audible** | Triggers `generateDocNarration` | Spinner label | Plays `[data-docs-audible-player]`; optional show/hide transcript |
| **Visual** | `generateDocWalkthrough({ enableVideo: true })` | Spinner | Shows video when returned; member+ gated |
| **Agent** | Label **Create** (`agentCreate`) → `createOrGetDocAgentNodus` then clipboard | Spinner | Label **Agent** + copy icon — copies NODUS JSON or absolute URL |

Client fallback: if `shouldEnrich` and RSC `after()` did not finish, `ensureDocMediaBackground` runs from the client; poll interval is **2000 ms**.

### Access rules (verified)

**Audible is public**

`generateDocNarration` has no auth gate — any visitor may trigger it; cost is amortized via the SHA cache / background enrich.

**Visual video requires member+**

In `generateDocWalkthrough`, `enableVideo: true` requires a session (`AUTH_REQUIRED`) and `hasMemberPrivileges(session.user.role)` (`MEMBER_REQUIRED`). Audio-only walkthroughs have no role gate.

**Draft quality for docs video**

`MediaConductor.generateDocWalkthrough` requests `qualityMode: 'draft'`, `aspectRatio: '16:9'`, `purpose: 'docs-walkthrough'`, and persists to Filebase.

### Conductor family

MediaConductor is one of several thin domain facades that reuse the shared media conductors without duplicating provider logic:

| Conductor | Path | Role |
|-----------|------|------|
| MediaConductor | `lib/media/conductor/media-conductor.ts` | Scripted clips + docs walkthroughs |
| NewsConductor | `features/news/conductor/news-conductor.ts` | Article generation, translation, publish-time media enrichment (TL;DR + TTS + featured image) |
| WalletConductor | `features/wallet/conductor/wallet-conductor.ts` | Multi-step wallet ops facade over wallet server actions |

## Related documentation

- [features/doc-system](/docs/features/doc-system.md) — Prerequisite: MDX docs chrome and audience-curated sidebar that host the Audible / Visual / Agent header.

- [features/video-conductor](/docs/features/video-conductor.md) — Depends-on: VideoConductor is the draft walkthrough engine MediaConductor calls for Visual.

- [features/generative-media](/docs/features/generative-media.md) — See-also: Generative Gallery Upload \| Generate — sibling media surface, not docs walkthrough.

- [features/news](/docs/features/news.md) — Same-workflow: NewsConductor enriches news articles with TTS and images using the same conductor family.

- [features/wallet](/docs/features/wallet.md) — Next-step: member roles that unlock Visual video generation.

- [development/scripted-media-pipeline](/docs/development/scripted-media-pipeline.md) — Deep-dive: scripted clip pipeline behind MediaConductor.generateScript / generateClip.

1 year)"
  description="Invalidated audibles and llm-text are retained on docs_article_enrichment.history (and Filebase objects). Ship a collector that garbage-collects invalidated history rows and ring-filebase objects older than 1 year — TODO(gc) in docs-article-enrichment.ts."
  implementationCost={28}
  labels={['docs', 'media', 'ops', 'storage']}
/>
