Skip to content

Commit 834becd

Browse files
docs: add CLI guide, unify config docs, fix channel-numbering drift
A user asked how to set tunarr_stream_mode (hls_direct_v2) and tunarr_channel_group via config.json and the answer was hard to find — the CLI path had drifted into a 6-line stub and the config docs were partly stale. This unifies the docs so both web and CLI users have one canonical config reference. - docs/cli.md (new): full "go full geek" CLI guide — interactive programmarr.py menu, calling the raw scripts directly (export → generate/LLM → create → fetch_images → sync) with a cron example, and hand-editing config.json for advanced keys. Points to --help for exhaustive flags per the repo's own convention. - README.md: CLI section now links to docs/cli.md instead of a stub. Rewrote the stale Channel Numbering section (old channel_blocks block-sizes/gaps model → current channel_order sequential-from-1 tight-pack). README "Advanced Configuration" table stays the single canonical config reference for shared keys. - config.json.example: removed the dead channel_blocks sizes object, added a correct channel_order example (verified vs CANONICAL_ORDER). - PROMPT.md / PROMPT.personal.md: fixed the static CLI numbering scheme fed to the LLM (block ranges + "leave gaps" → sequential-from-1, all 9 categories) so the AI path emits valid numbering. (personal file is gitignored — fixed locally only.) No code changed; the config surface was already unified (both UIs read the same config.json). The gap was documentation. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 9f657fd commit 834becd

4 files changed

Lines changed: 160 additions & 29 deletions

File tree

PROMPT.md

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,10 @@ and uses the context window more efficiently.
2121
Paste the prompt, then paste the full contents of `plex_library.csv` directly
2222
after it. Works with any LLM or interface that doesn't support file uploads.
2323

24-
> **Note on the Channel Numbering Scheme below:** the ranges shown are the defaults. When you
25-
> copy the prompt from the web app, it regenerates that section to match your configured block
26-
> sizes (Settings → Channel Numbering) and start number. The static text here is what the CLI uses.
24+
> **Note on the Channel Numbering Scheme below:** the category order shown is the default. When
25+
> you copy the prompt from the web app, it regenerates that section to match your configured
26+
> category order (Settings → Channel Numbering) and start number. The static text here is what
27+
> the CLI uses.
2728
2829
---
2930

@@ -40,14 +41,20 @@ You are a TV channel programmer. I have a self-hosted media server with the libr
4041

4142
## Channel Numbering Scheme
4243

43-
Assign channel numbers following this cable TV block structure:
44-
- **10–19**: TV Marathons — 24/7 single-show loops (needs 50+ episodes to qualify)
45-
- **20–29**: TV Blocks — themed multi-show rotations (era blocks, genre blocks, etc.)
46-
- **30–49**: Movie Channels — genre and decade-based pools
47-
- **50–69**: Franchise & Curated Series — ordered collections (film series in release order, etc.)
48-
- **70–79**: Specialty — single-movie loops, holiday, niche themes
44+
Number channels **sequentially starting at 1**, tight-packed in this category order — no gaps,
45+
no reserved ranges. A category with no channels is simply skipped (it consumes no numbers).
4946

50-
Keep numbers sequential within each block. Leave gaps for future additions.
47+
1. **TV Marathons** — 24/7 single-show loops (needs 50+ episodes to qualify)
48+
2. **TV Blocks** — themed multi-show rotations (era blocks, genre blocks, etc.)
49+
3. **TV & Movie Mix** — mixed-genre channels spanning shows and films
50+
4. **Movie Channels** — genre and decade-based pools
51+
5. **Studios / Directors / Actors** — channels curated by creator or studio
52+
6. **Networks** — all shows from a single TV network
53+
7. **Classic TV Blocks** — historical lineups (TGIF, Must See TV, etc.)
54+
8. **Franchise & Series** — ordered collections (film series in release order, etc.)
55+
9. **Specialty** — single-movie loops, holiday, niche themes
56+
57+
Assign every channel the next free number — e.g. 12 marathons take 1–12, then TV Blocks start at 13.
5158

5259
## Shuffle Types
5360

@@ -61,9 +68,9 @@ Each channel must have one of these shuffle types:
6168
Output one channel per line as a JSON object (JSONL). No wrapper object, no markdown fences, no commentary between lines — just one `{...}` per line.
6269

6370
```
64-
{"number": 10, "name": "Breaking Bad Marathon", "shuffle": "ordered", "content": ["Breaking Bad"]}
65-
{"number": 20, "name": "Crime TV Block", "shuffle": "block", "content": ["The Wire", "Ozark", "Justified"]}
66-
{"number": 30, "name": "80s Action Movies", "shuffle": "shuffle", "content": ["Die Hard", "Lethal Weapon", "Predator"]}
71+
{"number": 1, "name": "Breaking Bad Marathon", "shuffle": "ordered", "content": ["Breaking Bad"]}
72+
{"number": 2, "name": "Crime TV Block", "shuffle": "block", "content": ["The Wire", "Ozark", "Justified"]}
73+
{"number": 3, "name": "80s Action Movies", "shuffle": "shuffle", "content": ["Die Hard", "Lethal Weapon", "Predator"]}
6774
```
6875

6976
Each line must have exactly these fields:

README.md

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -224,12 +224,13 @@ These optional keys can be added directly to `config.json` (they're not in the U
224224

225225
### Channel Numbering
226226

227-
Channels are grouped into five blocks — TV Marathons, TV Blocks, Movie Channels, Franchise &
228-
Series, Specialty — placed one after another. **Settings → Channel Numbering** lets you set how
229-
many channel numbers each block reserves (defaults `10/10/20/20/10`); enlarge a block to fit
230-
more channels if you have a big library. Numbering on a fresh deploy starts at channel **1**, and
231-
keeping existing channels shifts new ones above the highest you kept. (Stored as `channel_blocks`
232-
in `config.json`.)
227+
Channels are numbered **sequentially from 1**, tight-packed in category order — no fixed block
228+
sizes, no gaps. Categories run in this order: TV Marathons → TV Blocks → TV & Movie Mix → Movie
229+
Channels → Studios/Directors/Actors → Networks → Classic TV Blocks → Franchise & Series →
230+
Specialty. Empty categories consume no numbers. **Settings → Channel Numbering** lets you drag
231+
the categories into whatever order you prefer. Numbering on a fresh deploy starts at channel
232+
**1**; keeping existing channels shifts new ones above the highest you kept. (Stored as
233+
`channel_order`, a list of category keys, in `config.json` — omit it for the default order.)
233234

234235
### Commercials
235236

@@ -239,13 +240,16 @@ Any channel can play commercials in the gaps between shows. In Tunarr, create a
239240

240241
## CLI (advanced)
241242

242-
If you prefer the terminal, all the same functionality is available via `programmarr.py`:
243+
If you prefer the terminal, everything the web app does is available from the command line — no
244+
Docker, no extra server. The Python scripts have zero dependencies beyond the standard library.
243245

244246
```bash
245247
python programmarr.py
246248
```
247249

248-
The Python scripts have zero dependencies beyond the standard library and work standalone without Docker.
250+
**→ Full walkthrough: [`docs/cli.md`](docs/cli.md)** — the interactive menu, calling the raw
251+
scripts directly (cron-friendly), and editing `config.json` by hand for advanced keys like
252+
`tunarr_stream_mode` and `tunarr_channel_group`.
249253

250254
---
251255

config.json.example

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,6 @@
1010
"tunarr_channel_group": "programmarr",
1111
"tunarr_stream_mode": "hls",
1212

13-
"_channel_blocks": "Per-category block sizes — how many channel numbers each category reserves. Omit for defaults (10/10/20/20/10). Scale a category up for a large library. Editable in Settings.",
14-
"channel_blocks": {
15-
"marathon": 10,
16-
"tv_block": 10,
17-
"movie": 20,
18-
"franchise": 20,
19-
"specialty": 10
20-
}
13+
"_channel_order": "Category order for sequential channel numbering. Channels are tight-packed from 1 in this order — no block sizes, no gaps. Omit for the default order. Editable in Settings → Channel Numbering.",
14+
"channel_order": ["marathon", "tv_block", "tv_movie_mix", "movie", "entity", "network", "programming_block", "franchise", "specialty"]
2115
}

docs/cli.md

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
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

Comments
 (0)