This commit is contained in:
Fredrik Burmester
2024-10-21 16:05:36 +02:00
parent ba76f2444d
commit 68d32bd0de
3 changed files with 64 additions and 50 deletions

View File

@@ -29,7 +29,13 @@ import { useFocusEffect, useLocalSearchParams } from "expo-router";
import { useAtomValue } from "jotai"; import { useAtomValue } from "jotai";
import { debounce } from "lodash"; import { debounce } from "lodash";
import React, { useCallback, useMemo, useRef, useState } from "react"; import React, { useCallback, useMemo, useRef, useState } from "react";
import { Dimensions, Pressable, StatusBar, View } from "react-native"; import {
Dimensions,
Pressable,
StatusBar,
useWindowDimensions,
View,
} from "react-native";
import { useSharedValue } from "react-native-reanimated"; import { useSharedValue } from "react-native-reanimated";
import Video, { OnProgressData, VideoRef } from "react-native-video"; import Video, { OnProgressData, VideoRef } from "react-native-video";
@@ -38,11 +44,10 @@ export default function page() {
const user = useAtomValue(userAtom); const user = useAtomValue(userAtom);
const [settings] = useSettings(); const [settings] = useSettings();
const videoRef = useRef<VideoRef | null>(null); const videoRef = useRef<VideoRef | null>(null);
const windowDimensions = useWindowDimensions();
const firstTime = useRef(true); const firstTime = useRef(true);
const screenDimensions = Dimensions.get("screen");
const [isPlaybackStopped, setIsPlaybackStopped] = useState(false); const [isPlaybackStopped, setIsPlaybackStopped] = useState(false);
const [showControls, setShowControls] = useState(true); const [showControls, setShowControls] = useState(true);
const [ignoreSafeAreas, setIgnoreSafeAreas] = useState(false); const [ignoreSafeAreas, setIgnoreSafeAreas] = useState(false);
@@ -267,7 +272,7 @@ export default function page() {
}, [play, stop]) }, [play, stop])
); );
const { orientation } = useOrientation(); useOrientation();
useOrientationSettings(); useOrientationSettings();
useAndroidNavigationBar(); useAndroidNavigationBar();
@@ -292,13 +297,18 @@ export default function page() {
</View> </View>
); );
if (!stream || !item || !videoSource) return null; if (!item || !stream)
return (
<View className="w-screen h-screen flex flex-col items-center justify-center bg-black">
<Text className="text-white">Error</Text>
</View>
);
return ( return (
<View <View
style={{ style={{
width: screenDimensions.width, width: windowDimensions.width,
height: screenDimensions.height, height: windowDimensions.height,
position: "relative", position: "relative",
}} }}
className="flex flex-col items-center justify-center" className="flex flex-col items-center justify-center"
@@ -318,31 +328,33 @@ export default function page() {
}} }}
className="absolute z-0 h-full w-full opacity-0" className="absolute z-0 h-full w-full opacity-0"
> >
<Video {videoSource && (
ref={videoRef} <Video
source={videoSource} ref={videoRef}
style={{ width: "100%", height: "100%" }} source={videoSource}
resizeMode={ignoreSafeAreas ? "cover" : "contain"} style={{ width: "100%", height: "100%" }}
onProgress={onProgress} resizeMode={ignoreSafeAreas ? "cover" : "contain"}
onError={() => {}} onProgress={onProgress}
onLoad={() => { onError={() => {}}
if (firstTime.current === true) { onLoad={() => {
play(); if (firstTime.current === true) {
firstTime.current = false; play();
} firstTime.current = false;
}} }
progressUpdateInterval={500} }}
playWhenInactive={true} progressUpdateInterval={500}
allowsExternalPlayback={true} playWhenInactive={true}
playInBackground={true} allowsExternalPlayback={true}
pictureInPicture={true} playInBackground={true}
showNotificationControls={true} pictureInPicture={true}
ignoreSilentSwitch="ignore" showNotificationControls={true}
fullscreen={false} ignoreSilentSwitch="ignore"
onPlaybackStateChanged={(state) => { fullscreen={false}
setIsPlaying(state.isPlaying); onPlaybackStateChanged={(state) => {
}} setIsPlaying(state.isPlaying);
/> }}
/>
)}
</Pressable> </Pressable>
<Controls <Controls

View File

@@ -16,7 +16,8 @@ import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
import { getBackdropUrl } from "@/utils/jellyfin/image/getBackdropUrl"; import { getBackdropUrl } from "@/utils/jellyfin/image/getBackdropUrl";
import { getStreamUrl } from "@/utils/jellyfin/media/getStreamUrl"; import { getStreamUrl } from "@/utils/jellyfin/media/getStreamUrl";
import { writeToLog } from "@/utils/log"; import { writeToLog } from "@/utils/log";
import { msToTicks, ticksToMs } from "@/utils/time"; import native from "@/utils/profiles/native";
import { msToTicks } from "@/utils/time";
import { Api } from "@jellyfin/sdk"; import { Api } from "@jellyfin/sdk";
import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client"; import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client";
import { import {
@@ -25,7 +26,7 @@ import {
} from "@jellyfin/sdk/lib/utils/api"; } from "@jellyfin/sdk/lib/utils/api";
import { useQuery } from "@tanstack/react-query"; import { useQuery } from "@tanstack/react-query";
import * as Haptics from "expo-haptics"; import * as Haptics from "expo-haptics";
import { useFocusEffect, useLocalSearchParams } from "expo-router"; import { useLocalSearchParams } from "expo-router";
import { useAtomValue } from "jotai"; import { useAtomValue } from "jotai";
import React, { useCallback, useMemo, useRef, useState } from "react"; import React, { useCallback, useMemo, useRef, useState } from "react";
import { import {
@@ -118,6 +119,7 @@ export default function page() {
maxStreamingBitrate: bitrateValue, maxStreamingBitrate: bitrateValue,
mediaSourceId: mediaSourceId, mediaSourceId: mediaSourceId,
subtitleStreamIndex: subtitleIndex, subtitleStreamIndex: subtitleIndex,
deviceProfile: native,
}); });
if (!res) return null; if (!res) return null;
@@ -230,8 +232,6 @@ export default function page() {
const { currentTime, isPlaying } = data.nativeEvent; const { currentTime, isPlaying } = data.nativeEvent;
console.log("onProgress", currentTime);
progress.value = currentTime; progress.value = currentTime;
const currentTimeInTicks = msToTicks(currentTime); const currentTimeInTicks = msToTicks(currentTime);
@@ -249,15 +249,15 @@ export default function page() {
[item?.Id, isPlaying, api, isPlaybackStopped] [item?.Id, isPlaying, api, isPlaybackStopped]
); );
useFocusEffect( // useFocusEffect(
useCallback(() => { // useCallback(() => {
play(); // play();
return () => { // return () => {
stop(); // stop();
}; // };
}, [play, stop]) // }, [play, stop])
); // );
useOrientation(); useOrientation();
useOrientationSettings(); useOrientationSettings();

View File

@@ -90,16 +90,10 @@ export const getStreamUrl = async ({
userId, userId,
maxStreamingBitrate, maxStreamingBitrate,
startTimeTicks, startTimeTicks,
enableTranscoding: maxStreamingBitrate ? true : undefined,
autoOpenLiveStream: true, autoOpenLiveStream: true,
mediaSourceId, mediaSourceId,
allowVideoStreamCopy: maxStreamingBitrate ? false : true,
audioStreamIndex, audioStreamIndex,
subtitleStreamIndex, subtitleStreamIndex,
deInterlace: true,
breakOnNonKeyFrames: false,
copyTimestamps: false,
enableMpegtsM2TsMode: false,
}, },
} }
); );
@@ -112,6 +106,10 @@ export const getStreamUrl = async ({
if (item.MediaType === "Video") { if (item.MediaType === "Video") {
if (mediaSource?.TranscodingUrl) { if (mediaSource?.TranscodingUrl) {
console.log(
"Video has transcoding URL:",
`${api.basePath}${mediaSource.TranscodingUrl}`
);
return { return {
url: `${api.basePath}${mediaSource.TranscodingUrl}`, url: `${api.basePath}${mediaSource.TranscodingUrl}`,
sessionId: sessionId, sessionId: sessionId,
@@ -120,6 +118,10 @@ export const getStreamUrl = async ({
} }
if (mediaSource?.SupportsDirectPlay) { if (mediaSource?.SupportsDirectPlay) {
console.log(
"Video is being direct played:",
`${api.basePath}/Videos/${itemId}/stream.mp4?playSessionId=${sessionData?.PlaySessionId}&mediaSourceId=${mediaSource?.Id}&static=true&subtitleStreamIndex=${subtitleStreamIndex}&audioStreamIndex=${audioStreamIndex}&deviceId=${api.deviceInfo.id}&api_key=${api.accessToken}`
);
return { return {
url: `${api.basePath}/Videos/${itemId}/stream.mp4?playSessionId=${sessionData?.PlaySessionId}&mediaSourceId=${mediaSource?.Id}&static=true&subtitleStreamIndex=${subtitleStreamIndex}&audioStreamIndex=${audioStreamIndex}&deviceId=${api.deviceInfo.id}&api_key=${api.accessToken}`, url: `${api.basePath}/Videos/${itemId}/stream.mp4?playSessionId=${sessionData?.PlaySessionId}&mediaSourceId=${mediaSource?.Id}&static=true&subtitleStreamIndex=${subtitleStreamIndex}&audioStreamIndex=${audioStreamIndex}&deviceId=${api.deviceInfo.id}&api_key=${api.accessToken}`,
sessionId: sessionId, sessionId: sessionId,