Metadata-Version: 2.5
Name: tubestoevsky
Version: 1.0.2
Summary: Turn YouTube transcripts into literary prose with an LLM
Project-URL: Homepage, https://github.com/iiw/tubestoevsky
Project-URL: Repository, https://github.com/iiw/tubestoevsky
Project-URL: Issues, https://github.com/iiw/tubestoevsky/issues
Author-email: Viktor Buzanov <viktor.buzanov@mailbox.org>
License-Expression: MIT
License-File: LICENSE
Keywords: langchain,llm,prose,subtitles,transcript,youtube,yt-dlp
Classifier: Development Status :: 5 - Production/Stable
Classifier: Environment :: Console
Classifier: Intended Audience :: End Users/Desktop
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Multimedia :: Video
Classifier: Topic :: Text Processing
Requires-Python: >=3.12
Requires-Dist: curl-cffi>=0.16.3
Requires-Dist: langchain-core>=0.3
Requires-Dist: langchain-openai>=1.6.6
Requires-Dist: python-dotenv>=1.0
Requires-Dist: pyyaml>=6.0
Requires-Dist: yt-dlp[default]>=2026.7.4
Description-Content-Type: text/markdown

# TubeStoevsky

Downloads a YouTube video transcript and turns it into literary prose through a LangChain LLM chain — outputs `TXT` + `MD`.

Works with any OpenAI-compatible chat completions API: a local gateway, Ollama, vLLM, OpenAI, or anything else speaking that protocol.

Available on PyPI as [tubestoevsky](https://pypi.org/project/tubestoevsky/).

## How it works

```
URL
 ├─ yt-dlp       subtitles (manual > auto)  -> {title}.srt
 ├─ SRT parser   timecodes and rolling dupes -> raw text
 ├─ LangChain    prose -> title -> Markdown
 └─ output       {title}.txt, {title}.md
```

## Install

```bash
# as a CLI tool
uv tool install tubestoevsky   # or: pip install tubestoevsky
tubestoevsky --help

# from a checkout, for development
uv sync
```

## Configuration

Store global settings with:

```console
$ tubestoevsky config set LLM_API_KEY sk-xxxxx
$ tubestoevsky config set LLM_MODEL gpt-6-luna
$ tubestoevsky config get LLM_MODEL
gpt-6-luna
```

Settings are saved to `~/.tubestoevsky/config.yaml`.

Precedence: **environment > `config.yaml` > `.env` > default**, with direct CLI
flags (`--lang`, `--out`, and `--model`) overriding all of those.

`.env` remains supported for existing installations. Copy
[`.env.example`](./.env.example) to `.env`; it is read from the current
directory, the project root, or `~/.config/tubestoevsky/.env`.

| Variable | Purpose | Default |
|---|---|---|
| `LLM_BASE_URL` | OpenAI-compatible endpoint (`/v1` appended when absent) | `http://127.0.0.1:4000` |
| `LLM_API_KEY` | API key for that endpoint | **required** |
| `LLM_MODEL` | model name served by it | `gpt-6-luna` |
| `TRANSCRIPT_LANG` | subtitle language | `en` |
| `OUTPUT_DIR` | output directory | `.` |
| `LLM_TEMPERATURE` | sampling temperature | `0.3` |
| `LLM_TIMEOUT` | request timeout, sec | `1800` |
| `YTDLP_COOKIES` / `YTDLP_COOKIES_FROM_BROWSER` | access to restricted videos | — |

## Usage

Example run:

```console
$ tubestoevsky --lang en --model deepseek-flash https://youtu.be/q0aFOxT6TNw
[1/4] Downloading subtitles (en) via yt-dlp ...
      -> iPhone-17-Pro-Review-Paradox-in-a-Box.srt
[2/4] Parsing SRT, deduplicating lines ...
      -> 2349 words (raw text: 12816 chars)
[3/4] LangChain chain (deepseek-flash via http://127.0.0.1:4000/v1) ...
      [LLM 1/3] rewriting as prose (2349 words) ...
      [LLM 1/3] done: 2300 words
      [LLM 2/3] document title ...
      [LLM 2/3] done: "iPhone 17 Pro Review: The Most Pro iPhone for the Masses"
      [LLM 3/3] Markdown layout ...
      [LLM 3/3] done: 2318 words
      -> prose: 2300 words, title: "iPhone 17 Pro Review: The Most Pro iPhone for the Masses"
[4/4] Writing .txt and .md ...
Done:
  ./iPhone-17-Pro-Review-Paradox-in-a-Box.srt
  ./iPhone-17-Pro-Review-Paradox-in-a-Box.txt
  ./iPhone-17-Pro-Review-Paradox-in-a-Box.md
```

Full generated output is available in [`examples/`](./examples):
[transcript](./examples/iPhone-17-Pro-Review-Paradox-in-a-Box.srt),
[prose](./examples/iPhone-17-Pro-Review-Paradox-in-a-Box.txt),
and [Markdown](./examples/iPhone-17-Pro-Review-Paradox-in-a-Box.md).

Install globally and run with an inline API key:

```bash
uv tool install tubestoevsky
LLM_API_KEY='sk-xxxxx' tubestoevsky --lang en --model qwen3.8-27b https://youtu.be/q0aFOxT6TNw
```

Other examples:

```bash
tubestoevsky run <URL>
tubestoevsky <URL>
tubestoevsky <URL> --lang en --out ./out
tubestoevsky <URL> --model gpt-6-luna

# or without installing:
uvx tubestoevsky <URL>

# from a checkout:
uv run python -m transcript_app <URL>
```

## Layout

```
transcript_app/
  __main__.py    CLI
  config.py      YAML + .env + environment + CLI flags
  downloader.py  yt-dlp
  srt.py         subtitle parser
  chain.py       LangChain chain over the API
  prompts.py     step prompts
  outputs.py     .txt/.md writers
```

## Notes

- LLM steps stream: some gateways fail to return monolithic responses.
- `Accept-Encoding: identity` and `use_responses_api=False` work around proxy hangs.
- Run long requests in the foreground.
