diff --git a/k-tv-frontend/app/(main)/docs/page.tsx b/k-tv-frontend/app/(main)/docs/page.tsx index 0dccd45..30071b5 100644 --- a/k-tv-frontend/app/(main)/docs/page.tsx +++ b/k-tv-frontend/app/(main)/docs/page.tsx @@ -121,13 +121,17 @@ const TOC = [ { id: "requirements", label: "Requirements" }, { id: "backend-setup", label: "Backend setup" }, { id: "frontend-setup", label: "Frontend setup" }, + { id: "docker", label: "Docker deployment" }, { id: "jellyfin", label: "Connecting Jellyfin" }, + { id: "local-files", label: "Local files" }, { id: "first-channel", label: "Your first channel" }, { id: "blocks", label: "Programming blocks" }, { id: "filters", label: "Filters reference" }, { id: "strategies", label: "Fill strategies" }, { id: "recycle-policy", label: "Recycle policy" }, { id: "import-export", label: "Import & export" }, + { id: "iptv", label: "IPTV export" }, + { id: "channel-password", label: "Channel passwords" }, { id: "tv-page", label: "Watching TV" }, { id: "troubleshooting", label: "Troubleshooting" }, ]; @@ -359,6 +363,77 @@ npm run dev`} + {/* ---------------------------------------------------------------- */} +
+

Docker deployment

+

+ The recommended way to run K-TV in production is with Docker Compose. + The repository ships a compose.yml that runs the backend + and frontend as separate containers, and an optional{" "} + compose.traefik.yml overlay for HTTPS via Traefik. +

+ +

Minimal compose.yml

+
{`services:
+  backend:
+    image: registry.example.com/k-tv-backend:latest
+    environment:
+      HOST: 0.0.0.0
+      DATABASE_URL: sqlite:/app/data/k-tv.db?mode=rwc
+      CORS_ALLOWED_ORIGINS: https://tv.example.com
+      JWT_SECRET: 
+      COOKIE_SECRET: <64+ char random string>
+      SECURE_COOKIE: "true"
+      PRODUCTION: "true"
+      JELLYFIN_BASE_URL: http://jellyfin:8096
+      JELLYFIN_API_KEY: 
+      JELLYFIN_USER_ID: 
+    volumes:
+      - ./data:/app/data
+
+  frontend:
+    image: registry.example.com/k-tv-frontend:latest
+    environment:
+      API_URL: http://backend:3000/api/v1
+    ports:
+      - "3001:3000"`}
+ +

Build-time vs runtime env vars

+

+ NEXT_PUBLIC_API_URL is embedded into the frontend bundle + at build time. It must be passed as a{" "} + --build-arg when building the image: +

+
{`docker build \\
+  --build-arg NEXT_PUBLIC_API_URL=https://tv-api.example.com/api/v1 \\
+  -t registry.example.com/k-tv-frontend:latest .`}
+

+ If you use the provided compose.yml, set{" "} + NEXT_PUBLIC_API_URL under build.args so it + is picked up automatically on every build. +

+

+ API_URL (server-side only — used by Next.js API routes) + is set at runtime via the container environment and can reference the + backend by its internal Docker hostname:{" "} + http://backend:3000/api/v1. It is never baked into the + image. +

+ +

HTTPS with Traefik

+

+ Merge compose.traefik.yml over the base file to add + Traefik labels for automatic TLS certificates and routing: +

+
{`docker compose -f compose.yml -f compose.traefik.yml up -d`}
+ + Set SECURE_COOKIE=true and{" "} + PRODUCTION=true whenever the backend is behind HTTPS. + The default cookie secret is publicly known — always replace it + before going live. + +
+ {/* ---------------------------------------------------------------- */}

Connecting Jellyfin

@@ -419,6 +494,66 @@ npm run dev`}

+ {/* ---------------------------------------------------------------- */} +
+

Local files provider

+

+ In addition to Jellyfin, K-TV can serve content directly from a + local directory. This is useful when you want to schedule video files + without running a separate media server. +

+ +

Enabling local files

+

+ Build the backend with the local-files Cargo feature + and set the LOCAL_FILES_DIR environment variable to the + root of your video library: +

+
{`cargo run --features local-files
+
+# .env
+LOCAL_FILES_DIR=/media/videos`}
+

+ On startup the backend indexes all video files under{" "} + LOCAL_FILES_DIR. Duration is detected via{" "} + ffprobe (must be + installed and on PATH). Tags are derived from ancestor + directory names; the top-level subdirectory acts as the collection + ID. +

+ +

Rescanning

+

+ When you add or remove files, trigger a rescan from the Dashboard + (the Rescan library{" "} + button appears when the local files provider is active) or call the + API directly: +

+
{`POST /api/v1/files/rescan
+Authorization: Bearer 
+
+# Response
+{ "items_found": 142 }`}
+ +

Streaming

+

+ Local file streams are served by{" "} + GET /api/v1/files/stream/:id. This endpoint is{" "} + public (no auth required) + and supports Range headers for seeking. The frontend + player uses the native <video> element for local + files instead of hls.js. +

+ +

Filter support

+ + When the local files provider is active, the series picker and genre + filter are hidden in the block editor — those fields are only + supported by Jellyfin. Tags, decade, duration limits, and collection + filters work normally. + +
+ {/* ---------------------------------------------------------------- */}

Your first channel

@@ -743,6 +878,75 @@ Output only valid JSON matching this structure:
+ {/* ---------------------------------------------------------------- */} +
+

IPTV export

+

+ K-TV can export your channels as a standard IPTV playlist so you can + watch in any IPTV client — TiviMate, VLC, Infuse, Jellyfin, and + others. +

+ +

Getting the URLs

+

+ Open the Dashboard and click the antenna icon on any channel card to + open the IPTV Export dialog. It shows two URLs: +

+ /iptv/playlist.m3u?token=…, + "M3U", + "Channel list — paste this into your IPTV client as the playlist source.", + ], + [ + /iptv/epg.xml?token=…, + "XMLTV", + "Electronic program guide — paste this as the EPG / guide data source.", + ], + ]} + /> + +

Adding to an IPTV client

+

+ Copy the M3U URL and add it as a new playlist in your client. If the + client supports XMLTV, also add the EPG URL so programme titles and + descriptions appear in the guide. +

+ + + Both URLs contain your session JWT as a query parameter. Anyone with + the URL can access your channels — treat it like a password and do + not share it publicly. Rotating your session (logging out and back + in) invalidates the old URLs. + + + + {/* ---------------------------------------------------------------- */} +
+

Channel passwords

+

+ Individual channels can be protected with an optional password. When + set, TV viewers are prompted to enter the password before the stream + plays. Channels without a password are always public. +

+ +

Setting a password

+

+ Enter a password in the Password{" "} + field when creating a channel or editing it in the Dashboard. Leave + the field blank to remove an existing password. +

+ + + Channel passwords are not end-to-end encrypted. They prevent casual + access — someone who can intercept network traffic or extract the + JWT from an IPTV URL can still reach the stream. Do not use channel + passwords as the sole protection for sensitive content. + +
+ {/* ---------------------------------------------------------------- */}

Watching TV

diff --git a/k-tv-frontend/app/(main)/layout.tsx b/k-tv-frontend/app/(main)/layout.tsx index ed46e0d..28d376a 100644 --- a/k-tv-frontend/app/(main)/layout.tsx +++ b/k-tv-frontend/app/(main)/layout.tsx @@ -15,7 +15,7 @@ export default function MainLayout({ children }: { children: ReactNode }) {