diff --git a/app/(auth)/items/[id]/page.tsx b/app/(auth)/items/[id]/page.tsx
index 05f5e430..6beb0711 100644
--- a/app/(auth)/items/[id]/page.tsx
+++ b/app/(auth)/items/[id]/page.tsx
@@ -11,7 +11,7 @@ import { useQuery } from "@tanstack/react-query";
import { Image } from "expo-image";
import { router, useLocalSearchParams } from "expo-router";
import { useAtom } from "jotai";
-import { useCallback, useMemo, useState } from "react";
+import { useCallback, useEffect, useMemo, useState } from "react";
import {
ActivityIndicator,
ScrollView,
@@ -36,6 +36,10 @@ import ios12 from "@/utils/profiles/ios12";
import { currentlyPlayingItemAtom } from "@/components/CurrentlyPlayingBar";
import { AudioTrackSelector } from "@/components/AudioTrackSelector";
import { SubtitleTrackSelector } from "@/components/SubtitleTrackSelector";
+import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
+import { Button } from "@/components/Button";
+import { Ionicons } from "@expo/vector-icons";
+import { NextEpisodeButton } from "@/components/series/NextEpisodeButton";
const page: React.FC = () => {
const local = useLocalSearchParams();
@@ -201,7 +205,7 @@ const page: React.FC = () => {
>
}
>
-
+
{item.Type === "Episode" ? (
<>
@@ -218,7 +222,6 @@ const page: React.FC = () => {
{item?.Name}
-
@@ -243,7 +246,6 @@ const page: React.FC = () => {
{item?.Name}
-
{item?.ProductionYear}
@@ -253,14 +255,17 @@ const page: React.FC = () => {
- {playbackUrl && (
+ {playbackUrl ? (
+ ) : (
+
)}
+
{item.Overview}
-
+
setMaxBitrate(val)}
@@ -277,7 +282,16 @@ const page: React.FC = () => {
selected={selectedSubtitleStream}
/>
-
+
+
+
+
+
diff --git a/components/Button.tsx b/components/Button.tsx
index 52069f13..01b88963 100644
--- a/components/Button.tsx
+++ b/components/Button.tsx
@@ -7,7 +7,7 @@ interface ButtonProps extends React.ComponentProps {
className?: string;
textClassName?: string;
disabled?: boolean;
- children?: string;
+ children?: string | ReactNode;
loading?: boolean;
color?: "purple" | "red" | "black";
iconRight?: ReactNode;
diff --git a/components/Chromecast.tsx b/components/Chromecast.tsx
index 4286b64e..69608057 100644
--- a/components/Chromecast.tsx
+++ b/components/Chromecast.tsx
@@ -1,5 +1,6 @@
import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
import React, { useEffect } from "react";
+import { View } from "react-native";
import {
CastButton,
useCastDevice,
@@ -30,5 +31,9 @@ export const Chromecast: React.FC = () => {
})();
}, [client, devices, castDevice, sessionManager, discoveryManager]);
- return ;
+ return (
+
+
+
+ );
};
diff --git a/components/CurrentlyPlayingBar.tsx b/components/CurrentlyPlayingBar.tsx
index 6a951bff..c13c675e 100644
--- a/components/CurrentlyPlayingBar.tsx
+++ b/components/CurrentlyPlayingBar.tsx
@@ -7,23 +7,13 @@ import {
import { Text } from "./common/Text";
import { Ionicons } from "@expo/vector-icons";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
-import { useSafeAreaInsets } from "react-native-safe-area-context";
-import Video, {
- OnProgressData,
- SelectedTrack,
- SelectedTrackType,
- VideoRef,
-} from "react-native-video";
+import Video, { OnProgressData, VideoRef } from "react-native-video";
import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
import { atom, useAtom } from "jotai";
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
import { useQuery, useQueryClient } from "@tanstack/react-query";
-import { useCastDevice, useRemoteMediaClient } from "react-native-google-cast";
import { getUserItemData } from "@/utils/jellyfin/user-library/getUserItemData";
import { getMediaInfoApi } from "@jellyfin/sdk/lib/utils/api";
-import { getStreamUrl } from "@/utils/jellyfin/media/getStreamUrl";
-import { chromecastProfile } from "@/utils/profiles/chromecast";
-import ios12 from "@/utils/profiles/ios12";
import { reportPlaybackProgress } from "@/utils/jellyfin/playstate/reportPlaybackProgress";
import { reportPlaybackStopped } from "@/utils/jellyfin/playstate/reportPlaybackStopped";
import Animated, {
@@ -35,7 +25,6 @@ import { useRouter, useSegments } from "expo-router";
import { BlurView } from "expo-blur";
import { writeToLog } from "@/utils/log";
import { getBackdropUrl } from "@/utils/jellyfin/image/getBackdropUrl";
-import { Image } from "expo-image";
export const currentlyPlayingItemAtom = atom<{
item: BaseItemDto;
@@ -43,13 +32,10 @@ export const currentlyPlayingItemAtom = atom<{
} | null>(null);
export const CurrentlyPlayingBar: React.FC = () => {
- const insets = useSafeAreaInsets();
const [api] = useAtom(apiAtom);
const [user] = useAtom(userAtom);
const [cp, setCp] = useAtom(currentlyPlayingItemAtom);
- const castDevice = useCastDevice();
- const client = useRemoteMediaClient();
const queryClient = useQueryClient();
const segments = useSegments();
@@ -57,8 +43,6 @@ export const CurrentlyPlayingBar: React.FC = () => {
const [paused, setPaused] = useState(true);
const [progress, setProgress] = useState(0);
- const [pip, setPip] = useState(false);
-
const aBottom = useSharedValue(0);
const aPadding = useSharedValue(0);
const aHeight = useSharedValue(100);
@@ -239,9 +223,6 @@ export const CurrentlyPlayingBar: React.FC = () => {
ignoreSilentSwitch="ignore"
controls={false}
pictureInPicture={true}
- onPictureInPictureStatusChanged={(e) => {
- setPip(e.isActive);
- }}
poster={
backdropUrl && item?.Type === "Audio"
? backdropUrl
diff --git a/components/DownloadItem.tsx b/components/DownloadItem.tsx
index bf8d6a6b..309e2a01 100644
--- a/components/DownloadItem.tsx
+++ b/components/DownloadItem.tsx
@@ -64,12 +64,16 @@ export const DownloadItem: React.FC = ({
}, [process]);
if (isLoading) {
- return ;
+ return (
+
+
+
+ );
}
if (playbackInfo?.MediaSources?.[0].SupportsDirectPlay === false) {
return (
-
+
);
@@ -77,21 +81,22 @@ export const DownloadItem: React.FC = ({
if (process && process.item.Id !== item.Id!) {
return (
- {}} style={{ opacity: 0.5 }}>
-
+ {}}>
+
+
+
);
}
- return (
-
- {process ? (
- {
- router.push("/downloads");
- }}
- className="flex flex-row items-center"
- >
+ if (process) {
+ return (
+ {
+ router.push("/downloads");
+ }}
+ >
+
{process.progress === 0 ? (
) : (
@@ -118,24 +123,32 @@ export const DownloadItem: React.FC = ({
{process.speed.toFixed(2)}x
) : null}
-
- ) : downloaded ? (
- {
- router.push("/downloads");
- }}
- >
+
+
+ );
+ } else if (downloaded) {
+ return (
+ {
+ router.push("/downloads");
+ }}
+ >
+
-
- ) : (
- {
- startRemuxing();
- }}
- >
+
+
+ );
+ } else {
+ return (
+ {
+ startRemuxing();
+ }}
+ >
+
-
- )}
-
- );
+
+
+ );
+ }
};
diff --git a/components/NewVideoPlayer.tsx b/components/NewVideoPlayer.tsx
new file mode 100644
index 00000000..13f002a4
--- /dev/null
+++ b/components/NewVideoPlayer.tsx
@@ -0,0 +1,56 @@
+import { useVideoPlayer, VideoView } from "expo-video";
+import { useEffect, useRef, useState } from "react";
+import {
+ PixelRatio,
+ StyleSheet,
+ View,
+ Button,
+ TouchableOpacity,
+} from "react-native";
+
+const videoSource =
+ "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4";
+
+interface Props {
+ videoSource: string;
+}
+
+export const NewVideoPlayer: React.FC = ({ videoSource }) => {
+ const ref = useRef(null);
+ const [isPlaying, setIsPlaying] = useState(true);
+ const player = useVideoPlayer(videoSource, (player) => {
+ player.loop = true;
+ player.play();
+ });
+
+ useEffect(() => {
+ const subscription = player.addListener("playingChange", (isPlaying) => {
+ setIsPlaying(isPlaying);
+ });
+
+ return () => {
+ subscription.remove();
+ };
+ }, [player]);
+
+ return (
+ {
+ ref.current?.enterFullscreen();
+ }}
+ className={`relative h-full bg-neutral-800 rounded-md overflow-hidden
+ `}
+ >
+
+
+ );
+};
diff --git a/components/VideoPlayer.tsx b/components/OldVideoPlayer.tsx
similarity index 99%
rename from components/VideoPlayer.tsx
rename to components/OldVideoPlayer.tsx
index a86e1814..905a9ae9 100644
--- a/components/VideoPlayer.tsx
+++ b/components/OldVideoPlayer.tsx
@@ -30,7 +30,7 @@ type VideoPlayerProps = {
onChangePlaybackURL: (url: string | null) => void;
};
-export const VideoPlayer: React.FC = ({
+export const OldVideoPlayer: React.FC = ({
itemId,
onChangePlaybackURL,
}) => {
diff --git a/components/ParallaxPage.tsx b/components/ParallaxPage.tsx
index 2260a804..90f8bff6 100644
--- a/components/ParallaxPage.tsx
+++ b/components/ParallaxPage.tsx
@@ -89,7 +89,9 @@ export const ParallaxScrollView: React.FC = ({
>
{headerImage}
- {children}
+
+ {children}
+
);
diff --git a/components/PlayButton.tsx b/components/PlayButton.tsx
index 850aeef0..2ebfe94e 100644
--- a/components/PlayButton.tsx
+++ b/components/PlayButton.tsx
@@ -1,21 +1,19 @@
-import { useState } from "react";
import { Button } from "./Button";
import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
-import { currentlyPlayingItemAtom } from "./CurrentlyPlayingBar";
-import { useAtom } from "jotai";
import { Feather, Ionicons } from "@expo/vector-icons";
import { runtimeTicksToMinutes } from "@/utils/time";
-type Props = {
+interface Props extends React.ComponentProps {
item: BaseItemDto;
onPress: () => void;
chromecastReady: boolean;
-};
+}
export const PlayButton: React.FC = ({
item,
onPress,
chromecastReady,
+ ...props
}) => {
return (
diff --git a/components/PlayedStatus.tsx b/components/PlayedStatus.tsx
index 9901f5a1..fa706625 100644
--- a/components/PlayedStatus.tsx
+++ b/components/PlayedStatus.tsx
@@ -47,7 +47,9 @@ export const PlayedStatus: React.FC<{ item: BaseItemDto }> = ({ item }) => {
invalidateQueries();
}}
>
-
+
+
+
) : (
= ({ item }) => {
invalidateQueries();
}}
>
-
+
+
+
)}