|
| 1 | +# CLI guide (no Docker required) |
| 2 | + |
| 3 | +Programmarr started as a handful of Python scripts, and they're all still here. If you'd |
| 4 | +rather not run another server on your network, you can do **everything the web app does** from |
| 5 | +the terminal. The scripts have **zero dependencies beyond the Python 3 standard library** and |
| 6 | +run standalone. |
| 7 | + |
| 8 | +> **Where config lives:** in the CLI, `config.json` sits in the **project root** (next to |
| 9 | +> `programmarr.py`). In Docker it lives in `data/`. Same file shape either way — see |
| 10 | +> [`config.json.example`](../config.json.example) and the **Advanced Configuration** table in |
| 11 | +> the [README](../README.md#advanced-configuration) for every key (including `tunarr_stream_mode` |
| 12 | +> and `tunarr_channel_group`). |
| 13 | +
|
| 14 | +## Requirements |
| 15 | + |
| 16 | +- Python 3 (no `pip install` needed — stdlib only) |
| 17 | +- A running [Tunarr](https://github.com/chrisbenincasa/tunarr) instance and a Plex server |
| 18 | +- Optionally a [TMDB API key](https://www.themoviedb.org/settings/api) for channel logos |
| 19 | + |
| 20 | +```bash |
| 21 | +git clone https://github.com/AlpineArchitecture/programmarr.git |
| 22 | +cd programmarr |
| 23 | +python programmarr.py |
| 24 | +``` |
| 25 | + |
| 26 | +--- |
| 27 | + |
| 28 | +## Path A — the interactive menu |
| 29 | + |
| 30 | +`python programmarr.py` is the guided front door. On first run (no `config.json` yet) it walks |
| 31 | +you through a short setup wizard, then drops you at the main menu: |
| 32 | + |
| 33 | +``` |
| 34 | + 1) AI path — export → LLM → deploy |
| 35 | + 2) No-AI path — auto-generate → deploy |
| 36 | + 3) Collections — sync Plex collections → deploy |
| 37 | +
|
| 38 | + i) Fetch channel images from TMDB |
| 39 | + s) Sync channels to Plex DVR |
| 40 | +
|
| 41 | + q) Quit |
| 42 | +``` |
| 43 | + |
| 44 | +- **AI path** — exports your library, builds a prompt you paste into an LLM (Claude/Gemini/ChatGPT), |
| 45 | + then deploys the `channels.json` the LLM returns. Always probes before deploying and asks |
| 46 | + whether to wipe-and-rebuild or preserve channels below a number. |
| 47 | +- **No-AI path** — generates a starter `channels.json` straight from your library metadata |
| 48 | + (decade + genre movie channels, 50+ episode TV marathons) and deploys it. No LLM involved. |
| 49 | +- **Collections** — turns your Plex collections (managed by Kometa/Trakt/Letterboxd) into |
| 50 | + channels, one per collection. |
| 51 | +- **i / s** — run the image fetch or Plex-DVR sync on their own. |
| 52 | + |
| 53 | +> **The wizard only writes the required keys** (`tunarr_url`, `plex_url`, `plex_token`, and TMDB |
| 54 | +> if given). Advanced keys are **not** prompted — see [Setting advanced config](#setting-advanced-config) |
| 55 | +> below to add them by hand. |
| 56 | +
|
| 57 | +--- |
| 58 | + |
| 59 | +## Path B — raw scripts (for cron and scripting) |
| 60 | + |
| 61 | +The menu just orchestrates these. Call them directly for automation. **Every script takes |
| 62 | +`--help`** — that's the authoritative flag reference; this guide only shows the chain and the |
| 63 | +gotchas worth knowing. |
| 64 | + |
| 65 | +``` |
| 66 | +export.py → generate_no_ai.py → create.py → fetch_images.py → sync_plex.py |
| 67 | + (or hand off to an LLM) |
| 68 | +``` |
| 69 | + |
| 70 | +| Script | What it does | |
| 71 | +|--------|--------------| |
| 72 | +| `export.py` | Pulls full metadata from Plex → `plex_library.csv` + `export_summary.json`. Auto-detects movie + TV sections. | |
| 73 | +| `generate_no_ai.py` | Builds a starter `channels.json` from the CSV (no AI). `--order KEY,KEY,…` overrides category order; `--start N` sets the first channel number. | |
| 74 | +| `generate_from_collections.py` | One channel per Plex collection. `--apply` to write; manages the collection block from `--base` up. | |
| 75 | +| `create.py` | Reads `channels.json` and deploys to Tunarr (delete-then-create). `--from N` scopes; `--protect N1,N2` preserves specific channels. | |
| 76 | +| `fetch_images.py` | Sets every channel's Tunarr icon (verified TMDB logos + generated badges). **Dry-run by default; `--apply` to commit.** | |
| 77 | +| `sync_plex.py` | Reconciles Tunarr's channel list into Plex's DVR mapping. Never deletes the DVR. | |
| 78 | + |
| 79 | +### Example: nightly no-AI rebuild via cron |
| 80 | + |
| 81 | +```bash |
| 82 | +#!/usr/bin/env bash |
| 83 | +cd /opt/programmarr |
| 84 | +python export.py |
| 85 | +python generate_no_ai.py |
| 86 | +python create.py |
| 87 | +python fetch_images.py --apply |
| 88 | +python sync_plex.py |
| 89 | +``` |
| 90 | + |
| 91 | +```cron |
| 92 | +# 4am daily |
| 93 | +0 4 * * * /opt/programmarr/rebuild.sh >> /var/log/programmarr.log 2>&1 |
| 94 | +``` |
| 95 | + |
| 96 | +> Live (auto-updating) channels are a feature of the **web app's** in-process scheduler. From the |
| 97 | +> CLI, a cron rebuild like the above is the equivalent — re-run the chain on whatever schedule you like. |
| 98 | +
|
| 99 | +--- |
| 100 | + |
| 101 | +## Setting advanced config |
| 102 | + |
| 103 | +Keys like the channel stream mode and the Tunarr group aren't in the setup wizard. Add them to |
| 104 | +`config.json` by hand — they sit right alongside the connection settings: |
| 105 | + |
| 106 | +```json |
| 107 | +{ |
| 108 | + "tunarr_url": "http://192.168.1.10:8000", |
| 109 | + "plex_url": "http://192.168.1.10:32400", |
| 110 | + "plex_token": "your-plex-token", |
| 111 | + |
| 112 | + "tunarr_stream_mode": "hls_direct_v2", |
| 113 | + "tunarr_channel_group": "Saturday Morning" |
| 114 | +} |
| 115 | +``` |
| 116 | + |
| 117 | +- **`tunarr_stream_mode`** — one of `hls`, `hls_slower`, `mpegts`, `hls_direct`, `hls_direct_v2` |
| 118 | + (default `hls`). Applied by `create.py` at channel creation. |
| 119 | +- **`tunarr_channel_group`** — the Tunarr group/folder all created channels land in (default |
| 120 | + `tunarr`). |
| 121 | +- **`channel_order`** — array of category keys controlling numbering order, e.g. |
| 122 | + `["marathon","tv_block","movie","franchise","specialty"]`. Omit for the default order. See |
| 123 | + [Channel Numbering](../README.md#channel-numbering). |
| 124 | + |
| 125 | +The full key list is in [`config.json.example`](../config.json.example) and the README's |
| 126 | +[Advanced Configuration](../README.md#advanced-configuration) table. |
0 commit comments