Metadata-Version: 2.4
Name: yt-mem-ai
Version: 0.1.0
Summary: Local-first YouTube AI CLI: download, transcribe (captions -> whisper), embed in LanceDB, discover subscriptions, and summarize/highlight/Q&A.
Project-URL: Homepage, https://github.com/dasein108/yt-mem-ai
Project-URL: Repository, https://github.com/dasein108/yt-mem-ai
Project-URL: Issues, https://github.com/dasein108/yt-mem-ai/issues
Author: dasein
License-Expression: MIT
License-File: LICENSE
Keywords: ai,cli,lancedb,summarization,transcription,whisper,youtube
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Multimedia :: Video
Classifier: Topic :: Text Processing :: Linguistic
Requires-Python: >=3.11
Requires-Dist: faster-whisper>=1.0
Requires-Dist: lancedb>=0.15
Requires-Dist: numpy>=1.26
Requires-Dist: openai>=1.40
Requires-Dist: python-dotenv>=1.0
Requires-Dist: sentence-transformers>=3.0
Requires-Dist: typer>=0.12
Requires-Dist: youtube-transcript-api>=1.0
Requires-Dist: yt-dlp>=2024.8
Provides-Extra: dev
Requires-Dist: httpx>=0.27; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: ruff>=0.6; extra == 'dev'
Description-Content-Type: text/markdown

# yt-mem-ai — YouTube AI CLI

## Install

```bash
# zero-install run (recommended)
uvx yt-mem-ai --help

# or bootstrap uv + warm the cache
curl -LsSf https://raw.githubusercontent.com/dasein108/yt-mem-ai/main/install.sh | sh
```

The desktop UI lives in a separate repo: **[yt-mem-ai-desktop](https://github.com/dasein108/yt-mem-ai-desktop)**.
It depends on this `yt-mem-ai` package and runs its own local REST API.

Download YouTube audio, transcribe (captions → faster-whisper fallback), store
everything in an embedded **LanceDB** with per-chunk embeddings, discover
subscription uploads, and search your library semantically. Summaries,
highlights, and Q&A are produced by Claude Code skills, not an API.

## Setup

```bash
uv sync --extra dev
cp .env.example .env   # fill WEBSHARE_* + YT_COOKIES_BROWSER; pick embedding backend
```

Config (`.env`): `YT_STORE_PATH` (LanceDB dir), `YT_EMBEDDING_BACKEND=local|openai`,
`YT_EMBEDDING_MODEL`, `YT_CHUNK_TARGET_S`, `OPENAI_API_KEY` (openai backend),
`WEBSHARE_PROXY_*`, `YT_COOKIES_BROWSER`.

**Proxy / VLESS:** `YT_USE_WEBSHARE` defaults **off**. If you already run a
system-level proxy/VPN (VLESS/Xray etc.), leave it off — traffic rides that
tunnel. Stacking the Webshare proxy on top breaks the authenticated
subscription feed (its CONNECT tunnel returns `405`). Only set
`YT_USE_WEBSHARE=true` if you have no other proxy and YouTube rate-limits your
raw IP. Discover tuning: `YT_DISCOVER_FEED_LIMIT` (newest-N cap, default 60),
`YT_DISCOVER_OVERLAP_S` (incremental overlap, default 3600), `YT_DISCOVER_TIMEOUT_S`.

## Commands

```bash
yt-ai fetch <url>            # download + transcribe + embed + store one video
yt-ai fetch <url> --captions-only  # captions only: no audio download / no whisper (fails if none)
yt-ai transcript <url>       # same pipeline
yt-ai discover               # new subscription uploads (--after/--deep/--min-duration/--json); incremental by default
yt-ai fetch-pending          # batch-fetch pending 'discovered' videos (since --since, default today; --limit)
yt-ai list                   # list stored videos (--status/--since/--json)
yt-ai show <video_id>        # metadata + transcript (--json)
yt-ai status                 # counts by status
yt-ai search "<query>"       # semantic search (--hybrid/--fts/--vector, -k N)
yt-ai save-summary <id> "<summary>" --highlights '<json>' --qa '<json>'  # persist a summary (used by skills)
yt-ai like <video_id>        # mark liked (feeds recommendations)
yt-ai dislike <video_id>     # mark disliked
yt-ai recommend              # rank your unrated fetched videos by taste (--limit/--json)
yt-ai compile                 # deep-linked highlights doc, budget-bounded (--since/--max-minutes/--json/--out)
yt-ai supercut                 # video reel of highlights, re-downloaded + labeled (--since/--max-minutes/--out/--keep-clips)
```

## Rate & recommend

Like/dislike videos you've fetched (`yt-ai like <id>` / `dislike <id>`), then
`yt-ai recommend` ranks your other fetched-but-unrated videos by similarity to
what you liked (minus what you disliked), using their transcript embeddings.
Before you've liked anything, it falls back to most-recently-published.

## Daily routine

```bash
yt-ai discover               # find new subscription uploads → 'discovered'
yt-ai fetch-pending          # download+transcribe+embed today's batch (robust, skips failures)
# then in Claude Code:
/daily-digest                # per-video summaries + digests/YYYY-MM-DD.md
yt-ai compile                # compile the day's highlights into a deep-linked markdown you can click into
```

`yt-ai discover` is **incremental**: it pulls the newest feed entries (one flat
call, capped by `YT_DISCOVER_FEED_LIMIT`), stamps each with an approximate
`timestamp` (yt-dlp `youtubetab:approximate_date`), and keeps only those newer
than the last run's stored high-water mark minus a 1h overlap
(`YT_DISCOVER_OVERLAP_S`) — so hour-rounded dates never miss a boundary video,
and already-processed videos (`is_seen`) are filtered out. Pass `--after
YYYY-MM-DD` to override the cutoff manually. Full per-video metadata
(description, tags, exact time) is fetched later at ingest, not during discover.

`yt-ai compile` builds `compilations/<DATE>.md`: each highlight from the day's
summarized videos becomes a deep link (`watch?v=ID&t=<start>s`) that jumps
straight to its moment, newest-video-first and budget-bounded by
`--max-minutes` (default 20). Fast — no downloading, just the same
`compile_highlights` selection rendered as markdown.

`yt-ai supercut` renders that same highlight selection as an actual video
reel instead of a doc: it **re-downloads** each highlight's section (720p,
`yt-dlp --download-sections`), burns a label onto each clip (title/timestamp),
and concats them into one mp4 — so it needs network access and a local
`ffmpeg`, and is much slower than `compile`. Output is
`supercuts/<since-or-today>.mp4` plus a sidecar `supercuts/<...>.mp4.refs.md`
listing each rendered clip's source link (and any clips skipped because their
download/render failed). Use `compile` for a quick clickable digest; use
`supercut` when you want a shareable video.

Single video on demand: `yt-ai fetch <url>` then the `/summarize-video` skill.

## Logging

The CLI writes structured JSON events to **`logs/common.jsonl`** (via
`obs.log_event`/`blog`) — one object per line, `{ts, source, level, event, msg,
...ctx}`. Override the path with `YT_LOG_FILE`; it's gitignored. Inspect with jq:

```bash
jq -c 'select(.level=="error")' logs/common.jsonl   # every error
tail -f logs/common.jsonl | jq -c '{ts,event,msg}'  # live tail, compact
```

## Tests

```bash
uv run pytest -q                       # offline unit tests (fake embedder)
YT_RUN_INTEGRATION=1 uv run pytest -q  # + real sentence-transformers integration
```
