Overview
++ K-TV turns your self-hosted media library into broadcast-style linear + TV channels. You define programming blocks — time slots with filters + and fill strategies — and the scheduler automatically picks content + from your{" "} + + Jellyfin + {" "} + library to fill them. Viewers open the TV page and watch a live + stream with no seeking — just like real TV. +
++ The project has two parts: a{" "} + backend (Rust / Axum) + that manages channels, generates schedules, and proxies streams from + Jellyfin, and a{" "} + frontend (Next.js) that + provides the TV viewer and the channel management dashboard. +
+Requirements
++ After creating the channel, open the edit sheet (pencil icon). Add + programming blocks in the list or draw them directly on the 24-hour + timeline. Once the schedule looks right, click{" "} + Generate schedule on the + channel card. K-TV queries Jellyfin, fills each block with matching + content, and starts broadcasting immediately. +
+Programming blocks
+
+ A programming block is a repeating daily time slot. Every day the
+ block starts at its start_time (in the channel
+ timezone) and runs for duration_mins minutes. The
+ scheduler fills it with as many items as will fit.
+
Timeline editor
+-
+
- + Draw a block — click + and drag on an empty area of the 24-hour timeline. + +
- + Move a block — drag the + block body left or right. Snaps to 15-minute increments. + +
- + Resize a block — drag + its right edge. + +
- + Select a block — click + it on the timeline to scroll its detail editor into view below. + +
+ Gaps between blocks are fine — the TV player shows a no-signal + screen during those times. You do not need to fill every minute of + the day. +
+ +Content types
+Overlays
++ Move your mouse or press any key to reveal the on-screen overlays. + They fade after a few seconds of inactivity. +
+-
+
- + Bottom-left — channel + info: what is playing, episode details, description, genre tags, + and a progress bar with start/end times. + +
- + Bottom-right — channel + controls (previous / next). + +
- + Top-right — Guide + toggle and CC button (when subtitles are available). + +
Program guide
++ Press G or click the + Guide button to open the upcoming schedule for the current channel. + Colour-coded blocks show each slot; the current item is highlighted. +
+ +Subtitles (CC)
++ When the playing item has subtitle tracks in its HLS stream, a{" "} + CC button appears in the + top-right corner. Click it to pick a language track or turn + subtitles off. The button is highlighted when subtitles are active. +
+ +Up next banner
++ When the current item is more than 80% complete, an "Up next" banner + appears at the bottom showing the next item's title and start time. +
+ +Autoplay after page refresh
++ Browsers block video autoplay on page refresh until the user + interacts with the page. Move your mouse or press any key after + refreshing and playback resumes immediately. +
+Troubleshooting
+ +Schedule generation fails
+
+ Check that JELLYFIN_BASE_URL,{" "}
+ JELLYFIN_API_KEY, and JELLYFIN_USER_ID are
+ all set. The backend logs a warning on startup when any are missing.
+ Confirm the Jellyfin server is reachable from the machine running
+ the backend.
+
Video won't play / stream error
++ Click Retry on the error + screen. If it keeps failing, check that Jellyfin is online and the + API key has not been revoked. For transcoding errors, check the + Jellyfin dashboard for active sessions and codec errors in its logs. +
+ +Block fills with no items
++ Your filter is too strict or Jellyfin returned nothing matching. + Try: +
+-
+
- Removing one filter at a time to find the culprit. +
- + Verifying genre/tag names match Jellyfin exactly — they are + case-sensitive. + +
-
+ Clearing
collectionsto search all libraries. +
+ -
+ Lowering
min_available_ratioif the recycle cooldown + is excluding too many items. +
+
Channel shows no signal
++ No signal means there is no scheduled slot at the current time. + Either no schedule has been generated yet (click Generate on the + Dashboard), or the current time falls in a gap between blocks. Add a + block covering the current time and regenerate. +
+ +CORS errors in the browser
+
+ Make sure CORS_ALLOWED_ORIGINS contains the exact
+ origin of the frontend — scheme, hostname, and port, no trailing
+ slash. Example: https://ktv.example.com. Wildcards are
+ not supported.
+
Subtitles not showing
++ The CC button only appears when Jellyfin includes subtitle tracks in + the HLS manifest. Verify the media item has external subtitle files + (SRT/ASS) associated in Jellyfin. Image-based subtitles (PGS/VOBSUB + from Blu-ray sources) are not supported by the HLS path. +
+