This commit is contained in:
Fredrik Burmester
2024-09-15 18:39:20 +02:00
parent ce2e5e0fb8
commit e3c4a291f0
9 changed files with 289 additions and 147 deletions

View File

@@ -0,0 +1,82 @@
import { Api } from "@jellyfin/sdk";
import { getItemsApi } from "@jellyfin/sdk/lib/utils/api/items-api";
import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client";
import { useQuery } from "@tanstack/react-query";
import { CurrentlyPlayingState } from "@/providers/PlaybackProvider";
interface AdjacentEpisodesProps {
api: Api | null;
currentlyPlaying?: CurrentlyPlayingState | null;
}
export const useAdjacentEpisodes = ({
api,
currentlyPlaying,
}: AdjacentEpisodesProps) => {
const { data: previousItem } = useQuery({
queryKey: [
"previousItem",
currentlyPlaying?.item.ParentId,
currentlyPlaying?.item.IndexNumber,
],
queryFn: async (): Promise<BaseItemDto | null> => {
if (
!api ||
!currentlyPlaying?.item.ParentId ||
currentlyPlaying?.item.IndexNumber === undefined ||
currentlyPlaying?.item.IndexNumber === null ||
currentlyPlaying.item.IndexNumber - 2 < 0
) {
console.log("No previous item");
return null;
}
const res = await getItemsApi(api).getItems({
parentId: currentlyPlaying.item.ParentId!,
startIndex: currentlyPlaying.item.IndexNumber! - 2,
limit: 1,
});
console.log(
"Prev: ",
res.data.Items?.map((i) => i.Name)
);
return res.data.Items?.[0] || null;
},
enabled: currentlyPlaying?.item.Type === "Episode",
});
const { data: nextItem } = useQuery({
queryKey: [
"nextItem",
currentlyPlaying?.item.ParentId,
currentlyPlaying?.item.IndexNumber,
],
queryFn: async (): Promise<BaseItemDto | null> => {
if (
!api ||
!currentlyPlaying?.item.ParentId ||
currentlyPlaying?.item.IndexNumber === undefined ||
currentlyPlaying?.item.IndexNumber === null
) {
console.log("No next item");
return null;
}
const res = await getItemsApi(api).getItems({
parentId: currentlyPlaying.item.ParentId!,
startIndex: currentlyPlaying.item.IndexNumber!,
limit: 1,
});
console.log(
"Next: ",
res.data.Items?.map((i) => i.Name)
);
return res.data.Items?.[0] || null;
},
enabled: currentlyPlaying?.item.Type === "Episode",
});
return { previousItem, nextItem };
};

View File

@@ -0,0 +1,27 @@
// hooks/useNavigationBarVisibility.ts
import { useEffect } from "react";
import { Platform } from "react-native";
import * as NavigationBar from "expo-navigation-bar";
export const useNavigationBarVisibility = (isPlaying: boolean | null) => {
useEffect(() => {
const handleVisibility = async () => {
if (Platform.OS === "android") {
if (isPlaying) {
await NavigationBar.setVisibilityAsync("hidden");
} else {
await NavigationBar.setVisibilityAsync("visible");
}
}
};
handleVisibility();
return () => {
if (Platform.OS === "android") {
NavigationBar.setVisibilityAsync("visible");
}
};
}, [isPlaying]);
};

80
hooks/useTrickplay.ts Normal file
View File

@@ -0,0 +1,80 @@
// hooks/useTrickplay.ts
import { useState, useCallback } from "react";
import { Api } from "@jellyfin/sdk";
import { SharedValue } from "react-native-reanimated";
interface TrickplayInfo {
data: {
Interval?: number;
TileWidth?: number;
TileHeight?: number;
Height?: number;
Width?: number;
ThumbnailCount?: number;
};
resolution?: string;
}
interface TrickplayUrl {
x: number;
y: number;
url: string;
}
export const useTrickplay = () => {
const [trickPlayUrl, setTrickPlayUrl] = useState<TrickplayUrl | null>(null);
const calculateTrickplayUrl = useCallback(
(
info: TrickplayInfo | null,
progress: SharedValue<number>,
api: Api | null,
id: string
) => {
if (!info || !id || !api) {
return null;
}
const { data, resolution } = info;
const { Interval, TileWidth, TileHeight, Height, Width, ThumbnailCount } =
data;
if (
!Interval ||
!TileWidth ||
!TileHeight ||
!Height ||
!Width ||
!ThumbnailCount ||
!resolution
) {
throw new Error("Invalid trickplay data");
}
const currentSecond = Math.max(0, Math.floor(progress.value / 10000000));
const cols = TileWidth;
const rows = TileHeight;
const imagesPerTile = cols * rows;
const imageIndex = Math.floor(currentSecond / (Interval / 1000));
const tileIndex = Math.floor(imageIndex / imagesPerTile);
const positionInTile = imageIndex % imagesPerTile;
const rowInTile = Math.floor(positionInTile / cols);
const colInTile = positionInTile % cols;
const newTrickPlayUrl = {
x: rowInTile,
y: colInTile,
url: `${api.basePath}/Videos/${id}/Trickplay/${resolution}/${tileIndex}.jpg?api_key=${api.accessToken}`,
};
setTrickPlayUrl(newTrickPlayUrl);
return newTrickPlayUrl;
},
[]
);
return { trickPlayUrl, calculateTrickplayUrl };
};