diff --git a/k-tv-backend/infra/src/jellyfin/provider.rs b/k-tv-backend/infra/src/jellyfin/provider.rs index 74d2ede..78721e1 100644 --- a/k-tv-backend/infra/src/jellyfin/provider.rs +++ b/k-tv-backend/infra/src/jellyfin/provider.rs @@ -395,7 +395,7 @@ impl IMediaProvider for JellyfinMediaProvider { impl JellyfinMediaProvider { fn hls_url(&self, item_id: &MediaItemId, bitrate: u32) -> String { format!( - "{}/Videos/{}/master.m3u8?videoCodec=h264&audioCodec=aac&VideoBitRate={}&mediaSourceId={}&api_key={}", + "{}/Videos/{}/master.m3u8?videoCodec=h264&audioCodec=aac&VideoBitRate={}&mediaSourceId={}&SubtitleMethod=Hls&subtitleCodec=vtt&api_key={}", self.config.base_url, item_id.as_ref(), bitrate, diff --git a/k-tv-frontend/app/(main)/docs/page.tsx b/k-tv-frontend/app/(main)/docs/page.tsx index 30071b5..287901d 100644 --- a/k-tv-frontend/app/(main)/docs/page.tsx +++ b/k-tv-frontend/app/(main)/docs/page.tsx @@ -131,7 +131,10 @@ const TOC = [ { id: "recycle-policy", label: "Recycle policy" }, { id: "import-export", label: "Import & export" }, { id: "iptv", label: "IPTV export" }, - { id: "channel-password", label: "Channel passwords" }, + { id: "access-control", label: "Access control" }, + { id: "channel-logo", label: "Channel logo" }, + { id: "webhooks", label: "Webhooks" }, + { id: "admin", label: "Admin panel" }, { id: "tv-page", label: "Watching TV" }, { id: "troubleshooting", label: "Troubleshooting" }, ]; @@ -545,6 +548,16 @@ Authorization: Bearer files instead of hls.js.

+

Transcode settings

+

+ When transcoding is available (TRANSCODE_DIR is set), + a gear icon appears in the Dashboard header. Click it to open the{" "} + Transcode Settings{" "} + dialog, where you can adjust the cache cleanup TTL — how long + transcoded segment files are kept before the hourly cleanup removes + them. +

+

Filter support

When the local files provider is active, the series picker and genre @@ -588,10 +601,12 @@ Authorization: Bearer content, and starts broadcasting immediately.

- Schedules are valid for 48 hours. K-TV does not regenerate them - automatically — return to the Dashboard and click{" "} - Generate whenever you - want a fresh lineup. + Schedules are valid for 48 hours. If the channel's{" "} + Auto-schedule toggle is + enabled (in the edit sheet), the server regenerates the schedule + automatically when it expires. Otherwise, return to the Dashboard + and click Generate{" "} + whenever you want a fresh lineup. @@ -696,6 +711,16 @@ Authorization: Bearer "string[]", "Jellyfin library / folder IDs. Find the ID in the Jellyfin URL when browsing a library. Leave empty to search all libraries.", ], + [ + series_names, + "string[]", + "Only include episodes from the listed TV series (OR-combined). Jellyfin only.", + ], + [ + search_term, + "string", + "Free-text search passed to the provider.", + ], ]} /> @@ -924,19 +949,40 @@ Output only valid JSON matching this structure: {/* ---------------------------------------------------------------- */} -
-

Channel passwords

+
+

Access control

- 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. + Each channel has an access_mode field that controls who + can watch it. Set it in the edit sheet.

+ public, + "Anyone can watch. This is the default.", + ], + [ + password_protected, + "Viewers must enter a password before the stream plays.", + ], + [ + account_required, + "Viewers must be logged in to any K-TV account.", + ], + [ + owner_only, + "Only the channel owner can watch.", + ], + ]} + />

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. + When access_mode is{" "} + password_protected, enter a value in the{" "} + Password field in the + edit sheet. Leave the field blank to remove an existing password.

@@ -947,6 +993,88 @@ Output only valid JSON matching this structure: + {/* ---------------------------------------------------------------- */} +
+ Corner where the logo appears.{" "} + top_right (default) /{" "} + top_left /{" "} + bottom_left /{" "} + bottom_right. + , + ], + [ + "Logo opacity", + "A value from 0.0 (invisible) to 1.0 (fully opaque). Controls how prominent the watermark is.", + ], + ]} + /> + + + {/* ---------------------------------------------------------------- */} +
+

Webhooks

+

+ Channels can fire an HTTP POST to a URL of your choice when domain + events occur (e.g. a schedule is generated). Configure webhooks in + the edit sheet under the{" "} + Webhook section. +

+ +

Preset formats

+
+ +

Template variables

+

+ Custom templates use Handlebars syntax. Available variables: +

+
{"{{event}}"}, "Event name, e.g. schedule_generated."], + [{"{{timestamp}}"}, "ISO 8601 timestamp of the event."], + [{"{{data.item.title}}"}, "Title of the affected media item (where applicable)."], + ]} + /> + +

Extra headers

+

+ Use webhook_headers to add custom HTTP headers to + every delivery — for example{" "} + Authorization: Bearer … for endpoints that require + authentication. +

+ +

Poll interval

+

+ webhook_poll_interval_secs controls how often the + backend checks for pending webhook deliveries. Lower values mean + faster delivery but more database reads. +

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

Watching TV

@@ -1019,6 +1147,32 @@ Output only valid JSON matching this structure:

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

Admin panel

+

+ The /admin route is available to any logged-in user. + It provides two live views into the running server: +

+
+ + Access requires a valid login session. Unauthenticated visitors are + redirected to the login page. + + + {/* ---------------------------------------------------------------- */}

Troubleshooting

diff --git a/k-tv-frontend/app/(main)/tv/page.tsx b/k-tv-frontend/app/(main)/tv/page.tsx index 60b05af..0412ca0 100644 --- a/k-tv-frontend/app/(main)/tv/page.tsx +++ b/k-tv-frontend/app/(main)/tv/page.tsx @@ -429,6 +429,14 @@ function TvPageContent() { case "M": toggleMute(); break; + case "c": + case "C": + if (subtitleTracks.length > 0) { + setActiveSubtitleTrack((prev) => + prev === -1 ? subtitleTracks[0].id : -1, + ); + } + break; default: { if (e.key >= "0" && e.key <= "9") { setChannelInput((prev) => { @@ -464,6 +472,7 @@ function TvPageContent() { channelCount, switchChannel, resetIdle, + subtitleTracks, ]); // ------------------------------------------------------------------