Files
streamyfin/components/series/CastAndCrew.tsx
Uruk f543fa9e3e refactor: remove unused code and simplify implementations
Removes extensive dead code including unused components, utilities, and augmentations that were no longer referenced in the codebase.

Simplifies play settings logic by removing complex stream ranking algorithm in favor of direct previous index matching for audio and subtitle selections.

Removes aspectRatio prop from video player as it was set to a constant "default" value and never changed.

Inlines POSTER_CAROUSEL_HEIGHT constant directly where used instead of importing from centralized constants file.

Eliminates unused features including image color extraction for TV platforms, M3U8 subtitle parsing, and various Jellyfin API helpers that were no longer needed.

Cleans up credential management by making internal helper functions private that should not be exposed to external consumers.
2026-01-12 11:04:23 +01:00

86 lines
2.6 KiB
TypeScript

import type {
BaseItemDto,
BaseItemPerson,
} from "@jellyfin/sdk/lib/generated-client/models";
import { useSegments } from "expo-router";
import { useAtom } from "jotai";
import type React from "react";
import { useMemo } from "react";
import { useTranslation } from "react-i18next";
import { TouchableOpacity, View, type ViewProps } from "react-native";
import useRouter from "@/hooks/useAppRouter";
// Matches `w-28` poster cards (approx 112px wide, 10/15 aspect ratio) + 2 lines of text.
const POSTER_CAROUSEL_HEIGHT = 220;
import { apiAtom } from "@/providers/JellyfinProvider";
import { getPrimaryImageUrl } from "@/utils/jellyfin/image/getPrimaryImageUrl";
import { HorizontalScroll } from "../common/HorizontalScroll";
import { Text } from "../common/Text";
import Poster from "../posters/Poster";
interface Props extends ViewProps {
item?: BaseItemDto | null;
loading?: boolean;
}
export const CastAndCrew: React.FC<Props> = ({ item, loading, ...props }) => {
const [api] = useAtom(apiAtom);
const segments = useSegments();
const { t } = useTranslation();
const router = useRouter();
const from = (segments as string[])[2];
const destinctPeople = useMemo(() => {
const people: Record<string, BaseItemPerson> = {};
item?.People?.forEach((person) => {
if (!person.Id) return;
const existingPerson = people[person.Id];
if (existingPerson) {
existingPerson.Role = `${existingPerson.Role}, ${person.Role}`;
} else {
people[person.Id] = person;
}
});
return Object.values(people);
}, [item?.People]);
if (!from) return null;
return (
<View {...props} className='flex flex-col'>
<Text className='text-lg font-bold mb-2 px-4'>
{t("item_card.cast_and_crew")}
</Text>
<HorizontalScroll
loading={loading}
keyExtractor={(i, _idx) => i.Id?.toString() || ""}
height={POSTER_CAROUSEL_HEIGHT}
data={destinctPeople}
renderItem={(i) => (
<TouchableOpacity
onPress={() => {
if (i.Id) {
router.push({
pathname: "/persons/[personId]",
params: { personId: i.Id },
});
}
}}
className='flex flex-col w-28'
>
<Poster id={i.Id} url={getPrimaryImageUrl({ api, item: i })} />
<Text className='mt-2' numberOfLines={1}>
{i.Name}
</Text>
<Text className='text-xs opacity-50' numberOfLines={1}>
{i.Role}
</Text>
</TouchableOpacity>
)}
/>
</View>
);
};