/** * 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 { 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/chromecast/options"; 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; showTechnicalInfo: boolean; onToggleTechnicalInfo: () => 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, mediaSources, selectedMediaSource, onMediaSourceChange, audioTracks, selectedAudioTrack, onAudioTrackChange, subtitleTracks, selectedSubtitleTrack, onSubtitleTrackChange, playbackSpeed, onPlaybackSpeedChange, showTechnicalInfo, onToggleTechnicalInfo, }) => { const insets = useSafeAreaInsets(); 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 */} Playback Settings {/* Quality/Media Source */} {renderSectionHeader("Quality", "film-outline", "quality")} {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 */} {renderSectionHeader("Audio", "musical-notes", "audio")} {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 || "Unknown"} {track.codec && ( {track.codec.toUpperCase()} )} {selectedAudioTrack?.index === track.index && ( )} ))} )} {/* Subtitle Tracks */} {renderSectionHeader("Subtitles", "text", "subtitles")} {expandedSection === "subtitles" && ( { onSubtitleTrackChange(null); setExpandedSection(null); }} style={{ flexDirection: "row", justifyContent: "space-between", alignItems: "center", padding: 16, backgroundColor: selectedSubtitleTrack === null ? "#2a2a2a" : "transparent", }} > 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 || "Unknown"} {track.codec && ( {track.codec.toUpperCase()} {track.isForced && " • Forced"} )} {selectedSubtitleTrack?.index === track.index && ( )} ))} )} {/* Playback Speed */} {renderSectionHeader("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: playbackSpeed === speed ? "#2a2a2a" : "transparent", }} > {speed === 1 ? "Normal" : `${speed}x`} {playbackSpeed === speed && ( )} ))} )} {/* Technical Info Toggle */} Show Technical Info ); };