fix: linting (#1184)

This commit is contained in:
Fredrik Burmester
2025-11-14 19:34:59 +01:00
committed by GitHub
parent 30dc3980e3
commit 2be78a232c
25 changed files with 114 additions and 70 deletions

View File

@@ -1,3 +1,4 @@
import { File, Paths } from "expo-file-system";
import { useNavigation } from "expo-router"; import { useNavigation } from "expo-router";
import * as Sharing from "expo-sharing"; import * as Sharing from "expo-sharing";
import { useCallback, useEffect, useId, useMemo, useState } from "react"; import { useCallback, useEffect, useId, useMemo, useState } from "react";
@@ -48,18 +49,17 @@ export default function Page() {
// Sharing it as txt while its formatted allows us to share it with many more applications // Sharing it as txt while its formatted allows us to share it with many more applications
const share = useCallback(async () => { const share = useCallback(async () => {
const uri = `${FileSystem.documentDirectory}logs.txt`; const logsFile = new File(Paths.document, "logs.txt");
setLoading(true); setLoading(true);
FileSystem.writeAsStringAsync(uri, JSON.stringify(filteredLogs)) try {
.then(() => { logsFile.write(JSON.stringify(filteredLogs));
setLoading(false); await Sharing.shareAsync(logsFile.uri, { mimeType: "txt", UTI: "txt" });
Sharing.shareAsync(uri, { mimeType: "txt", UTI: "txt" }); } catch (e: any) {
}) writeErrorLog("Something went wrong attempting to export", e);
.catch((e) => } finally {
writeErrorLog("Something went wrong attempting to export", e), setLoading(false);
) }
.finally(() => setLoading(false));
}, [filteredLogs]); }, [filteredLogs]);
useEffect(() => { useEffect(() => {

View File

@@ -87,14 +87,15 @@ export default function page() {
<Text className='font-bold text-2xl mb-1'>{data?.details?.name}</Text> <Text className='font-bold text-2xl mb-1'>{data?.details?.name}</Text>
<Text className='opacity-50'> <Text className='opacity-50'>
{t("jellyseerr.born")}{" "} {t("jellyseerr.born")}{" "}
{new Date(data?.details?.birthday!).toLocaleDateString( {data?.details?.birthday &&
`${locale}-${region}`, new Date(data.details.birthday).toLocaleDateString(
{ `${locale}-${region}`,
year: "numeric", {
month: "long", year: "numeric",
day: "numeric", month: "long",
}, day: "numeric",
)}{" "} },
)}{" "}
| {data?.details?.placeOfBirth} | {data?.details?.placeOfBirth}
</Text> </Text>
</> </>

View File

@@ -33,7 +33,6 @@ export default function page() {
<View className='flex flex-1'> <View className='flex flex-1'>
<FlashList <FlashList
data={channels?.Items} data={channels?.Items}
estimatedItemSize={76}
renderItem={({ item }) => ( renderItem={({ item }) => (
<View className='flex flex-row items-center px-4 mb-2'> <View className='flex flex-row items-center px-4 mb-2'>
<View className='w-22 mr-4 rounded-lg overflow-hidden'> <View className='w-22 mr-4 rounded-lg overflow-hidden'>

View File

@@ -1,5 +1,5 @@
{ {
"$schema": "https://biomejs.dev/schemas/2.2.7/schema.json", "$schema": "https://biomejs.dev/schemas/2.3.5/schema.json",
"files": { "files": {
"includes": [ "includes": [
"**/*", "**/*",

View File

@@ -86,7 +86,7 @@
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.20.0", "@babel/core": "^7.20.0",
"@biomejs/biome": "^2.2.4", "@biomejs/biome": "^2.3.5",
"@react-native-community/cli": "^20.0.0", "@react-native-community/cli": "^20.0.0",
"@react-native-tvos/config-tv": "^0.1.1", "@react-native-tvos/config-tv": "^0.1.1",
"@types/jest": "^29.5.12", "@types/jest": "^29.5.12",
@@ -300,23 +300,23 @@
"@babel/types": ["@babel/types@7.28.5", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA=="], "@babel/types": ["@babel/types@7.28.5", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA=="],
"@biomejs/biome": ["@biomejs/biome@2.3.4", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.3.4", "@biomejs/cli-darwin-x64": "2.3.4", "@biomejs/cli-linux-arm64": "2.3.4", "@biomejs/cli-linux-arm64-musl": "2.3.4", "@biomejs/cli-linux-x64": "2.3.4", "@biomejs/cli-linux-x64-musl": "2.3.4", "@biomejs/cli-win32-arm64": "2.3.4", "@biomejs/cli-win32-x64": "2.3.4" }, "bin": { "biome": "bin/biome" } }, "sha512-TU08LXjBHdy0mEY9APtEtZdNQQijXUDSXR7IK1i45wgoPD5R0muK7s61QcFir6FpOj/RP1+YkPx5QJlycXUU3w=="], "@biomejs/biome": ["@biomejs/biome@2.3.5", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.3.5", "@biomejs/cli-darwin-x64": "2.3.5", "@biomejs/cli-linux-arm64": "2.3.5", "@biomejs/cli-linux-arm64-musl": "2.3.5", "@biomejs/cli-linux-x64": "2.3.5", "@biomejs/cli-linux-x64-musl": "2.3.5", "@biomejs/cli-win32-arm64": "2.3.5", "@biomejs/cli-win32-x64": "2.3.5" }, "bin": { "biome": "bin/biome" } }, "sha512-HvLhNlIlBIbAV77VysRIBEwp55oM/QAjQEin74QQX9Xb259/XP/D5AGGnZMOyF1el4zcvlNYYR3AyTMUV3ILhg=="],
"@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.3.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-w40GvlNzLaqmuWYiDU6Ys9FNhJiclngKqcGld3iJIiy2bpJ0Q+8n3haiaC81uTPY/NA0d8Q/I3Z9+ajc14102Q=="], "@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.3.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-fLdTur8cJU33HxHUUsii3GLx/TR0BsfQx8FkeqIiW33cGMtUD56fAtrh+2Fx1uhiCsVZlFh6iLKUU3pniZREQw=="],
"@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.3.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-3s7TLVtjJ7ni1xADXsS7x7GMUrLBZXg8SemXc3T0XLslzvqKj/dq1xGeBQ+pOWQzng9MaozfacIHdK2UlJ3jGA=="], "@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.3.5", "", { "os": "darwin", "cpu": "x64" }, "sha512-qpT8XDqeUlzrOW8zb4k3tjhT7rmvVRumhi2657I2aGcY4B+Ft5fNwDdZGACzn8zj7/K1fdWjgwYE3i2mSZ+vOA=="],
"@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.3.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-y7efHyyM2gYmHy/AdWEip+VgTMe9973aP7XYKPzu/j8JxnPHuSUXftzmPhkVw0lfm4ECGbdBdGD6+rLmTgNZaA=="], "@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.3.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-u/pybjTBPGBHB66ku4pK1gj+Dxgx7/+Z0jAriZISPX1ocTO8aHh8x8e7Kb1rB4Ms0nA/SzjtNOVJ4exVavQBCw=="],
"@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.3.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-IruVGQRwMURivWazchiq7gKAqZSFs5so6gi0hJyxk7x6HR+iwZbO2IxNOqyLURBvL06qkIHs7Wffl6Bw30vCbQ=="], "@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.3.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-eGUG7+hcLgGnMNl1KHVZUYxahYAhC462jF/wQolqu4qso2MSk32Q+QrpN7eN4jAHAg7FUMIo897muIhK4hXhqg=="],
"@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.3.4", "", { "os": "linux", "cpu": "x64" }, "sha512-gKfjWR/6/dfIxPJCw8REdEowiXCkIpl9jycpNVHux8aX2yhWPLjydOshkDL6Y/82PcQJHn95VCj7J+BRcE5o1Q=="], "@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.3.5", "", { "os": "linux", "cpu": "x64" }, "sha512-XrIVi9YAW6ye0CGQ+yax0gLfx+BFOtKaNX74n+xHWla6Cl6huUmcKNO7HPx7BiKnJUzrxXY1qYlm7xMvi08X4g=="],
"@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.3.4", "", { "os": "linux", "cpu": "x64" }, "sha512-mzKFFv/w66e4/jCobFmD3kymCqG+FuWE7sVa4Yjqd9v7qt2UhXo67MSZKY9Ih18V2IwPzRKQPCw6KwdZs6AXSA=="], "@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.3.5", "", { "os": "linux", "cpu": "x64" }, "sha512-awVuycTPpVTH/+WDVnEEYSf6nbCBHf/4wB3lquwT7puhNg8R4XvonWNZzUsfHZrCkjkLhFH/vCZK5jHatD9FEg=="],
"@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.3.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-5TJ6JfVez+yyupJ/iGUici2wzKf0RrSAxJhghQXtAEsc67OIpdwSKAQboemILrwKfHDi5s6mu7mX+VTCTUydkw=="], "@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.3.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-DlBiMlBZZ9eIq4H7RimDSGsYcOtfOIfZOaI5CqsWiSlbTfqbPVfWtCf92wNzx8GNMbu1s7/g3ZZESr6+GwM/SA=="],
"@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.3.4", "", { "os": "win32", "cpu": "x64" }, "sha512-FGCijXecmC4IedQ0esdYNlMpx0Jxgf4zceCaMu6fkjWyjgn50ZQtMiqZZQ0Q/77yqPxvtkgZAvt5uGw0gAAjig=="], "@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.3.5", "", { "os": "win32", "cpu": "x64" }, "sha512-nUmR8gb6yvrKhtRgzwo/gDimPwnO5a4sCydf8ZS2kHIJhEmSmk+STsusr1LHTuM//wXppBawvSQi2xFXJCdgKQ=="],
"@bottom-tabs/react-navigation": ["@bottom-tabs/react-navigation@1.0.2", "", { "dependencies": { "color": "^5.0.0" }, "peerDependencies": { "@react-navigation/native": ">=7", "react": "*", "react-native": "*", "react-native-bottom-tabs": "*" } }, "sha512-OrCw8s2NzFxO1TO5W2vyr7HNvh1Yjy00f72D/0BIPtImc0aj5CRrT9nFRE7YP0FWZb0AY5+0QU9jaoph1rBlSg=="], "@bottom-tabs/react-navigation": ["@bottom-tabs/react-navigation@1.0.2", "", { "dependencies": { "color": "^5.0.0" }, "peerDependencies": { "@react-navigation/native": ">=7", "react": "*", "react-native": "*", "react-native-bottom-tabs": "*" } }, "sha512-OrCw8s2NzFxO1TO5W2vyr7HNvh1Yjy00f72D/0BIPtImc0aj5CRrT9nFRE7YP0FWZb0AY5+0QU9jaoph1rBlSg=="],

View File

@@ -50,7 +50,6 @@ export const PlayButton: React.FC<Props> = ({
selectedOptions, selectedOptions,
isOffline, isOffline,
colors, colors,
...props
}: Props) => { }: Props) => {
const { showActionSheetWithOptions } = useActionSheet(); const { showActionSheetWithOptions } = useActionSheet();
const client = useRemoteMediaClient(); const client = useRemoteMediaClient();

View File

@@ -120,7 +120,6 @@ export function InfiniteHorizontalScroll({
renderItem={({ item, index }) => ( renderItem={({ item, index }) => (
<View className='mr-2'>{renderItem(item, index)}</View> <View className='mr-2'>{renderItem(item, index)}</View>
)} )}
estimatedItemSize={height}
horizontal horizontal
onEndReached={() => { onEndReached={() => {
if (hasNextPage) { if (hasNextPage) {

View File

@@ -29,15 +29,15 @@ export const MovieCard: React.FC<MovieCardProps> = ({ item }) => {
const { showActionSheetWithOptions } = useActionSheet(); const { showActionSheetWithOptions } = useActionSheet();
const base64Image = useMemo(() => { const base64Image = useMemo(() => {
return storage.getString(item?.Id!); return item?.Id ? storage.getString(item.Id) : undefined;
}, []); }, [item?.Id]);
/** /**
* Handles deleting the file with haptic feedback. * Handles deleting the file with haptic feedback.
*/ */
const handleDeleteFile = useCallback(() => { const handleDeleteFile = useCallback(() => {
if (item.Id) { if (item.Id) {
deleteFile(item.Id, item.Type); deleteFile(item.Id);
} }
}, [deleteFile, item.Id]); }, [deleteFile, item.Id]);

View File

@@ -143,7 +143,6 @@ const ParallaxSlideShow = <T,>({
renderItem={({ item, index }) => renderItem(item, index)} renderItem={({ item, index }) => renderItem(item, index)}
keyExtractor={keyExtractor} keyExtractor={keyExtractor}
numColumns={3} numColumns={3}
estimatedItemSize={214}
ItemSeparatorComponent={() => <View className='h-2 w-2' />} ItemSeparatorComponent={() => <View className='h-2 w-2' />}
/> />
</View> </View>

View File

@@ -1,16 +1,15 @@
import { FlashList } from "@shopify/flash-list"; import { FlashList } from "@shopify/flash-list";
import type { ContentStyle } from "@shopify/flash-list/src/FlashListProps";
import { t } from "i18next"; import { t } from "i18next";
import type React from "react"; import type React from "react";
import type { PropsWithChildren } from "react"; import type { PropsWithChildren } from "react";
import { View, type ViewProps } from "react-native"; import { View, type ViewProps, type ViewStyle } from "react-native";
import { Text } from "@/components/common/Text"; import { Text } from "@/components/common/Text";
import { DiscoverSliderType } from "@/utils/jellyseerr/server/constants/discover"; import { DiscoverSliderType } from "@/utils/jellyseerr/server/constants/discover";
import type DiscoverSlider from "@/utils/jellyseerr/server/entity/DiscoverSlider"; import type DiscoverSlider from "@/utils/jellyseerr/server/entity/DiscoverSlider";
export interface SlideProps { export interface SlideProps {
slide: DiscoverSlider; slide: DiscoverSlider;
contentContainerStyle?: ContentStyle; contentContainerStyle?: ViewStyle;
} }
interface Props<T> extends SlideProps { interface Props<T> extends SlideProps {
@@ -45,7 +44,6 @@ const Slide = <T,>({
}} }}
showsHorizontalScrollIndicator={false} showsHorizontalScrollIndicator={false}
keyExtractor={keyExtractor} keyExtractor={keyExtractor}
estimatedItemSize={250}
data={data} data={data}
onEndReachedThreshold={1} onEndReachedThreshold={1}
onEndReached={onEndReached} onEndReached={onEndReached}

View File

@@ -34,7 +34,6 @@ export const SearchItemWrapper = <T,>({
}} }}
showsHorizontalScrollIndicator={false} showsHorizontalScrollIndicator={false}
keyExtractor={(_, index) => index.toString()} keyExtractor={(_, index) => index.toString()}
estimatedItemSize={250}
data={items} data={items}
onEndReachedThreshold={1} onEndReachedThreshold={1}
onEndReached={onEndReached} onEndReached={onEndReached}

View File

@@ -47,7 +47,6 @@ const JellyseerrSeasonEpisodes: React.FC<{
horizontal horizontal
loading={isLoading} loading={isLoading}
showsHorizontalScrollIndicator={false} showsHorizontalScrollIndicator={false}
estimatedItemSize={50}
data={seasonWithEpisodes?.episodes} data={seasonWithEpisodes?.episodes}
keyExtractor={(item) => item.id.toString()} keyExtractor={(item) => item.id.toString()}
renderItem={(item, index) => ( renderItem={(item, index) => (
@@ -284,7 +283,6 @@ const JellyseerrSeasons: React.FC<{
</View> </View>
)} )}
ItemSeparatorComponent={() => <View className='h-2' />} ItemSeparatorComponent={() => <View className='h-2' />}
estimatedItemSize={250}
renderItem={({ item: season }) => ( renderItem={({ item: season }) => (
<> <>
<TouchableOpacity <TouchableOpacity

View File

@@ -49,7 +49,6 @@ export const NextUp: React.FC<{ seriesId: string }> = ({ seriesId }) => {
<FlashList <FlashList
contentContainerStyle={{ paddingLeft: 16 }} contentContainerStyle={{ paddingLeft: 16 }}
horizontal horizontal
estimatedItemSize={172}
showsHorizontalScrollIndicator={false} showsHorizontalScrollIndicator={false}
data={items} data={items}
renderItem={({ item, index }) => ( renderItem={({ item, index }) => (

View File

@@ -4,7 +4,7 @@ import { useQuery } from "@tanstack/react-query";
import { router } from "expo-router"; import { router } from "expo-router";
import { useAtom } from "jotai"; import { useAtom } from "jotai";
import { useEffect, useMemo, useRef } from "react"; import { useEffect, useMemo, useRef } from "react";
import { TouchableOpacity, type ViewProps } from "react-native"; import { TouchableOpacity, type ViewStyle } from "react-native";
import { useDownload } from "@/providers/DownloadProvider"; import { useDownload } from "@/providers/DownloadProvider";
import { apiAtom, userAtom } from "@/providers/JellyfinProvider"; import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
import ContinueWatchingPoster from "../ContinueWatchingPoster"; import ContinueWatchingPoster from "../ContinueWatchingPoster";
@@ -14,17 +14,20 @@ import {
} from "../common/HorizontalScroll"; } from "../common/HorizontalScroll";
import { ItemCardText } from "../ItemCardText"; import { ItemCardText } from "../ItemCardText";
interface Props extends ViewProps { interface Props {
item?: BaseItemDto | null; item?: BaseItemDto | null;
loading?: boolean; loading?: boolean;
isOffline?: boolean; isOffline?: boolean;
style?: ViewStyle;
containerStyle?: ViewStyle;
} }
export const SeasonEpisodesCarousel: React.FC<Props> = ({ export const SeasonEpisodesCarousel: React.FC<Props> = ({
item, item,
loading, loading,
isOffline, isOffline,
...props style,
containerStyle,
}) => { }) => {
const [api] = useAtom(apiAtom); const [api] = useAtom(apiAtom);
const [user] = useAtom(userAtom); const [user] = useAtom(userAtom);
@@ -90,6 +93,8 @@ export const SeasonEpisodesCarousel: React.FC<Props> = ({
data={episodes} data={episodes}
extraData={item} extraData={item}
loading={loading || isPending} loading={loading || isPending}
style={style}
containerStyle={containerStyle}
renderItem={(_item, _idx) => ( renderItem={(_item, _idx) => (
<TouchableOpacity <TouchableOpacity
key={_item.Id} key={_item.Id}
@@ -104,7 +109,6 @@ export const SeasonEpisodesCarousel: React.FC<Props> = ({
<ItemCardText item={_item} /> <ItemCardText item={_item} />
</TouchableOpacity> </TouchableOpacity>
)} )}
{...props}
/> />
); );
}; };

View File

@@ -271,7 +271,6 @@ export const EpisodeList: React.FC<Props> = ({ item, close, goToItem }) => {
</View> </View>
)} )}
keyExtractor={(e: BaseItemDto) => e.Id ?? ""} keyExtractor={(e: BaseItemDto) => e.Id ?? ""}
estimatedItemSize={200}
showsHorizontalScrollIndicator={false} showsHorizontalScrollIndicator={false}
/> />
)} )}

View File

@@ -89,10 +89,10 @@ const SliderScrubber: React.FC<SliderScrubberProps> = ({
<Image <Image
cachePolicy={"memory-disk"} cachePolicy={"memory-disk"}
style={{ style={{
width: 150 * trickplayInfo?.data.TileWidth!, width: 150 * trickplayInfo.data.TileWidth,
height: height:
(150 / trickplayInfo.aspectRatio!) * (150 / trickplayInfo.aspectRatio) *
trickplayInfo?.data.TileHeight!, trickplayInfo.data.TileHeight,
transform: [ transform: [
{ translateX: -x * tileWidth }, { translateX: -x * tileWidth },
{ translateY: -y * tileHeight }, { translateY: -y * tileHeight },

View File

@@ -63,10 +63,10 @@ export const TrickplayBubble: FC<TrickplayBubbleProps> = ({
<Image <Image
cachePolicy={"memory-disk"} cachePolicy={"memory-disk"}
style={{ style={{
width: tileWidth * trickplayInfo?.data.TileWidth!, width: tileWidth * (trickplayInfo.data.TileWidth ?? 1),
height: height:
(tileWidth / trickplayInfo.aspectRatio!) * (tileWidth / (trickplayInfo.aspectRatio ?? 1)) *
trickplayInfo?.data.TileHeight!, (trickplayInfo.data.TileHeight ?? 1),
transform: [ transform: [
{ translateX: -x * tileWidth }, { translateX: -x * tileWidth },
{ translateY: -y * tileHeight }, { translateY: -y * tileHeight },

View File

@@ -31,7 +31,9 @@ export interface ActiveDownload {
export interface BackgroundDownloaderModuleType { export interface BackgroundDownloaderModuleType {
startDownload(url: string, destinationPath?: string): Promise<number>; startDownload(url: string, destinationPath?: string): Promise<number>;
enqueueDownload(url: string, destinationPath?: string): Promise<number>;
cancelDownload(taskId: number): void; cancelDownload(taskId: number): void;
cancelQueuedDownload(url: string): void;
cancelAllDownloads(): void; cancelAllDownloads(): void;
getActiveDownloads(): Promise<ActiveDownload[]>; getActiveDownloads(): Promise<ActiveDownload[]>;
addListener( addListener(

View File

@@ -104,7 +104,7 @@
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.20.0", "@babel/core": "^7.20.0",
"@biomejs/biome": "^2.2.4", "@biomejs/biome": "^2.3.5",
"@react-native-community/cli": "^20.0.0", "@react-native-community/cli": "^20.0.0",
"@react-native-tvos/config-tv": "^0.1.1", "@react-native-tvos/config-tv": "^0.1.1",
"@types/jest": "^29.5.12", "@types/jest": "^29.5.12",

View File

@@ -8,6 +8,7 @@ import {
getAllDownloadedItems, getAllDownloadedItems,
getDownloadedItemById, getDownloadedItemById,
getDownloadsDatabase, getDownloadsDatabase,
updateDownloadedItem,
} from "./Downloads/database"; } from "./Downloads/database";
import { getDownloadedItemSize } from "./Downloads/fileOperations"; import { getDownloadedItemSize } from "./Downloads/fileOperations";
import { useDownloadEventHandlers } from "./Downloads/hooks/useDownloadEventHandlers"; import { useDownloadEventHandlers } from "./Downloads/hooks/useDownloadEventHandlers";
@@ -29,7 +30,7 @@ function useDownloadProvider() {
const successHapticFeedback = useHaptic("success"); const successHapticFeedback = useHaptic("success");
// Track task ID to process ID mapping // Track task ID to process ID mapping
const taskMapRef = useRef<Map<number, string>>(new Map()); const taskMapRef = useRef<Map<number | string, string>>(new Map());
// Reactive downloaded items that updates when refreshKey changes // Reactive downloaded items that updates when refreshKey changes
const downloadedItems = useMemo(() => { const downloadedItems = useMemo(() => {
@@ -130,13 +131,13 @@ function useDownloadProvider() {
cancelDownload, cancelDownload,
getDownloadedItemSize, getDownloadedItemSize,
getDownloadedItemById, getDownloadedItemById,
updateDownloadedItem,
triggerRefresh, triggerRefresh,
APP_CACHE_DOWNLOAD_DIRECTORY: APP_CACHE_DOWNLOAD_DIRECTORY.uri, APP_CACHE_DOWNLOAD_DIRECTORY: APP_CACHE_DOWNLOAD_DIRECTORY.uri,
appSizeUsage, appSizeUsage,
// Deprecated/not implemented in simple version // Deprecated/not implemented in simple version
startDownload: async () => {}, startDownload: async () => {},
cleanCacheDirectory: async () => {}, cleanCacheDirectory: async () => {},
updateDownloadedItem: () => {},
dumpDownloadDiagnostics: async () => "", dumpDownloadDiagnostics: async () => "",
}; };
} }
@@ -161,9 +162,9 @@ export function useDownload() {
startDownload: async () => {}, startDownload: async () => {},
getDownloadedItemSize: () => 0, getDownloadedItemSize: () => 0,
getDownloadedItemById: () => undefined, getDownloadedItemById: () => undefined,
updateDownloadedItem: () => {},
APP_CACHE_DOWNLOAD_DIRECTORY: "", APP_CACHE_DOWNLOAD_DIRECTORY: "",
cleanCacheDirectory: async () => {}, cleanCacheDirectory: async () => {},
updateDownloadedItem: () => {},
appSizeUsage: async () => ({ total: 0, remaining: 0, appSize: 0 }), appSizeUsage: async () => ({ total: 0, remaining: 0, appSize: 0 }),
dumpDownloadDiagnostics: async () => "", dumpDownloadDiagnostics: async () => "",
}; };

View File

@@ -185,10 +185,16 @@ export async function fetchSegments(
}> { }> {
try { try {
const segments = await fetchAndParseSegments(itemId, api); const segments = await fetchAndParseSegments(itemId, api);
return segments; return {
introSegments: segments.introSegments,
creditSegments: segments.creditSegments,
};
} catch (error) { } catch (error) {
console.error(`[SEGMENTS] Failed to fetch segments:`, error); console.error(`[SEGMENTS] Failed to fetch segments:`, error);
return {}; return {
introSegments: undefined,
creditSegments: undefined,
};
} }
} }
@@ -222,7 +228,12 @@ export async function downloadAdditionalAssets(params: {
mediaSource.TranscodingUrl mediaSource.TranscodingUrl
? Promise.resolve(mediaSource) ? Promise.resolve(mediaSource)
: downloadSubtitles(mediaSource, item, api.basePath || ""), : downloadSubtitles(mediaSource, item, api.basePath || ""),
item.Id ? fetchSegments(item.Id, api) : Promise.resolve({}), item.Id
? fetchSegments(item.Id, api)
: Promise.resolve({
introSegments: undefined,
creditSegments: undefined,
}),
// Cover image downloads (run but don't wait for results) // Cover image downloads (run but don't wait for results)
downloadCoverImage(item, api, saveImageFn).catch((err) => { downloadCoverImage(item, api, saveImageFn).catch((err) => {
console.error("[COVER] Error downloading cover:", err); console.error("[COVER] Error downloading cover:", err);

View File

@@ -181,6 +181,41 @@ export function removeDownloadedItem(id: string): DownloadedItem | undefined {
return itemToDelete; return itemToDelete;
} }
/**
* Update a downloaded item in the database
*/
export function updateDownloadedItem(
_id: string,
updatedItem: DownloadedItem,
): void {
const db = getDownloadsDatabase();
const baseItem = updatedItem.item;
if (baseItem.Type === "Movie" && baseItem.Id) {
db.movies[baseItem.Id] = updatedItem;
} else if (
baseItem.Type === "Episode" &&
baseItem.SeriesId &&
baseItem.ParentIndexNumber !== undefined &&
baseItem.ParentIndexNumber !== null &&
baseItem.IndexNumber !== undefined &&
baseItem.IndexNumber !== null
) {
const seriesId = baseItem.SeriesId;
const seasonNumber = baseItem.ParentIndexNumber;
const episodeNumber = baseItem.IndexNumber;
if (db.series[seriesId]?.seasons[seasonNumber]?.episodes[episodeNumber]) {
db.series[seriesId].seasons[seasonNumber].episodes[episodeNumber] =
updatedItem;
}
} else if (baseItem.Id && db.other?.[baseItem.Id]) {
db.other[baseItem.Id] = updatedItem;
}
saveDownloadsDatabase(db);
}
/** /**
* Clear all downloaded items from the database * Clear all downloaded items from the database
*/ */

View File

@@ -24,7 +24,7 @@ import {
} from "./useDownloadSpeedCalculator"; } from "./useDownloadSpeedCalculator";
interface UseDownloadEventHandlersProps { interface UseDownloadEventHandlersProps {
taskMapRef: MutableRefObject<Map<number, string>>; taskMapRef: MutableRefObject<Map<number | string, string>>;
processes: JobStatus[]; processes: JobStatus[];
updateProcess: ( updateProcess: (
processId: string, processId: string,
@@ -59,7 +59,8 @@ export function useDownloadEventHandlers({
// If no mapping exists, find by URL (for queued downloads) // If no mapping exists, find by URL (for queued downloads)
if (!processId && event.url) { if (!processId && event.url) {
// Check if we have a URL mapping (queued download) // Check if we have a URL mapping (queued download)
processId = taskMapRef.current.get(event.url); const urlKey = event.url;
processId = taskMapRef.current.get(urlKey);
if (!processId) { if (!processId) {
// Fallback: search by matching URL in processes // Fallback: search by matching URL in processes
@@ -74,7 +75,7 @@ export function useDownloadEventHandlers({
if (processId) { if (processId) {
// Create taskId mapping and remove URL mapping // Create taskId mapping and remove URL mapping
taskMapRef.current.set(event.taskId, processId); taskMapRef.current.set(event.taskId, processId);
taskMapRef.current.delete(event.url); taskMapRef.current.delete(urlKey);
console.log( console.log(
`[DPL] Mapped queued download: taskId=${event.taskId} to processId=${processId.slice(0, 8)}...`, `[DPL] Mapped queued download: taskId=${event.taskId} to processId=${processId.slice(0, 8)}...`,
); );

View File

@@ -27,7 +27,7 @@ import type { JobStatus } from "../types";
import { generateFilename, uriToFilePath } from "../utils"; import { generateFilename, uriToFilePath } from "../utils";
interface UseDownloadOperationsProps { interface UseDownloadOperationsProps {
taskMapRef: MutableRefObject<Map<number, string>>; taskMapRef: MutableRefObject<Map<number | string, string>>;
processes: JobStatus[]; processes: JobStatus[];
setProcesses: (updater: (prev: JobStatus[]) => JobStatus[]) => void; setProcesses: (updater: (prev: JobStatus[]) => JobStatus[]) => void;
removeProcess: (id: string) => void; removeProcess: (id: string) => void;
@@ -169,7 +169,7 @@ export function useDownloadOperations({
if (typeof key === "number") { if (typeof key === "number") {
taskId = key; taskId = key;
} else { } else {
downloadUrl = key; downloadUrl = key as string;
} }
} }
}); });

View File

@@ -94,9 +94,9 @@ export const PlaySettingsProvider: React.FC<{ children: React.ReactNode }> = ({
console.log(`${data?.url?.slice(0, 100)}...${data?.url?.slice(-50)}`); console.log(`${data?.url?.slice(0, 100)}...${data?.url?.slice(-50)}`);
_setPlaySettings(newSettings); _setPlaySettings(newSettings);
setPlayUrl(data?.url!); if (data?.url) setPlayUrl(data.url);
setPlaySessionId(data?.sessionId!); if (data?.sessionId) setPlaySessionId(data.sessionId);
setMediaSource(data?.mediaSource!); if (data?.mediaSource) setMediaSource(data.mediaSource);
return data; return data;
} catch (error) { } catch (error) {