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
Template variables
Custom templates use Handlebars syntax. Available variables:
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.
Admin panel
The /admin route is available to any logged-in user.
It provides two live views into the running server:
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.