docs: update README files to include new environment variables and local files feature

This commit is contained in:
2026-03-16 02:29:42 +01:00
parent e805028d46
commit abcf872d2d
3 changed files with 130 additions and 28 deletions

View File

@@ -11,7 +11,7 @@ The backend is a Cargo workspace with three crates following Hexagonal (Ports &
```
k-tv-backend/
├── domain/ # Pure business logic — no I/O, no frameworks
├── infra/ # Adapters: SQLite/Postgres repositories, Jellyfin HTTP client
├── infra/ # Adapters: SQLite/Postgres repositories, Jellyfin HTTP client, local files
└── api/ # Axum HTTP server — routes, DTOs, startup wiring
```
@@ -79,11 +79,20 @@ OIDC state (CSRF token, PKCE verifier, nonce) is stored in a short-lived encrypt
If Jellyfin variables are not set, the server starts normally but schedule generation endpoints return an error. Channel CRUD and auth still work.
### Local Files (optional — requires `local-files` feature)
| Variable | Default | Description |
|----------|---------|-------------|
| `LOCAL_FILES_DIR` | — | Absolute path to local video library root. Enables the local-files provider when set. |
| `TRANSCODE_DIR` | — | Directory for FFmpeg HLS transcode cache. Enables transcoding when set. |
| `TRANSCODE_CLEANUP_TTL_HOURS` | `24` | Hours after last access before a transcode cache entry is deleted. |
### CORS & Production
| Variable | Default | Description |
|----------|---------|-------------|
| `CORS_ALLOWED_ORIGINS` | `http://localhost:5173` | Comma-separated allowed origins |
| `BASE_URL` | `http://localhost:3000` | Public base URL used to build stream URLs for local files |
| `PRODUCTION` | `false` | Enforces minimum secret lengths when `true` |
## Feature Flags
@@ -100,6 +109,7 @@ default = ["sqlite", "auth-jwt", "jellyfin"]
| `auth-jwt` | JWT Bearer token authentication |
| `auth-oidc` | OpenID Connect integration |
| `jellyfin` | Jellyfin media provider adapter |
| `local-files` | Local filesystem media provider with optional FFmpeg transcoding |
## API Reference
@@ -137,11 +147,49 @@ All endpoints are under `/api/v1/`. Endpoints marked **Bearer** require an `Auth
| `GET` | `/channels/:id/epg?from=&until=` | Bearer | EPG slots overlapping a time window (RFC3339 datetimes) |
| `GET` | `/channels/:id/stream` | Bearer | `307` redirect to the current item's stream URL — `204` if no-signal |
### Other
### Library
All endpoints require Bearer auth and return `501 Not Implemented` if the active provider lacks the relevant capability.
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| `GET` | `/config` | — | Server configuration flags |
| `GET` | `/library/collections` | Bearer | List media collections/libraries |
| `GET` | `/library/series` | Bearer | List TV series (supports `?collection=`, `?provider=`) |
| `GET` | `/library/genres` | Bearer | List genres (supports `?type=`, `?provider=`) |
| `GET` | `/library/items` | Bearer | Search/filter media items (supports `?q=`, `?type=`, `?series[]=`, `?collection=`, `?limit=`, `?strategy=`, `?provider=`) |
### Files (local-files feature only)
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| `GET` | `/files/stream/:id` | — | Range-header video streaming for local files |
| `POST` | `/files/rescan` | Bearer | Trigger library rescan, returns `{ items_found }` |
| `GET` | `/files/transcode/:id/playlist.m3u8` | — | HLS transcode playlist |
| `GET` | `/files/transcode/:id/:segment` | — | HLS transcode segment |
| `GET` | `/files/transcode-settings` | Bearer | Get transcode settings (`cleanup_ttl_hours`) |
| `PUT` | `/files/transcode-settings` | Bearer | Update transcode settings |
| `GET` | `/files/transcode-stats` | Bearer | Cache stats `{ cache_size_bytes, item_count }` |
| `DELETE` | `/files/transcode-cache` | Bearer | Clear the transcode cache |
### IPTV
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| `GET` | `/iptv/playlist.m3u` | `?token=` | M3U playlist of all channels |
| `GET` | `/iptv/epg.xml` | `?token=` | XMLTV EPG for all channels |
### Admin
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| `GET` | `/admin/logs` | `?token=` | SSE stream of live server log lines (`{ level, target, message, timestamp }`) |
| `GET` | `/admin/activity` | Bearer | Recent 50 in-app activity events |
### Config
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| `GET` | `/config` | — | Server configuration flags and provider capabilities |
## Examples
@@ -267,6 +315,21 @@ curl -s -I http://localhost:3000/api/v1/channels/<id>/stream \
### Channel
A named broadcast channel owned by a user. Holds a `schedule_config` (the programming template) and a `recycle_policy`.
Channel fields:
| Field | Description |
|-------|-------------|
| `access_mode` | `public` / `password_protected` / `account_required` / `owner_only` |
| `access_password` | Hashed password when `access_mode` is `password_protected` |
| `logo` | URL or inline SVG for the watermark overlay |
| `logo_position` | `top_right` (default) / `top_left` / `bottom_left` / `bottom_right` |
| `logo_opacity` | 0.01.0, default 1.0 |
| `auto_schedule` | When `true`, the server auto-regenerates the schedule when it expires |
| `webhook_url` | HTTP endpoint called on domain events |
| `webhook_poll_interval_secs` | Polling interval for webhook delivery |
| `webhook_body_template` | Handlebars template for the webhook POST body |
| `webhook_headers` | JSON object of extra HTTP headers sent with webhooks |
### ScheduleConfig
The shareable programming template: an ordered list of `ProgrammingBlock`s. Channels do not need to cover all 24 hours — gaps are valid and produce a no-signal state.
@@ -286,6 +349,8 @@ Provider-agnostic filter used by algorithmic blocks:
| `tags` | Provider tag strings |
| `min_duration_secs` / `max_duration_secs` | Duration bounds for item selection |
| `collections` | Abstract groupings (Jellyfin library IDs, Plex sections, folder paths, etc.) |
| `series_names` | List of TV series names (OR-combined) |
| `search_term` | Free-text search term for library browsing |
### FillStrategy
How an algorithmic block fills its time budget:
@@ -305,6 +370,22 @@ Controls when previously aired items become eligible again:
| `cooldown_generations` | Don't replay within this many schedule generations |
| `min_available_ratio` | Always keep at least this fraction (0.01.0) of the matching pool selectable, even if their cooldown hasn't expired. Prevents small libraries from running dry. |
### ProviderCapabilities
`GET /config` returns `providers[]` with per-provider capabilities. Library endpoints return `501` if the active provider lacks the relevant capability.
| Capability | Description |
|------------|-------------|
| `collections` | Provider can list/filter by collections |
| `series` | Provider exposes TV series groupings |
| `genres` | Provider exposes genre metadata |
| `tags` | Provider supports tag filtering |
| `decade` | Provider supports decade filtering |
| `search` | Provider supports free-text search |
| `streaming_protocol` | `hls` or `direct_file` |
| `rescan` | Provider supports triggering a library rescan |
| `transcode` | FFmpeg transcoding is available (local-files only) |
### No-signal state
`GET /channels/:id/now` and `GET /channels/:id/stream` return `204 No Content` when the current time falls in a gap between blocks. The frontend should display static / noise in this case — matching the broadcast TV experience.
@@ -338,6 +419,9 @@ cargo build -F sqlite,auth-jwt,auth-oidc,jellyfin
# PostgreSQL variant
cargo build --no-default-features -F postgres,auth-jwt,jellyfin
# With local files + transcoding
cargo build -F sqlite,auth-jwt,jellyfin,local-files
```
### Docker
@@ -357,7 +441,8 @@ k-tv-backend/
│ │ # ScheduledSlot, MediaItem, PlaybackRecord, User, ...
│ ├── value_objects.rs # MediaFilter, FillStrategy, RecyclePolicy,
│ │ # MediaItemId, ContentType, Email, ...
│ ├── ports.rs # IMediaProvider trait
│ ├── ports.rs # IMediaProvider trait, ProviderCapabilities
│ ├── events.rs # Domain event types
│ ├── repositories.rs # ChannelRepository, ScheduleRepository, UserRepository
│ ├── services.rs # ChannelService, ScheduleEngineService, UserService
│ └── errors.rs # DomainError
@@ -366,7 +451,9 @@ k-tv-backend/
│ ├── channel_repository.rs # SQLite + Postgres ChannelRepository adapters
│ ├── schedule_repository.rs # SQLite + Postgres ScheduleRepository adapters
│ ├── user_repository.rs # SQLite + Postgres UserRepository adapters
│ ├── activity_log_repository/ # Activity log persistence
│ ├── jellyfin.rs # Jellyfin IMediaProvider adapter
│ ├── local_files/ # Local filesystem provider + FFmpeg transcoder
│ ├── auth/
│ │ ├── jwt.rs # JWT create + validate
│ │ └── oidc.rs # OIDC flow (stateless cookie state)
@@ -376,13 +463,22 @@ k-tv-backend/
├── api/src/
│ ├── routes/
│ │ ├── auth.rs # /auth/* endpoints
│ │ ├── channels.rs # /channels/* endpoints (CRUD, EPG, broadcast)
│ │ ── config.rs # /config endpoint
│ │ ├── channels/ # /channels/* endpoints (CRUD, EPG, broadcast)
│ │ ── admin.rs # /admin/logs (SSE), /admin/activity
│ │ ├── config.rs # /config endpoint
│ │ ├── files.rs # /files/* endpoints (local-files feature)
│ │ ├── iptv.rs # /iptv/playlist.m3u, /iptv/epg.xml
│ │ └── library.rs # /library/* endpoints
│ ├── config.rs # Config::from_env()
│ ├── state.rs # AppState
│ ├── extractors.rs # CurrentUser (JWT Bearer extractor)
│ ├── error.rs # ApiError → HTTP status mapping
│ ├── dto.rs # All request + response types
│ ├── events.rs # SSE event broadcasting
│ ├── log_layer.rs # Tracing layer → SSE log stream
│ ├── poller.rs # Webhook polling task
│ ├── scheduler.rs # Auto-schedule renewal task
│ ├── webhook.rs # Webhook delivery
│ └── main.rs # Startup wiring
├── migrations_sqlite/