From d70b45d1fd6302ec83fe175ea48d44ef32bdec9c Mon Sep 17 00:00:00 2001 From: Zoe Roux Date: Sun, 20 Jul 2025 14:54:29 +0200 Subject: [PATCH] Add external subtitles handling --- front/app.config.ts | 1 + front/src/models/video-info.ts | 1 + front/src/ui/player/index.tsx | 38 +++++++++++++++++++++++++--------- 3 files changed, 30 insertions(+), 10 deletions(-) diff --git a/front/app.config.ts b/front/app.config.ts index 1fe88cd5..894babbf 100644 --- a/front/app.config.ts +++ b/front/app.config.ts @@ -67,6 +67,7 @@ export const expo: ExpoConfig = { "react-native-video", { enableNotificationControls: true, + enableAndroidPictureInPicture: true, }, ], ], diff --git a/front/src/models/video-info.ts b/front/src/models/video-info.ts index 377f144a..f6970e90 100644 --- a/front/src/models/video-info.ts +++ b/front/src/models/video-info.ts @@ -46,6 +46,7 @@ export const Subtitle = z.object({ title: z.string().nullable(), language: z.string().nullable(), codec: z.string(), + mimeCodec: z.string().nullable(), extension: z.string(), isDefault: z.boolean(), isForced: z.boolean(), diff --git a/front/src/ui/player/index.tsx b/front/src/ui/player/index.tsx index 176983b6..c2b2e0ae 100644 --- a/front/src/ui/player/index.tsx +++ b/front/src/ui/player/index.tsx @@ -1,6 +1,7 @@ import { Stack } from "expo-router"; +import { useEffect, useRef } from "react"; import { StyleSheet, View } from "react-native"; -import { useVideoPlayer, VideoView } from "react-native-video"; +import { useVideoPlayer, VideoView, VideoViewRef } from "react-native-video"; import { entryDisplayNumber } from "~/components/entries"; import { FullVideo, VideoInfo } from "~/models"; import { Head } from "~/primitives"; @@ -34,19 +35,35 @@ const mapMetadata = (item: FullVideo | undefined) => { export const Player = () => { const [slug] = useQueryState("slug", undefined!); - const { apiUrl, authToken } = useToken(); - const [playMode] = useLocalSetting<"direct" | "hls">("playMode", "direct"); - const player = useVideoPlayer({ - uri: `${apiUrl}/api/videos/${slug}/${playMode === "direct" ? "direct" : "master.m3u8"}`, - headers: { - Authorization: `Bearer ${authToken}`, - }, - }); - const { data, error } = useFetch(Player.query(slug)); const { data: info, error: infoError } = useFetch(Player.infoQuery(slug)); const metadata = mapMetadata(data); + const { apiUrl, authToken } = useToken(); + const [playMode] = useLocalSetting<"direct" | "hls">("playMode", "direct"); + const player = useVideoPlayer( + { + uri: `${apiUrl}/api/videos/${slug}/${playMode === "direct" ? "direct" : "master.m3u8"}`, + headers: { + Authorization: `Bearer ${authToken}`, + }, + externalSubtitles: info?.subtitles + .filter((x) => x.link) + .map((x) => ({ + uri: x.link!, + // TODO: translate this `Unknown` + label: x.title ?? "Unknown", + language: x.language ?? "und", + type: x.codec, + })), + }, + (p) => { + p.playWhenInactive = true; + p.playInBackground = true; + p.play(); + }, + ); + // const [playbackError, setPlaybackError] = useState( // undefined, // ); @@ -92,6 +109,7 @@ export const Player = () => { navigationBarHidden: true, statusBarHidden: true, orientation: "landscape", + contentStyle: { paddingLeft: 0, paddingRight: 0 }, }} />