+ 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.
+
+ 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.
+
+
+
{/* ---------------------------------------------------------------- */}
+ 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:
+
+ 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 }) {