/** * Chromecast Settings Menu * Allows users to configure audio, subtitles, quality, and playback speed */ import { Ionicons } from "@expo/vector-icons"; import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models"; import React, { useState } from "react"; import { useTranslation } from "react-i18next"; import { Modal, Pressable, ScrollView, View } from "react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { Text } from "@/components/common/Text"; import type { AudioTrack, MediaSource, SubtitleTrack, } from "@/utils/casting/types"; interface ChromecastSettingsMenuProps { visible: boolean; onClose: () => void; item: BaseItemDto; mediaSources: MediaSource[]; selectedMediaSource: MediaSource | null; onMediaSourceChange: (source: MediaSource) => void; audioTracks: AudioTrack[]; selectedAudioTrack: AudioTrack | null; onAudioTrackChange: (track: AudioTrack) => void; subtitleTracks: SubtitleTrack[]; selectedSubtitleTrack: SubtitleTrack | null; onSubtitleTrackChange: (track: SubtitleTrack | null) => void; playbackSpeed: number; onPlaybackSpeedChange: (speed: number) => void; } const PLAYBACK_SPEEDS = [0.25, 0.5, 0.75, 1, 1.25, 1.5, 1.75, 2]; export const ChromecastSettingsMenu: React.FC = ({ visible, onClose, item: _item, // Reserved for future use (technical info display) mediaSources, selectedMediaSource, onMediaSourceChange, audioTracks, selectedAudioTrack, onAudioTrackChange, subtitleTracks, selectedSubtitleTrack, onSubtitleTrackChange, playbackSpeed, onPlaybackSpeedChange, }) => { const insets = useSafeAreaInsets(); const { t } = useTranslation(); const [expandedSection, setExpandedSection] = useState(null); const toggleSection = (section: string) => { setExpandedSection(expandedSection === section ? null : section); }; const renderSectionHeader = ( title: string, icon: keyof typeof Ionicons.glyphMap, sectionKey: string, ) => ( toggleSection(sectionKey)} style={{ flexDirection: "row", justifyContent: "space-between", alignItems: "center", padding: 16, borderBottomWidth: 1, borderBottomColor: "#333", }} > {title} ); return ( e.stopPropagation()} > {/* Header */} {t("casting_player.playback_settings")} {/* Quality/Media Source - only show when sources available */} {mediaSources.length > 0 && renderSectionHeader( t("casting_player.quality"), "film-outline", "quality", )} {mediaSources.length > 0 && expandedSection === "quality" && ( {mediaSources.map((source) => ( { onMediaSourceChange(source); setExpandedSection(null); }} style={{ flexDirection: "row", justifyContent: "space-between", alignItems: "center", padding: 16, backgroundColor: selectedMediaSource?.id === source.id ? "#2a2a2a" : "transparent", }} > {source.name} {source.bitrate && ( {Math.round(source.bitrate / 1000000)} Mbps )} {selectedMediaSource?.id === source.id && ( )} ))} )} {/* Audio Tracks - only show if more than one track */} {audioTracks.length > 1 && renderSectionHeader( t("casting_player.audio"), "musical-notes", "audio", )} {audioTracks.length > 1 && expandedSection === "audio" && ( {audioTracks.map((track) => ( { onAudioTrackChange(track); setExpandedSection(null); }} style={{ flexDirection: "row", justifyContent: "space-between", alignItems: "center", padding: 16, backgroundColor: selectedAudioTrack?.index === track.index ? "#2a2a2a" : "transparent", }} > {track.displayTitle || track.language || t("casting_player.unknown")} {track.codec && ( {track.codec.toUpperCase()} )} {selectedAudioTrack?.index === track.index && ( )} ))} )} {/* Subtitle Tracks - only show if subtitles available */} {subtitleTracks.length > 0 && renderSectionHeader( t("casting_player.subtitles"), "text", "subtitles", )} {subtitleTracks.length > 0 && expandedSection === "subtitles" && ( { onSubtitleTrackChange(null); setExpandedSection(null); }} style={{ flexDirection: "row", justifyContent: "space-between", alignItems: "center", padding: 16, backgroundColor: selectedSubtitleTrack === null ? "#2a2a2a" : "transparent", }} > {t("casting_player.none")} {selectedSubtitleTrack === null && ( )} {subtitleTracks.map((track) => ( { onSubtitleTrackChange(track); setExpandedSection(null); }} style={{ flexDirection: "row", justifyContent: "space-between", alignItems: "center", padding: 16, backgroundColor: selectedSubtitleTrack?.index === track.index ? "#2a2a2a" : "transparent", }} > {track.displayTitle || track.language || t("casting_player.unknown")} {track.codec && ( {track.codec.toUpperCase()} {track.isForced && ` • ${t("casting_player.forced")}`} )} {selectedSubtitleTrack?.index === track.index && ( )} ))} )} {/* Playback Speed */} {renderSectionHeader( t("casting_player.playback_speed"), "speedometer", "speed", )} {expandedSection === "speed" && ( {PLAYBACK_SPEEDS.map((speed) => ( { onPlaybackSpeedChange(speed); setExpandedSection(null); }} style={{ flexDirection: "row", justifyContent: "space-between", alignItems: "center", padding: 16, backgroundColor: Math.abs(playbackSpeed - speed) < 0.01 ? "#2a2a2a" : "transparent", }} > {speed === 1 ? t("casting_player.normal") : `${speed}x`} {Math.abs(playbackSpeed - speed) < 0.01 && ( )} ))} )} ); };