Metadata-Version: 2.4
Name: reko-yt
Version: 0.5.0
Summary: A modern, local-first CLI tool to extract transcripts from YouTube and transform them into concise summaries and key points.
Keywords: youtube,summarization,llm,cli,ollama,transcript
Author: Riccardo Ruspoli
Author-email: Riccardo Ruspoli <riccardoruspoli@gmail.com>
License-Expression: MIT
License-File: LICENSE
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Multimedia :: Video
Classifier: Topic :: Text Processing :: Linguistic
Requires-Dist: bleach>=6.3,<6.5
Requires-Dist: dspy>=3.2.1,<3.4.0
Requires-Dist: fastapi>=0.136.3,<0.142.0
Requires-Dist: iso639-lang~=2.6.3
Requires-Dist: jinja2~=3.1.6
Requires-Dist: markdown~=3.10.2
Requires-Dist: ollama~=0.6.2
Requires-Dist: pytubefix>=10.9,<10.12
Requires-Dist: tiktoken>=0.13,<0.14
Requires-Dist: tqdm>=4.68.1,<4.71.0
Requires-Dist: uvicorn>=0.49,<0.53
Requires-Dist: youtube-transcript-api~=1.2.4
Requires-Python: >=3.10, <3.15
Project-URL: Homepage, https://github.com/riccardoruspoli/reko
Project-URL: Repository, https://github.com/riccardoruspoli/reko
Project-URL: Issues, https://github.com/riccardoruspoli/reko/issues
Description-Content-Type: text/markdown

<div align="center">

# reko

**Make YouTube videos readable** 🎥

_A modern, local-first CLI tool to extract transcripts from YouTube and transform them into concise summaries, key points, and briefs._

[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://opensource.org/licenses/MIT)
[![PyPI version](https://img.shields.io/pypi/v/reko-yt.svg)](https://pypi.org/project/reko-yt/)
[![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)

</div>

## ✨ What is reko?

`reko` is a command-line tool that converts YouTube videos into clean Markdown summaries using transcripts and Large Language Models.

Give it a video URL, a playlist, or a file containing multiple URLs, and `reko` will fetch the transcript and generate a readable Markdown document with a summary, key points, or a concise Brief.

It’s designed to be simple, fast, and automation-friendly, making it easy to extract useful information from long, informational videos.  
By default, `reko` is local-first and privacy-friendly when used with Ollama, while still supporting paid cloud models when needed.

<p align="center">
  <img src="docs/screenshot.png" alt="Example of a Markdown summary generated by reko" width="700">
</p>

## 🚀 Features

- Generate clean Markdown summaries, key points, or a decision-oriented Brief.
- Summarize a single video, a playlist, or a batch of URLs.
- Optimized for Small Language Models (SLMs), which are often sufficient for high-quality summarization.
- Works with Ollama and cloud providers via APIs.
- Multi-language summaries: uses native transcripts when available, with automatic fallback and translation.
- Uses transcript chunking to handle long videos, with provider-specific optimizations where they are measured to help.
- Skips reprocessing when a summary already exists (with an option to force regeneration).

## 🧠 How it works

At a high level, `reko` follows a simple pipeline:

1. Resolve the input target (video, playlist, or file).
2. Fetch the YouTube transcript in the requested language, with fallback and translation when needed.
3. Split the transcript into word-based chunks, summarize each chunk, and merge the results.
4. Output a Markdown file and/or print to stdout, with optional key points.

The portable chunked workflow is the default foundation. When a provider exposes
capabilities that make a different route demonstrably worthwhile, reko can use it
automatically while retaining the same output contract and safe fallback.

### OpenAI routing optimization

For eligible official OpenAI models, reko has a benchmarked full-context route.
It estimates input tokens locally, uses one request when the transcript safely
fits a conservative capacity and cost threshold, generates directly in the target
language, and validates the requested Markdown sections. Other providers and
larger inputs continue to use the portable chunked workflow. This is an
implementation detail rather than a provider preference, and leaves room for
future provider-specific routes supported by comparable evidence.

### Transcript cache

Raw YouTube transcripts are cached locally by video ID and requested language. Re-running a video with different output options or LLM settings reuses the cached transcript and avoids unnecessary YouTube requests. Set `REKO_DATA_DIR` to choose the cache location; use `--refresh-transcript` only when you explicitly need to fetch a fresh transcript.

## 📦 Installation

### Prerequisites

- **Python 3.10–3.14** (all versions are verified in CI)
- An LLM endpoint:
  - **Ollama** (local): install Ollama and pull the model you want to use.
  - **Hosted APIs** (for example `openai/...`): configure the required API keys in your environment. When using non-local providers, the required environment variables must be configured according to the supported provider of the model which can be found [here](https://docs.litellm.ai/docs/providers).

### Install from PyPI

```bash
pip install reko-yt
```

## ⚡ Quick start

Show available commands:

```bash
reko --help
reko summarize --help
```

> [!NOTE]
> Small Language Models are a great default for most summarization tasks.

Summarize a single YouTube video (prints and saves by default):

```bash
reko summarize 'https://www.youtube.com/watch?v=eMlx5fFNoYc' 'ollama/llama3.2:3b'
reko summarize 'https://www.youtube.com/watch?v=eMlx5fFNoYc' 'openai/gpt-5-nano'
```

For OpenAI GPT-5 models, `reko` uses the Responses API and maps `--max-tokens` to
OpenAI's completion-token limit. GPT-5 reasoning models only support the default
sampling temperature, so custom `--temperature` values are ignored for those
models. Its benchmarked OpenAI route uses LiteLLM's local model metadata and
`tiktoken` to choose a full-context request without contacting an extra API. It
reserves completion and safety tokens, and uses a conservative fallback limit
when metadata is unavailable. You can tune reasoning cost/latency with:

```bash
reko summarize 'https://www.youtube.com/watch?v=eMlx5fFNoYc' \
  'openai/gpt-5-nano' \
  --reasoning-effort low
```

Generate key points only, in Italian, without saving to disk:

```bash
reko summarize 'https://www.youtube.com/watch?v=eMlx5fFNoYc' \
  'ollama/llama3.2:3b' \
  --length short \
  --language it \
  --key-points-only \
  --print-only
```

Generate a Brief when you want the central thesis, the important facts, why they
matter, and a practical takeaway instead of a narrative summary:

```bash
reko summarize 'https://www.youtube.com/watch?v=eMlx5fFNoYc' \
  'openai/gpt-5-nano' \
  --brief \
  --language it
```

Summarize a playlist or a batch of URLs:

```bash
reko summarize 'https://www.youtube.com/playlist?list=PL908547EAA7E4AE74' 'ollama/llama3.2:3b'
reko summarize urls.txt 'ollama/llama3.2:3b'
```

> [!WARNING]
> When processing playlists or large batches, YouTube may temporarily rate-limit your IP.  
> Using a proxy is recommended for high-volume usage.

Use a remote Ollama instance:

```bash
reko summarize 'https://www.youtube.com/watch?v=eMlx5fFNoYc' 'ollama/llama3.2:3b' --host 'http://YOUR_OLLAMA_HOST:11434'
```

## 🌐 Local web UI

Start a local web page that can summarize a YouTube URL and render the Markdown:

```bash
reko serve
```

Then open `http://127.0.0.1:8000` in your browser.

Options:

```bash
reko serve --host 127.0.0.1 --port 8000
reko serve --host 0.0.0.0 --port 8000  # expose on your LAN
```

Notes:

- The web UI supports single video URLs (no playlists/batch files) and lets you
  choose Summary, Key Points, or Brief. Brief is exclusive because it derives
  its own internal summary and renders a different four-section document.
- Jobs run asynchronously. The UI streams progress with Server-Sent Events (SSE)
  and falls back to a one-second status poll when SSE is unavailable.
- A job reports transcript-cache hit/miss, the selected workflow, any applicable
  token estimate and route ceiling, retries, input/output words, elapsed time,
  phase timings, and terminal success/failure/cancellation.
- Cancel is cooperative: an active LLM request is allowed to finish, but no next
  phase is started. Failed and cancelled jobs can be retried from the UI.
- `REKO_MAX_CONCURRENT_JOBS` controls the in-memory job limit and defaults to
  `1`, which is appropriate for a home server or a local LLM endpoint.
- If a reverse proxy is in front of reko, disable response buffering for
  `/api/jobs/*/events` and preserve `text/event-stream`; otherwise the browser
  will automatically use polling instead of live progress.

## 🐳 Container deployment

The container runs only reko. Configure Ollama or another LLM provider separately in the web UI; Ollama is intentionally not part of the Compose stack.

```bash
cp .env.example .env
docker compose up -d
```

Open `http://YOUR_SERVER:8000`. Transcript cache files are persisted in `./data`. For an Ollama service running on the Docker host, use `http://host.docker.internal:11434` in the UI; for a different host, use an address reachable from the container.

Container logs remain available through Docker, for example with `docker compose
logs -f reko`. The Compose service uses Docker's `local` logging driver with
compression and rotation: up to three 10 MiB files are retained per container.
Docker manages these files in its own data directory; do not edit them directly.

The image runs as UID/GID `10001` by default. If `./data` is managed by your host user, set its actual numeric IDs in `.env` and align the directory ownership before starting the service:

```bash
REKO_UID=1000
REKO_GID=1000
sudo chown -R 1000:1000 ./data
```

Use `id -u` and `id -g` to obtain the correct values instead of assuming `1000`. The container only receives the provider credentials explicitly declared in `compose.yaml`. For example, to use an OpenAI key from an ignored local `.env`, add this entry under the Compose service `environment` mapping:

```yaml
OPENAI_API_KEY: ${OPENAI_API_KEY:-}
```

Then add `OPENAI_API_KEY=...` to `.env` and protect it with `chmod 600 .env`. Do not commit provider credentials.

Check service health with:

```bash
curl http://localhost:8000/health
```

## 🧪 Development

All automated checks are offline and use mock data only. Install the Python
development dependencies with `uv`. A normal checkout installs the pinned
frontend tool version with `npm ci` from the committed `package-lock.json`.

When introducing or updating frontend dependencies, use `npm install` once to
update both `package.json` and `package-lock.json`. Commit both files, then use
`npm ci` again for regular development and CI.

Run the checks with:

```bash
uv sync --group dev
npm ci
uv run ruff format --check src test
uv run ruff check src test
npm run format:frontend:check
uv run python -m pytest --cov=src/reko --cov-branch --cov-report=term-missing --cov-report=xml --cov-fail-under=90
uv build
```

Use the formatter on every HTML, CSS, and JavaScript source file when needed:

```bash
npm run format:frontend
```

## 🚀 Releases

The release workflow runs when a `vX.Y.Z` tag is pushed. It runs the offline quality checks, builds the Python distributions, publishes to PyPI through Trusted Publishing, pushes a tagged image to GHCR, and creates GitHub release notes with git-cliff.

Before the first release, configure PyPI Trusted Publishing for this repository and the `release.yml` workflow, create the protected GitHub environment named `pypi`, and set the GHCR package visibility you want. Set the release version in `pyproject.toml`; the workflow verifies that the tag version matches it.

If a tagged release fails after PyPI accepts the artifacts, fix the later workflow configuration and rerun the workflow. Existing identical PyPI artifacts are skipped, so the rerun can continue to GHCR and the GitHub Release.

To refresh the committed changelog before creating a release, run git-cliff locally:

```bash
git cliff --tag vX.Y.Z --output CHANGELOG.md
```

## 📄 License

MIT. See `LICENSE`.
