import { motion } from 'framer-motion' import type { ButtonHTMLAttributes } from 'react' import { ArrowLeft, AudioLines, Bookmark, Download, Info, List, ListDetails, Moon, PictureInPicture2, Search, Subtitles, Users, } from '../../lib/icons' import SpeedMenu from './SpeedMenu' import QualityMenu, { type QualityOption } from './QualityMenu' import TrackMenu from './TrackMenu' import SubtitleStyleMenu from './SubtitleStyleMenu' import PictureMenu from './PictureMenu' import type { BaseItemDto } from '../../api/types' interface TrackInfo { Index?: number Codec?: string | null Profile?: string | null Language?: string | null Title?: string | null Channels?: number | null ChannelLayout?: string | null IsDefault?: boolean | null IsForced?: boolean | null IsHearingImpaired?: boolean | null IsExternal?: boolean | null AudioSpatialFormat?: string | null Type?: string } interface Props { item: BaseItemDto | null | undefined playbackRate: number onPlaybackRateChange: (rate: number) => void qualityKey: string onQualitySelect: (q: QualityOption) => void onPictureInPicture: () => void audioTracks: TrackInfo[] audioIndex: number | null onAudioSelect: (i: number | null) => void subtitleTracks: TrackInfo[] subtitleIndex: number | null onSubtitleSelect: (i: number | null) => void hasSeries: boolean episodesOpen: boolean onToggleEpisodes: () => void hasChapters: boolean chaptersOpen: boolean onToggleChapters: () => void bookmarksOpen: boolean onToggleBookmarks: () => void subSearchOpen: boolean onToggleSubSearch: () => void syncPlayOpen: boolean onToggleSyncPlay: () => void syncPlayActive: boolean videoBrightness: number videoContrast: number videoSaturation: number onPictureChange: (key: 'brightness' | 'contrast' | 'saturation', value: number) => void onPictureReset: () => void streamInfoOpen: boolean onToggleStreamInfo: () => void onBack: () => void sleepRemainingSec?: number onDownload?: () => void isDownloaded?: boolean } export default function PlayerTopBar({ item, playbackRate, onPlaybackRateChange, qualityKey, onQualitySelect, onPictureInPicture, audioTracks, audioIndex, onAudioSelect, subtitleTracks, subtitleIndex, onSubtitleSelect, hasSeries, episodesOpen, onToggleEpisodes, hasChapters, chaptersOpen, onToggleChapters, bookmarksOpen, onToggleBookmarks, subSearchOpen, onToggleSubSearch, syncPlayOpen, onToggleSyncPlay, syncPlayActive, videoBrightness, videoContrast, videoSaturation, onPictureChange, onPictureReset, streamInfoOpen, onToggleStreamInfo, onBack, sleepRemainingSec, onDownload, isDownloaded, }: Props) { return (
{onDownload && ( )} {sleepRemainingSec != null && sleepRemainingSec > 0 && ( {formatSleep(sleepRemainingSec)} )} } title="Audio" tracks={audioTracks} selectedIndex={audioIndex ?? (audioTracks.find(t => t.IsDefault)?.Index ?? null)} onSelect={onAudioSelect} variant="audio" /> } title="Subtitles" tracks={subtitleTracks} selectedIndex={subtitleIndex} onSelect={onSubtitleSelect} variant="subtitle" /> {subtitleIndex != null && ( )} {hasSeries && ( )} {hasChapters && ( )} {syncPlayActive && ( )}
) } function ChromeButton({ children, className = '', ...props }: ButtonHTMLAttributes) { return ( ) } function formatSleep(sec: number): string { const m = Math.floor(sec / 60) const s = sec % 60 if (m >= 60) return `${Math.floor(m / 60)}:${String(m % 60).padStart(2, '0')}` return `${m}:${String(s).padStart(2, '0')}` }