Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
5082998
Add HTML build script and shell template for standalone viewer
BirdInTheTree Apr 9, 2026
1338c20
fix: build.py concatenates all JS parts, not just app.js
BirdInTheTree Apr 9, 2026
bc7ec07
Port CSS from tvplotlines-app for standalone HTML viewer
BirdInTheTree Apr 9, 2026
8447b82
Add grid rendering and app init for standalone HTML viewer
BirdInTheTree Apr 9, 2026
29a290f
Add analytics rendering with 5 sections ported from tvplotlines-app
BirdInTheTree Apr 9, 2026
5cdcd62
Add router, localStorage Store, toolbar, and JSON import
BirdInTheTree Apr 9, 2026
fd2deed
Add export functions: JSON, TXT, CSV, Final Draft .fdx
BirdInTheTree Apr 9, 2026
d84204d
Add LLM pipeline for browser-side plotline extraction
BirdInTheTree Apr 9, 2026
70c21ea
fix: dark mode CSS, inciting_incident styling, analytics header, epis…
BirdInTheTree Apr 10, 2026
a7b1f16
Add --html and --html-output flags to CLI run command
BirdInTheTree Apr 10, 2026
bbc476a
Add minimal welcome screen with first-visit detection
BirdInTheTree Apr 10, 2026
dc18c41
Add onboarding animation that demos full pipeline on real UI
BirdInTheTree Apr 10, 2026
51f1cde
refactor: consolidate hollywood and narratology prompts under one sys…
BirdInTheTree Apr 15, 2026
662965d
refactor(narratology): thread stable event ids through passes 2–6
BirdInTheTree Apr 15, 2026
58fec56
feat(viewer): turn the HTML viewer into an end-to-end app
BirdInTheTree Apr 15, 2026
2f68591
feat: v2 scope — narratology runtime, prompt inlining, native PDF, kn…
BirdInTheTree Apr 15, 2026
1ccd849
chore(repo): pull internal docs back to 3-resources, rewrite README
BirdInTheTree Apr 15, 2026
ac07dda
docs: sync docs/ with the current code (hollywood + narratology)
BirdInTheTree Apr 15, 2026
16aedf3
chore: drop gitignore entries for drafts now moved out
BirdInTheTree Apr 15, 2026
ce73086
docs: add AI disclosure, methodology, and viewer footer
BirdInTheTree Apr 15, 2026
a023592
docs: add 411@alpineanimation.ch as contact
BirdInTheTree Apr 15, 2026
9209124
docs: obfuscate contact email against naive scrapers
BirdInTheTree Apr 15, 2026
2cf4f66
docs: switch contact to purpose-specific alias
BirdInTheTree Apr 15, 2026
b77d023
refactor: rename tvplotlines → tvplot
BirdInTheTree Apr 15, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 24 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,27 @@
# Changelog

## Unreleased
## 0.2.0 — 2026-04-15

### Changed — BREAKING
- **Package renamed `tvplotlines` → `tvplot`.** PyPI package, import path, CLI command, env vars, and localStorage keys all move. Migration: `pip install tvplot` and replace imports `from tvplotlines …` → `from tvplot …` and CLI invocations `tvplotlines run` → `tvplot run`. Env vars `TVPLOTLINES_OUTPUT_DIR` / `TVPLOTLINES_SYNOPSES_DIR` renamed to `TVPLOT_OUTPUT_DIR` / `TVPLOT_SYNOPSES_DIR`. `tvplotlines 0.1.0` on PyPI will be yanked with a "renamed to tvplot" notice.

### Added
- **AI-use, privacy, and methodology docs** — `docs/ai-disclosure.md` documents the data flow (nothing reaches Alpine Animation — your inputs and API key travel straight from your machine to the LLM vendor), lists Anthropic's and OpenAI's applicable policies, and records a paper-trail for vendor-policy compliance. `docs/methodology.md` is the plain-language Art. 50 description of how the pipeline decides, what it can't do, and where to read the code. README links both, and the standalone HTML viewer carries a dismissible AI-assisted-content footer.
- **Narratology pipeline — runnable**. New module `src/tvplot/narratology.py` implements all six passes (context, fabula, actants, story, arc, review). Results are emitted in the existing `TVPlotlinesResult` shape so the HTML viewer renders hollywood and narratology output unchanged. Actants (`who_chases`, `what_they_chase`, `stands_in_the_way`, `who_wins_if_it_works`) map onto `hero`, `goal`, `obstacle`, `stakes`. Pass 5 arc functions land in `Event.plot_fn`. Pass 6 verdicts apply in-place (MERGE/DROP/REASSIGN/REFUNCTION) and reviewed ranks override computed ranks.
- **Narratology in the browser viewer**. `runPipelineNarratology` in `pipeline.js` mirrors the Python orchestrator, runs all six passes via browser LLM calls (per-episode passes parallelised with `Promise.all`), and produces the same hollywood-compatible shape. `runPipeline` dispatches on the user's system choice. The "Narratology" option in LLM settings is now enabled.
- **Prompts inlined at build time**. `src/tvplot/html/build.py` reads every `prompts/{hollywood,narratology}/*.md` and injects them into the HTML as a `_PROMPTS` object, so the viewer no longer carries a hardcoded copy that could drift from the source.
- **"Analyze a series" entry flow** in the HTML viewer. Welcome screen now takes a show name + season; the viewer asks the configured LLM for episode synopses, shows them in an editable preview, then runs the normal pipeline. Download the single HTML and you have an end-to-end app. Fallback — drag-and-drop `.txt` synopses — still works.
- **Native PDF export** via lazy-loaded jsPDF (CDN). Generates a vector PDF directly from the loaded result: title page, per-plotline sections with actant/story-DNA fields and event lists, and an episode-by-episode appendix. Falls back to the browser print stack if the library can't be fetched (offline).
- **Knowledge consolidated into the repo**. `3-resources/tvplot/*` → `docs/knowledge/` with `decisions/` (ADR 001-005), `specs/` (architecture, per-pass specs, mas4bw audit), `theory/`, `prompting/`, `results/` (reference BB S01-S05 outputs), `v3_narratology/` (67 evergreen notes across 9 sources), and `archive/` (old autoresearch + plans). Personal artifacts (the book and friends-post) stay in `3-resources/tvplot/`.

### Changed
- **Prompt layout**: `prompts_en/` → `prompts/hollywood/`. Added `prompts/narratology/` from the former sister project, bundling eight structuralist prompts (context, fabula, actants, story, arc, review, synopses_writer, glossary). `docs/layered-schema.md` documents the shared `base + layers` JSON.
- **Library API**: `get_plotlines(..., lang=)` replaced by `get_plotlines(..., system=)` (values: `"hollywood"` default, `"narratology"`). `LLMConfig.lang` → `LLMConfig.system`. CLI: `tvplot run --lang` → `--system`; `write-synopses --system` added.
- Narratology pipeline is not yet runnable — `system="narratology"` raises `NotImplementedError` with a clear message. Prompts are in place; runners come in v2.

### Removed
- **Russian prompts** (`prompts_ru/`) — out of scope. Will be reintroduced once both analysis systems stabilise in English.
- Sister repo `tvplot_narratology/` merged into this repo under `src/tvplot/prompts/narratology/`.

### Added
- **Pass 4: arc functions** (`plot_fn`) — each event gets a season-arc role alongside its episode function. Pass 4 runs per-plotline, sees episode functions as context.
Expand All @@ -15,7 +36,7 @@
- **CLI**: `--stop-after pass1` saves intermediate JSON, `--resume-from` resumes from it
- **CLI**: `--output-dir` saves timestamped copy of results
- **CLI**: `--no-glossary`, `--no-fandom`, `--fandom-wiki` flags for write-synopses
- **Environment variables**: `TVPLOTLINES_OUTPUT_DIR`, `TVPLOTLINES_SYNOPSES_DIR` — default output locations
- **Environment variables**: `TVPLOT_OUTPUT_DIR`, `TVPLOT_SYNOPSES_DIR` — default output locations
- **Rules and formulas reference**: `docs/formulas.md`
- Chain-of-thought nudge in all prompts before OUTPUT section

Expand Down Expand Up @@ -56,4 +77,4 @@ Initial open-source release.
- Multi-season continuity via `prior` parameter
- Providers: Anthropic (default), OpenAI, Ollama, DeepSeek, Groq, any OpenAI-compatible API
- Pass 2 modes: parallel, batch (50% cheaper), sequential
- CLI: `tvplotlines run`
- CLI: `tvplot run`
6 changes: 3 additions & 3 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
## Setup

```bash
git clone https://github.com/BirdInTheTree/tvplotlines.git
cd tvplotlines
git clone https://github.com/BirdInTheTree/tvplot.git
cd tvplot
pip install -e ".[dev]"
```

Expand All @@ -21,7 +21,7 @@ Tests use mocked LLM responses — no API key needed.
- Python 3.11+
- Type hints on public functions
- `verb_noun` naming: `extract_plotlines`, `detect_context`
- Prompts live in `src/tvplotlines/prompts_en/` as markdown files
- Prompts live in `src/tvplot/prompts_en/` as markdown files

## Pull requests

Expand Down
120 changes: 93 additions & 27 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,55 +1,84 @@
# tvplotlines
# tvplot

[![PyPI](https://img.shields.io/pypi/v/tvplotlines?cacheSeconds=3600)](https://pypi.org/project/tvplotlines/)
[![License](https://img.shields.io/github/license/BirdInTheTree/tvplotlines?cacheSeconds=3600)](LICENSE)
[![Python](https://img.shields.io/pypi/pyversions/tvplotlines?cacheSeconds=3600)](https://pypi.org/project/tvplotlines/)
[![PyPI](https://img.shields.io/pypi/v/tvplot?cacheSeconds=3600)](https://pypi.org/project/tvplot/)
[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
[![Python](https://img.shields.io/pypi/pyversions/tvplot?cacheSeconds=3600)](https://pypi.org/project/tvplot/)

Turn episode synopses into a writers' room corkboard — story engine, plotlines with their *story DNA* and *arcs* (see definitions in [glossary](src/tvplotlines/prompts_en/glossary.md)) — in one function call.
Turn episode synopses into a writers' room corkboard — story engine, plotlines with their *story DNA* and *arcs* (see [glossary](src/tvplot/prompts/hollywood/glossary.md)) — in one function call.

For TV researchers, screenwriters, and anyone building narrative analysis tools or a tv series.

<p align="center">
<a href="https://birdinthetree.github.io/plotter-app/">
<img src="docs/images/app-screenshot-1.png" alt="tvplotlines output — plotline×episode grid for Breaking Bad S01">
<img src="docs/images/app-screenshot-1.png" alt="tvplot output — plotline×episode grid for Breaking Bad S01">
</a>
<br>
<strong><a href="https://birdinthetree.github.io/plotter-app/">Try the interactive demo →</a></strong>
</p>

A naive LLM prompt covers 5–12% of a season's source material. tvplotlines covers **78–91%** — by separating *what* the model looks for (narrative theory) from *how* it calculates the results (code).
A naive LLM prompt covers 5–12% of a season's source material. tvplot covers **78–91%** — by separating *what* the model looks for (narrative theory) from *how* it calculates the results (code).

## Quick start
## Two ways to use it

**Library / CLI** — `pip install tvplot`, point it at synopsis files, get JSON.

**Standalone HTML viewer** — one self-contained `.html` file that runs the whole pipeline in your browser. Type a show name, the viewer asks the LLM for synopses, runs the analysis, and renders the grid. No server, no dependencies — download the file and it works.

## Quick start (CLI)

```bash
pip install tvplotlines
pip install tvplot
export ANTHROPIC_API_KEY=sk-ant-…
```

Working on your own series? Point it at your synopses — one `.txt` file per episode (see [Input format](#input-format)):

```bash
tvplotlines run my-series/
tvplot run my-series/
```

Want to analyze an existing show? The optional `writer` extension generates synopses from online sources:

```bash
pip install 'tvplotlines[writer]'
tvplotlines write-synopses "Mad Men" --season 1
tvplotlines run mad-men/
pip install 'tvplot[writer]'
tvplot write-synopses "Mad Men" --season 1
tvplot run mad-men/
```

Generate a standalone HTML viewer alongside the JSON:

```bash
tvplot run mad-men/ --html # writes mad-men.json + mad-men.html
tvplot run mad-men/ --html-output viewer.html
```

Multiple seasons are supported — plotline continuity is tracked across seasons (see [Python API](#python-api)).

Pre-computed results are in [`examples/results/`](examples/results/) — explore the output without spending API credits.

## Quick start (standalone viewer)

```bash
python -m tvplot.html.build --output tvplot.html
open tvplot.html
```

Then in the browser:

1. Click **LLM** in the toolbar, paste your Anthropic or OpenAI key, pick an analysis system.
2. On the welcome screen, type a show name and season → click **Analyze**.
3. The viewer asks the model for episode synopses, lets you preview/edit them, and runs the pipeline.
4. Export the result as JSON, CSV, Final Draft (`.fdx`), or PDF.

If the model doesn't know the show, drop your own `.txt` synopsis files via the **+ Load** button.

## What you get

One JSON file per season. Each contains:

- **Cast** — characters with aliases
- **Plotlines** — with A/B/C ranking, Story DNA (hero, goal, obstacle, stakes), and episode span
- **Events** — per-episode, assigned to plotlines with narrative function (setup → inciting incident → escalation → crisis → climax → resolution)
- **Events** — per-episode, assigned to plotlines with episode-level function and season-level arc function

<details>
<summary>Example: Breaking Bad S01 (truncated)</summary>
Expand Down Expand Up @@ -85,6 +114,7 @@ One JSON file per season. Each contains:
"event": "During the meth lab raid, Walt spots his former student Jesse escaping through a window",
"plotline_id": "empire",
"function": "inciting_incident",
"plot_fn": "inciting_incident",
"characters": ["walt", "jesse"]
}
]
Expand All @@ -97,9 +127,11 @@ One JSON file per season. Each contains:

## How it works

### Plotline extraction (`tvplotlines run`)
You pick an **analysis system** with `--system` (CLI) or the LLM Settings dialog (viewer). Both systems share the same input format and output the same `TVPlotlinesResult` shape, so the viewer renders either one.

Five LLM passes, each with a specialized prompt:
### Hollywood — screenwriting model (default)

Story DNA — hero, goal, obstacle, stakes — and Freytag's seven dramatic functions. Five LLM passes:

| Pass | Role | Output | Calls |
|------|------|--------|-------|
Expand All @@ -111,15 +143,32 @@ Five LLM passes, each with a specialized prompt:

Pass 1 runs 3× in parallel and picks the most common plotline set. Pass 3 sees the full picture no earlier pass had and corrects structural problems. Pass 4 assigns arc-level functions — an event that was a climax in episode 3 might be an escalation in the season-long arc.

### Narratology — structuralist model

Bal's three layers, Greimas's actant model (subject/object/helper/opponent/power/receiver), Bremond's narrative cycle, Labov's reportability. Six LLM passes:

| Pass | Role | Output | Calls |
|------|------|--------|-------|
| **1 context** | Format, story schema, breach, protagonists | Show-level context | 1 |
| **2 fabula** | Per-episode events as state transitions, with stable ids | Bare events + cast | 1 per episode |
| **3 actants** | Plotlines via the actant model (anti-subject test) | Plotlines | 1 |
| **4 story** | Per-event episode function and direction; theme | Story-level event tags | 1 per episode |
| **5 arc** | Season arc function, drive vs texture, MRE per plotline | Season-level event tags | 1 per plotline |
| **6 review** | Verdicts and ranks | Structural corrections | 1 |

The actant fields (`who_chases`, `what_they_chase`, `stands_in_the_way`, `who_wins_if_it_works`) map onto the same `hero / goal / obstacle / stakes` slots, so existing tools that consume `TVPlotlinesResult` see no schema change.

The schema document at [`docs/layered-schema.md`](docs/layered-schema.md) describes a richer `base + layers` JSON form for keeping multiple systems' analyses of the same season side by side.

### Synopsis generation (`write-synopses`)

The `writer` extension collects raw episode data from online sources, then rewrites each episode into a full synopsis via LLM — ensuring every sentence is a beat (conflict or change), with explicit causality and character names.

Tested with Claude Sonnet (default). OpenAI and Ollama supported for both pipelines. Ollama defaults to Qwen 2.5 14B — chosen to run on CPU without a GPU. Quality is noticeably lower than cloud models but works for experimentation.
Tested with Claude Sonnet (default). OpenAI and Ollama supported for both pipelines and both analysis systems. Ollama defaults to Qwen 2.5 14B — chosen to run on CPU without a GPU. Quality is noticeably lower than cloud models but works for experimentation.

## Input format

One `.txt` file per episode. Include `S01E01`, `S01E02`, etc. in the filename. Each file is a plain-text (.txt) synopsis — 150–500 words covering the main events.
One `.txt` file per episode. Include `S01E01`, `S01E02`, etc. in the filename. Each file is a plain-text synopsis — 150–500 words covering the main events.

```
breaking-bad/
Expand All @@ -131,46 +180,63 @@ breaking-bad/
The folder name becomes the show title. Override with `--show`:

```bash
tvplotlines run got/ --show "Game of Thrones"
tvplot run got/ --show "Game of Thrones"
```

## Python API

```python
from tvplotlines import get_plotlines
from tvplot import get_plotlines

s01 = get_plotlines("Breaking Bad", season=1, episodes=season_1_synopses)
s02 = get_plotlines("Breaking Bad", season=2, episodes=season_2_synopses, prior=s01)

# Or pick the analysis system explicitly
result = get_plotlines(
"Mad Men", season=1, episodes=synopses, system="narratology",
)
```

Pass `prior` to track plotline continuity across seasons.

## LLM providers

```bash
tvplotlines run breaking-bad/ # Anthropic (default)
tvplotlines run breaking-bad/ --provider openai # OpenAI
tvplotlines run breaking-bad/ --provider ollama # Ollama (local, free)
tvplot run breaking-bad/ # Anthropic (default)
tvplot run breaking-bad/ --provider openai # OpenAI
tvplot run breaking-bad/ --provider ollama # Ollama (local, free)
tvplot run breaking-bad/ --system narratology # narratology pipeline
```

See [docs/api.md](docs/api.md) for full API reference.

## Key concepts

- **Plotline** — a narrative thread across episodes (e.g. "Walt: Empire")
- **Story DNA** — hero, goal, obstacle, stakes
- **Story DNA** — hero, goal, obstacle, stakes (hollywood) ↔ subject, object, opponent, receiver (narratology)
- **A/B/C ranking** — plotline weight (A = main, B = secondary, C = tertiary, runner = minor thread)
- **Format** — procedural (House), serial (Breaking Bad), hybrid (X-Files), ensemble (Game of Thrones)
- **Story engine** — one sentence capturing the show's core dramatic mechanism
- **Function** — episode-level role in a plotline arc (setup, inciting_incident, escalation, turning_point, crisis, climax, resolution; narratology adds `recognition`)
- **Arc function** — season-level role of the same event

## How this uses AI

`tvplot` calls either Claude (Anthropic) or GPT (OpenAI) — you pick one per run — to produce its analyses. If your application surfaces this library's output to end users, you're responsible for disclosing the AI involvement to them (EU AI Act Art. 50; Anthropic AUP; OpenAI Usage Policies). The standalone HTML viewer shipped here carries that notice in its footer.

- [`docs/ai-disclosure.md`](docs/ai-disclosure.md) — what gets sent where, and to whom. Alpine Animation (Switzerland) publishes the library and collects **no** data; your API key and inputs travel directly from your machine to the LLM vendor you pick.
- [`docs/methodology.md`](docs/methodology.md) — plain-language description of how the pipeline decides, what it can't do, and where to find the code for each step.

Outputs are AI-generated and can contain hallucinations. Treat them as a starting point for your own analysis, not a verified source.

## Citation

```bibtex
@software{tvplotlines2026,
@software{tvplot2026,
author = {Vashko, N.},
title = {tvplotlines: LLM-Driven Plotline Extraction from Episode Synopses},
title = {tvplot: LLM-Driven Plotline Extraction from Episode Synopses},
year = {2026},
url = {https://github.com/BirdInTheTree/tvplotlines}
url = {https://github.com/BirdInTheTree/tvplot}
}
```

Expand Down
Loading
Loading