fix: trailers for movies

This commit is contained in:
Fredrik Burmester
2024-12-31 21:47:43 +01:00
parent 479a1f037e
commit f8d22bb7d6
8 changed files with 56 additions and 56 deletions

View File

@@ -4,7 +4,7 @@ import { ParallaxScrollView } from "@/components/ParallaxPage";
import { Ratings } from "@/components/Ratings";
import { NextUp } from "@/components/series/NextUp";
import { SeasonPicker } from "@/components/series/SeasonPicker";
import { SeriesActions } from "@/components/series/SeriesActions";
import { ItemActions } from "@/components/series/SeriesActions";
import { SeriesHeader } from "@/components/series/SeriesHeader";
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
import { getBackdropUrl } from "@/utils/jellyfin/image/getBackdropUrl";

View File

@@ -1,12 +1,10 @@
import { useGlobalSearchParams, useNavigation } from "expo-router";
import { useState, useCallback, useEffect, useMemo } from "react";
import { Button, Dimensions } from "react-native";
import { Alert, View } from "react-native";
import { useGlobalSearchParams } from "expo-router";
import { useCallback, useEffect, useMemo, useState } from "react";
import { Alert, Dimensions, View } from "react-native";
import YoutubePlayer, { PLAYER_STATES } from "react-native-youtube-iframe";
export default function page() {
const searchParams = useGlobalSearchParams();
const navigation = useNavigation();
console.log(searchParams);
const { url } = searchParams as { url: string };
@@ -29,15 +27,10 @@ export default function page() {
}, []);
useEffect(() => {
navigation.setOptions({
headerShown: false,
});
togglePlaying();
}, []);
const screenWidth = Dimensions.get("screen").width;
const screenHeight = Dimensions.get("screen").height;
return (
<View className="flex flex-col bg-black items-center justify-center h-full">

View File

@@ -338,6 +338,7 @@ function Layout() {
<Stack.Screen
name="(auth)/trailer/page"
options={{
headerShown: false,
presentation: "modal",
title: "",
}}

View File

@@ -15,12 +15,12 @@ import useDefaultPlaySettings from "@/hooks/useDefaultPlaySettings";
import { useImageColors } from "@/hooks/useImageColors";
import { useOrientation } from "@/hooks/useOrientation";
import { apiAtom } from "@/providers/JellyfinProvider";
import { SubtitleHelper } from "@/utils/SubtitleHelper";
import { useSettings } from "@/utils/atoms/settings";
import { getLogoImageUrlById } from "@/utils/jellyfin/image/getLogoImageUrlById";
import {
BaseItemDto,
MediaSourceInfo,
MediaStream,
} from "@jellyfin/sdk/lib/generated-client/models";
import { Image } from "expo-image";
import { useNavigation } from "expo-router";
@@ -31,10 +31,9 @@ import { View } from "react-native";
import { useSafeAreaInsets } from "react-native-safe-area-context";
import { Chromecast } from "./Chromecast";
import { ItemHeader } from "./ItemHeader";
import { ItemTechnicalDetails } from "./ItemTechnicalDetails";
import { MediaSourceSelector } from "./MediaSourceSelector";
import { MoreMoviesWithActor } from "./MoreMoviesWithActor";
import { SubtitleHelper } from "@/utils/SubtitleHelper";
import { ItemTechnicalDetails } from "./ItemTechnicalDetails";
export type SelectedOptions = {
bitrate: Bitrate;

View File

@@ -1,10 +1,11 @@
import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
import React from "react";
import { View, ViewProps } from "react-native";
import { GenreTags } from "./GenreTags";
import { MoviesTitleHeader } from "./movies/MoviesTitleHeader";
import { Ratings } from "./Ratings";
import { EpisodeTitleHeader } from "./series/EpisodeTitleHeader";
import { GenreTags } from "./GenreTags";
import React from "react";
import { ItemActions } from "./series/SeriesActions";
interface Props extends ViewProps {
item?: BaseItemDto | null;
@@ -27,7 +28,10 @@ export const ItemHeader: React.FC<Props> = ({ item, ...props }) => {
return (
<View className="flex flex-col" {...props}>
<View className="flex flex-col" {...props}>
<Ratings item={item} className="mb-2" />
<View className="flex flex-row items-center justify-between">
<Ratings item={item} className="mb-2" />
<ItemActions item={item} />
</View>
{item.Type === "Episode" && (
<>
<EpisodeTitleHeader item={item} />

View File

@@ -2,13 +2,13 @@ import { Ionicons } from "@expo/vector-icons";
import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client";
import { useRouter } from "expo-router";
import { useCallback, useMemo } from "react";
import { View, TouchableOpacity, ViewProps } from "react-native";
import { TouchableOpacity, View, ViewProps } from "react-native";
interface Props extends ViewProps {
item: BaseItemDto;
}
export const SeriesActions = ({ item, ...props }: Props) => {
export const ItemActions = ({ item, ...props }: Props) => {
const router = useRouter();
const trailerLink = useMemo(() => item.RemoteTrailers?.[0]?.Url, [item]);

View File

@@ -3,7 +3,7 @@ import { Text } from "../common/Text";
import { Ratings } from "../Ratings";
import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client";
import { useMemo } from "react";
import { SeriesActions } from "./SeriesActions";
import { ItemActions } from "./SeriesActions";
interface Props {
item: BaseItemDto;
@@ -56,7 +56,7 @@ export const SeriesHeader = ({ item }: Props) => {
<Text className="">{yearString}</Text>
<View className="flex flex-row items-center justify-between">
<Ratings item={item} className="mb-2" />
<SeriesActions item={item} />
<ItemActions item={item} />
</View>
<Text className="">{item?.Overview}</Text>
</View>

View File

@@ -39,7 +39,7 @@ import React, {
useMemo,
useState,
} from "react";
import {AppState, AppStateStatus, Platform} from "react-native";
import { AppState, AppStateStatus, Platform } from "react-native";
import { toast } from "sonner-native";
import { apiAtom } from "./JellyfinProvider";
import * as Notifications from "expo-notifications";
@@ -195,7 +195,7 @@ function useDownloadProvider() {
[settings?.optimizedVersionsServerUrl, authHeader]
);
const APP_CACHE_DOWNLOAD_DIRECTORY = `${FileSystem.cacheDirectory}${Application.applicationId}/Downloads/`
const APP_CACHE_DOWNLOAD_DIRECTORY = `${FileSystem.cacheDirectory}${Application.applicationId}/Downloads/`;
const startDownload = useCallback(
async (process: JobStatus) => {
@@ -423,32 +423,25 @@ function useDownloadProvider() {
throw new Error("Base directory not found");
}
console.log(`ignoreList length: ${ignoreList?.length}`);
const dirContents = await FileSystem.readDirectoryAsync(baseDirectory);
for (const item of dirContents) {
// Exclude mmkv directory.
// Deleting this deletes all user information as well. Logout should handle this.
if (
(item == "mmkv" && !includeMMKV) ||
ignoreList.some(i => item.includes(i))
ignoreList.some((i) => item.includes(i))
) {
console.log("Skipping read for item", item)
continue;
}
await FileSystem.getInfoAsync(`${baseDirectory}${item}`)
.then((itemInfo) => {
console.log("Loading itemInfo", itemInfo);
if (itemInfo.exists && !itemInfo.isDirectory) {
callback(itemInfo);
}
})
.catch(e =>
console.error(e)
)
.catch((e) => console.error(e));
}
}
};
const deleteLocalFiles = async (): Promise<void> => {
await forEveryDocumentDirFile(false, [], (file) => {
@@ -545,28 +538,36 @@ function useDownloadProvider() {
};
const cleanCacheDirectory = async () => {
const cacheDir = await FileSystem.getInfoAsync(APP_CACHE_DOWNLOAD_DIRECTORY);
const cacheDir = await FileSystem.getInfoAsync(
APP_CACHE_DOWNLOAD_DIRECTORY
);
if (cacheDir.exists) {
const cachedFiles = await FileSystem.readDirectoryAsync(APP_CACHE_DOWNLOAD_DIRECTORY)
let position = 0
const batchSize = 3
const cachedFiles = await FileSystem.readDirectoryAsync(
APP_CACHE_DOWNLOAD_DIRECTORY
);
let position = 0;
const batchSize = 3;
// batching promise.all to avoid OOM
while (position < cachedFiles.length) {
const itemsForBatch = cachedFiles.slice(position, position + batchSize)
await Promise.all(itemsForBatch.map(async file => {
const info = await FileSystem.getInfoAsync(`${APP_CACHE_DOWNLOAD_DIRECTORY}${file}`)
if (info.exists) {
await FileSystem.deleteAsync(info.uri, { idempotent: true })
return Promise.resolve(file)
}
return Promise.reject()
}))
const itemsForBatch = cachedFiles.slice(position, position + batchSize);
await Promise.all(
itemsForBatch.map(async (file) => {
const info = await FileSystem.getInfoAsync(
`${APP_CACHE_DOWNLOAD_DIRECTORY}${file}`
);
if (info.exists) {
await FileSystem.deleteAsync(info.uri, { idempotent: true });
return Promise.resolve(file);
}
return Promise.reject();
})
);
position += batchSize
position += batchSize;
}
}
}
};
const deleteFileByType = async (type: BaseItemDto["Type"]) => {
await Promise.all(
@@ -583,20 +584,22 @@ function useDownloadProvider() {
};
const appSizeUsage = useMemo(async () => {
const sizes: number[] = downloadedFiles?.map(d => {
return getDownloadedItemSize(d.item.Id!!)
}) || [];
const sizes: number[] =
downloadedFiles?.map((d) => {
return getDownloadedItemSize(d.item.Id!!);
}) || [];
await forEveryDocumentDirFile(
true,
getAllDownloadedItems().map(d => d.item.Id!!),
getAllDownloadedItems().map((d) => d.item.Id!!),
(file) => {
if (file.exists) {
sizes.push(file.size);
}
}).catch(e => {
console.error(e)
})
}
).catch((e) => {
console.error(e);
});
return sizes.reduce((sum, size) => sum + size, 0);
}, [logs, downloadedFiles, forEveryDocumentDirFile]);
@@ -690,7 +693,7 @@ function useDownloadProvider() {
appSizeUsage,
getDownloadedItemSize,
APP_CACHE_DOWNLOAD_DIRECTORY,
cleanCacheDirectory
cleanCacheDirectory,
};
}