mirror of
https://github.com/streamyfin/streamyfin.git
synced 2026-01-15 23:59:08 +00:00
fix: linting (#1184)
This commit is contained in:
committed by
GitHub
parent
30dc3980e3
commit
2be78a232c
@@ -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(() => {
|
||||||
|
|||||||
@@ -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>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -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'>
|
||||||
|
|||||||
@@ -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": [
|
||||||
"**/*",
|
"**/*",
|
||||||
|
|||||||
20
bun.lock
20
bun.lock
@@ -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=="],
|
||||||
|
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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]);
|
||||||
|
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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}
|
||||||
|
|||||||
@@ -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}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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 }) => (
|
||||||
|
|||||||
@@ -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}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -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 },
|
||||||
|
|||||||
@@ -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 },
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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 () => "",
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -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)}...`,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
Reference in New Issue
Block a user