import { Ionicons } from "@expo/vector-icons"; import { MediaSourceInfo, type MediaStream, } from "@jellyfin/sdk/lib/generated-client"; import React, { useMemo, useRef } from "react"; import { TouchableOpacity, View } from "react-native"; import { Badge } from "./Badge"; import { Text } from "./common/Text"; import { BottomSheetModal, BottomSheetBackdropProps, BottomSheetBackdrop, BottomSheetView, BottomSheetScrollView, } from "@gorhom/bottom-sheet"; import { Button } from "./Button"; import { useTranslation } from "react-i18next"; import { formatBitrate } from "@/utils/bitrate"; interface Props { source?: MediaSourceInfo; } export const ItemTechnicalDetails: React.FC = ({ source, ...props }) => { const bottomSheetModalRef = useRef(null); const { t } = useTranslation(); return ( {t("item_card.video")} bottomSheetModalRef.current?.present()}> {t("item_card.more_details")} ( )} > {t("item_card.video")} {t("item_card.audio")} stream.Type === "Audio" ) || [] } /> {t("item_card.subtitles")} stream.Type === "Subtitle" ) || [] } /> ); }; const SubtitleStreamInfo = ({ subtitleStreams, }: { subtitleStreams: MediaStream[]; }) => { return ( {subtitleStreams.map((stream, index) => ( {stream.DisplayTitle} } text={stream.Language} /> } /> ))} ); }; const AudioStreamInfo = ({ audioStreams }: { audioStreams: MediaStream[] }) => { return ( {audioStreams.map((audioStreams, index) => ( {audioStreams.DisplayTitle} } text={audioStreams.Language} /> } text={audioStreams.Codec} /> } text={audioStreams.ChannelLayout} /> } text={formatBitrate(audioStreams.BitRate)} /> ))} ); }; const VideoStreamInfo = ({ source }: { source?: MediaSourceInfo }) => { if (!source) return null; const videoStream = useMemo(() => { return source.MediaStreams?.find( (stream) => stream.Type === "Video" ) as MediaStream; }, [source.MediaStreams]); if (!videoStream) return null; return ( } text={formatFileSize(source.Size)} /> } text={`${videoStream.Width}x${videoStream.Height}`} /> } text={videoStream.VideoRange} /> } text={videoStream.Codec} /> } text={formatBitrate(videoStream.BitRate)} /> } text={`${videoStream.AverageFrameRate?.toFixed(0)} fps`} /> ); }; const formatFileSize = (bytes?: number | null) => { if (!bytes) return "N/A"; const sizes = ["Bytes", "KB", "MB", "GB", "TB"]; if (bytes === 0) return "0 Byte"; const i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)).toString()); return Math.round((bytes / Math.pow(1024, i)) * 100) / 100 + " " + sizes[i]; };