feat(docs): enhance documentation with Docker deployment and local files provider sections
This commit is contained in:
@@ -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`}</Pre>
|
||||
</Note>
|
||||
</Section>
|
||||
|
||||
{/* ---------------------------------------------------------------- */}
|
||||
<Section id="docker">
|
||||
<H2>Docker deployment</H2>
|
||||
<P>
|
||||
The recommended way to run K-TV in production is with Docker Compose.
|
||||
The repository ships a <Code>compose.yml</Code> that runs the backend
|
||||
and frontend as separate containers, and an optional{" "}
|
||||
<Code>compose.traefik.yml</Code> overlay for HTTPS via Traefik.
|
||||
</P>
|
||||
|
||||
<H3>Minimal compose.yml</H3>
|
||||
<Pre>{`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: <openssl rand -hex 32>
|
||||
COOKIE_SECRET: <64+ char random string>
|
||||
SECURE_COOKIE: "true"
|
||||
PRODUCTION: "true"
|
||||
JELLYFIN_BASE_URL: http://jellyfin:8096
|
||||
JELLYFIN_API_KEY: <key>
|
||||
JELLYFIN_USER_ID: <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"`}</Pre>
|
||||
|
||||
<H3>Build-time vs runtime env vars</H3>
|
||||
<P>
|
||||
<Code>NEXT_PUBLIC_API_URL</Code> is embedded into the frontend bundle
|
||||
at build time. It must be passed as a{" "}
|
||||
<Code>--build-arg</Code> when building the image:
|
||||
</P>
|
||||
<Pre>{`docker build \\
|
||||
--build-arg NEXT_PUBLIC_API_URL=https://tv-api.example.com/api/v1 \\
|
||||
-t registry.example.com/k-tv-frontend:latest .`}</Pre>
|
||||
<P>
|
||||
If you use the provided <Code>compose.yml</Code>, set{" "}
|
||||
<Code>NEXT_PUBLIC_API_URL</Code> under <Code>build.args</Code> so it
|
||||
is picked up automatically on every build.
|
||||
</P>
|
||||
<P>
|
||||
<Code>API_URL</Code> (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:{" "}
|
||||
<Code>http://backend:3000/api/v1</Code>. It is never baked into the
|
||||
image.
|
||||
</P>
|
||||
|
||||
<H3>HTTPS with Traefik</H3>
|
||||
<P>
|
||||
Merge <Code>compose.traefik.yml</Code> over the base file to add
|
||||
Traefik labels for automatic TLS certificates and routing:
|
||||
</P>
|
||||
<Pre>{`docker compose -f compose.yml -f compose.traefik.yml up -d`}</Pre>
|
||||
<Note>
|
||||
Set <Code>SECURE_COOKIE=true</Code> and{" "}
|
||||
<Code>PRODUCTION=true</Code> whenever the backend is behind HTTPS.
|
||||
The default cookie secret is publicly known — always replace it
|
||||
before going live.
|
||||
</Note>
|
||||
</Section>
|
||||
|
||||
{/* ---------------------------------------------------------------- */}
|
||||
<Section id="jellyfin">
|
||||
<H2>Connecting Jellyfin</H2>
|
||||
@@ -419,6 +494,66 @@ npm run dev`}</Pre>
|
||||
</P>
|
||||
</Section>
|
||||
|
||||
{/* ---------------------------------------------------------------- */}
|
||||
<Section id="local-files">
|
||||
<H2>Local files provider</H2>
|
||||
<P>
|
||||
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.
|
||||
</P>
|
||||
|
||||
<H3>Enabling local files</H3>
|
||||
<P>
|
||||
Build the backend with the <Code>local-files</Code> Cargo feature
|
||||
and set the <Code>LOCAL_FILES_DIR</Code> environment variable to the
|
||||
root of your video library:
|
||||
</P>
|
||||
<Pre>{`cargo run --features local-files
|
||||
|
||||
# .env
|
||||
LOCAL_FILES_DIR=/media/videos`}</Pre>
|
||||
<P>
|
||||
On startup the backend indexes all video files under{" "}
|
||||
<Code>LOCAL_FILES_DIR</Code>. Duration is detected via{" "}
|
||||
<strong className="text-zinc-300">ffprobe</strong> (must be
|
||||
installed and on <Code>PATH</Code>). Tags are derived from ancestor
|
||||
directory names; the top-level subdirectory acts as the collection
|
||||
ID.
|
||||
</P>
|
||||
|
||||
<H3>Rescanning</H3>
|
||||
<P>
|
||||
When you add or remove files, trigger a rescan from the Dashboard
|
||||
(the <strong className="text-zinc-300">Rescan library</strong>{" "}
|
||||
button appears when the local files provider is active) or call the
|
||||
API directly:
|
||||
</P>
|
||||
<Pre>{`POST /api/v1/files/rescan
|
||||
Authorization: Bearer <token>
|
||||
|
||||
# Response
|
||||
{ "items_found": 142 }`}</Pre>
|
||||
|
||||
<H3>Streaming</H3>
|
||||
<P>
|
||||
Local file streams are served by{" "}
|
||||
<Code>GET /api/v1/files/stream/:id</Code>. This endpoint is{" "}
|
||||
<strong className="text-zinc-300">public</strong> (no auth required)
|
||||
and supports <Code>Range</Code> headers for seeking. The frontend
|
||||
player uses the native <Code><video></Code> element for local
|
||||
files instead of hls.js.
|
||||
</P>
|
||||
|
||||
<H3>Filter support</H3>
|
||||
<Note>
|
||||
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.
|
||||
</Note>
|
||||
</Section>
|
||||
|
||||
{/* ---------------------------------------------------------------- */}
|
||||
<Section id="first-channel">
|
||||
<H2>Your first channel</H2>
|
||||
@@ -743,6 +878,75 @@ Output only valid JSON matching this structure:
|
||||
</Note>
|
||||
</Section>
|
||||
|
||||
{/* ---------------------------------------------------------------- */}
|
||||
<Section id="iptv">
|
||||
<H2>IPTV export</H2>
|
||||
<P>
|
||||
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.
|
||||
</P>
|
||||
|
||||
<H3>Getting the URLs</H3>
|
||||
<P>
|
||||
Open the Dashboard and click the antenna icon on any channel card to
|
||||
open the IPTV Export dialog. It shows two URLs:
|
||||
</P>
|
||||
<Table
|
||||
head={["URL", "Format", "Purpose"]}
|
||||
rows={[
|
||||
[
|
||||
<Code key="m3u">/iptv/playlist.m3u?token=…</Code>,
|
||||
"M3U",
|
||||
"Channel list — paste this into your IPTV client as the playlist source.",
|
||||
],
|
||||
[
|
||||
<Code key="xml">/iptv/epg.xml?token=…</Code>,
|
||||
"XMLTV",
|
||||
"Electronic program guide — paste this as the EPG / guide data source.",
|
||||
],
|
||||
]}
|
||||
/>
|
||||
|
||||
<H3>Adding to an IPTV client</H3>
|
||||
<P>
|
||||
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.
|
||||
</P>
|
||||
|
||||
<Warn>
|
||||
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.
|
||||
</Warn>
|
||||
</Section>
|
||||
|
||||
{/* ---------------------------------------------------------------- */}
|
||||
<Section id="channel-password">
|
||||
<H2>Channel passwords</H2>
|
||||
<P>
|
||||
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.
|
||||
</P>
|
||||
|
||||
<H3>Setting a password</H3>
|
||||
<P>
|
||||
Enter a password in the <strong className="text-zinc-300">Password</strong>{" "}
|
||||
field when creating a channel or editing it in the Dashboard. Leave
|
||||
the field blank to remove an existing password.
|
||||
</P>
|
||||
|
||||
<Warn>
|
||||
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.
|
||||
</Warn>
|
||||
</Section>
|
||||
|
||||
{/* ---------------------------------------------------------------- */}
|
||||
<Section id="tv-page">
|
||||
<H2>Watching TV</H2>
|
||||
|
||||
Reference in New Issue
Block a user