diff --git a/k-tv-frontend/app/(main)/tv/components/schedule-overlay.tsx b/k-tv-frontend/app/(main)/tv/components/schedule-overlay.tsx index 85971a4..f6a1974 100644 --- a/k-tv-frontend/app/(main)/tv/components/schedule-overlay.tsx +++ b/k-tv-frontend/app/(main)/tv/components/schedule-overlay.tsx @@ -3,7 +3,12 @@ import { cn } from "@/lib/utils"; export interface ScheduleSlot { id: string; + /** Headline: series name for episodes, film title for everything else. */ title: string; + /** Secondary line: "S1 · E3 · Episode Title" for episodes, year for movies. */ + subtitle?: string | null; + /** Rounded slot duration in minutes. */ + durationMins: number; startTime: string; // "HH:MM" endTime: string; // "HH:MM" isCurrent?: boolean; @@ -50,8 +55,17 @@ export function ScheduleOverlay({ channelName, slots }: ScheduleOverlayProps) { > {slot.title}

+ {slot.subtitle && ( +

+ {slot.subtitle} +

+ )}

{slot.startTime} – {slot.endTime} + {" · "}{slot.durationMins}m

diff --git a/k-tv-frontend/app/(main)/tv/page.tsx b/k-tv-frontend/app/(main)/tv/page.tsx index 82d8845..cbdf8a0 100644 --- a/k-tv-frontend/app/(main)/tv/page.tsx +++ b/k-tv-frontend/app/(main)/tv/page.tsx @@ -11,7 +11,7 @@ import { NoSignal, } from "./components"; import type { SubtitleTrack } from "./components/video-player"; -import { Maximize2, Minimize2, Volume2, VolumeX } from "lucide-react"; +import { Maximize2, Minimize2, Volume1, Volume2, VolumeX } from "lucide-react"; import { useAuthContext } from "@/context/auth-context"; import { useChannels, useCurrentBroadcast, useEpg } from "@/hooks/use-channels"; import { @@ -76,12 +76,17 @@ export default function TvPage() { } }, []); - // Volume / mute + // Volume control + const [volume, setVolume] = useState(1); // 0.0 – 1.0 const [isMuted, setIsMuted] = useState(false); + const [showVolumeSlider, setShowVolumeSlider] = useState(false); useEffect(() => { - if (videoRef.current) videoRef.current.muted = isMuted; - }, [isMuted]); + if (!videoRef.current) return; + videoRef.current.muted = isMuted; + videoRef.current.volume = volume; + }, [isMuted, volume]); const toggleMute = useCallback(() => setIsMuted((m) => !m), []); + const VolumeIcon = isMuted || volume === 0 ? VolumeX : volume < 0.5 ? Volume1 : Volume2; // Channel jump by number (e.g. press "1","4" → jump to ch 14 after 1.5 s) const [channelInput, setChannelInput] = useState(""); @@ -137,10 +142,11 @@ export default function TvPage() { const resetIdle = useCallback(() => { setShowOverlays(true); if (idleTimer.current) clearTimeout(idleTimer.current); - idleTimer.current = setTimeout( - () => setShowOverlays(false), - IDLE_TIMEOUT_MS, - ); + idleTimer.current = setTimeout(() => { + setShowOverlays(false); + setShowVolumeSlider(false); + setShowSubtitlePicker(false); + }, IDLE_TIMEOUT_MS); // Resume playback if autoplay was blocked (e.g. on page refresh with no prior interaction) videoRef.current?.play().catch(() => {}); }, []); @@ -395,15 +401,44 @@ export default function TvPage() { )} - + {/* Volume control */} +
+ + + {showVolumeSlider && ( +
+ { + const v = Number(e.target.value) / 100; + setVolume(v); + setIsMuted(v === 0); + }} + className="w-full accent-white" + /> +
+ + + {isMuted ? "0" : Math.round(volume * 100)}% + +
+
+ )} +