mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-06-23 15:30:34 -04:00
Use react-native-video instead of expo-av and add subtitle support
This commit is contained in:
parent
e741b5aa6d
commit
84b4c998a7
62
front/apps/mobile/app.config.js
Normal file
62
front/apps/mobile/app.config.js
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
* Kyoo - A portable and vast media library solution.
|
||||||
|
* Copyright (c) Kyoo.
|
||||||
|
*
|
||||||
|
* See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||||
|
*
|
||||||
|
* Kyoo is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* Kyoo is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const IS_DEV = process.env.APP_VARIANT === "development";
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
expo: {
|
||||||
|
name: "kyoo",
|
||||||
|
slug: "kyoo",
|
||||||
|
scheme: "kyoo",
|
||||||
|
version: "1.0.0",
|
||||||
|
orientation: "default",
|
||||||
|
icon: "./assets/icon.png",
|
||||||
|
entryPoint: "./index.ts",
|
||||||
|
userInterfaceStyle: "light",
|
||||||
|
splash: {
|
||||||
|
image: "./assets/splash.png",
|
||||||
|
resizeMode: "contain",
|
||||||
|
backgroundColor: "#ffffff",
|
||||||
|
},
|
||||||
|
updates: {
|
||||||
|
fallbackToCacheTimeout: 0,
|
||||||
|
},
|
||||||
|
assetBundlePatterns: ["**/*"],
|
||||||
|
ios: {
|
||||||
|
supportsTablet: true,
|
||||||
|
},
|
||||||
|
android: {
|
||||||
|
package: IS_DEV ? "moe.sdg.kyoo.dev" : "moe.sdg.kyoo",
|
||||||
|
adaptiveIcon: {
|
||||||
|
foregroundImage: "./assets/adaptive-icon.png",
|
||||||
|
backgroundColor: "#FFFFFF",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
web: {
|
||||||
|
favicon: "./assets/favicon.png",
|
||||||
|
},
|
||||||
|
extra: {
|
||||||
|
eas: {
|
||||||
|
projectId: "55de6b52-c649-4a15-9a45-569ff5ed036c",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
export default config;
|
@ -1,49 +0,0 @@
|
|||||||
{
|
|
||||||
"expo": {
|
|
||||||
"name": "kyoo",
|
|
||||||
"slug": "kyoo",
|
|
||||||
"scheme": "kyoo",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"orientation": "default",
|
|
||||||
"icon": "./assets/icon.png",
|
|
||||||
"entryPoint": "./index.ts",
|
|
||||||
"userInterfaceStyle": "light",
|
|
||||||
"splash": {
|
|
||||||
"image": "./assets/splash.png",
|
|
||||||
"resizeMode": "contain",
|
|
||||||
"backgroundColor": "#ffffff"
|
|
||||||
},
|
|
||||||
"updates": {
|
|
||||||
"fallbackToCacheTimeout": 0
|
|
||||||
},
|
|
||||||
"assetBundlePatterns": ["**/*"],
|
|
||||||
"ios": {
|
|
||||||
"supportsTablet": true
|
|
||||||
},
|
|
||||||
"android": {
|
|
||||||
"package": "moe.sdg.kyoo",
|
|
||||||
"adaptiveIcon": {
|
|
||||||
"foregroundImage": "./assets/adaptive-icon.png",
|
|
||||||
"backgroundColor": "#FFFFFF"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"web": {
|
|
||||||
"favicon": "./assets/favicon.png"
|
|
||||||
},
|
|
||||||
"extra": {
|
|
||||||
"eas": {
|
|
||||||
"projectId": "55de6b52-c649-4a15-9a45-569ff5ed036c"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"plugins": [
|
|
||||||
[
|
|
||||||
"expo-build-properties",
|
|
||||||
{
|
|
||||||
"android": {
|
|
||||||
"compileSdkVersion": 31
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
@ -6,7 +6,9 @@
|
|||||||
"dev": "expo start",
|
"dev": "expo start",
|
||||||
"android": "expo start --android",
|
"android": "expo start --android",
|
||||||
"ios": "expo start --ios",
|
"ios": "expo start --ios",
|
||||||
"web": "expo start --web"
|
"web": "expo start --web",
|
||||||
|
"build": "eas build --profile production --platform android",
|
||||||
|
"build:dev": "eas build --profile development --platform android"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@gorhom/portal": "^1.0.14",
|
"@gorhom/portal": "^1.0.14",
|
||||||
|
@ -6,6 +6,8 @@
|
|||||||
"web": "yarn workspace web dev",
|
"web": "yarn workspace web dev",
|
||||||
"mobile": "yarn workspace mobile dev",
|
"mobile": "yarn workspace mobile dev",
|
||||||
"build:web": "yarn workspace web build",
|
"build:web": "yarn workspace web build",
|
||||||
|
"build:mobile": "yarn workspace mobile build",
|
||||||
|
"build:mobile:dev": "yarn workspace mobile build:dev",
|
||||||
"lint": "eslint ."
|
"lint": "eslint ."
|
||||||
},
|
},
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
|
@ -36,12 +36,12 @@ const queryFn = async <Data,>(
|
|||||||
context: QueryFunctionContext,
|
context: QueryFunctionContext,
|
||||||
): Promise<Data> => {
|
): Promise<Data> => {
|
||||||
const kyooUrl =
|
const kyooUrl =
|
||||||
Platform.OS !== "web"
|
(Platform.OS !== "web"
|
||||||
? process.env.PUBLIC_BACK_URL
|
? process.env.PUBLIC_BACK_URL
|
||||||
: typeof window === "undefined"
|
: typeof window === "undefined"
|
||||||
? process.env.KYOO_URL ?? "http://localhost:5000"
|
? process.env.KYOO_URL ?? "http://localhost:5000"
|
||||||
: "/api";
|
// TODO remove the hardcoded fallback. This is just for testing purposes
|
||||||
console.log(process.env.PUBLIC_BACK_URL)
|
: "/api") ?? "https://beta.sdg.moe";
|
||||||
if (!kyooUrl) console.error("Kyoo's url is not defined.");
|
if (!kyooUrl) console.error("Kyoo's url is not defined.");
|
||||||
|
|
||||||
let resp;
|
let resp;
|
||||||
|
@ -21,10 +21,11 @@
|
|||||||
import { Platform } from "react-native";
|
import { Platform } from "react-native";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
export const imageFn = (url: string) => {
|
export const imageFn = (url: string) =>
|
||||||
console.log(process.env.PUBLIC_BACK_URL)
|
// TODO remove the hardcodded fallback
|
||||||
return Platform.OS === "web" ? `/api/${url}` : process.env.PUBLIC_BACK_URL + url;
|
Platform.OS === "web"
|
||||||
}
|
? `/api/${url}`
|
||||||
|
: (process.env.PUBLIC_BACK_URL ?? "https://beta.sdg.moe") + url;
|
||||||
|
|
||||||
export const ImagesP = z.object({
|
export const ImagesP = z.object({
|
||||||
/**
|
/**
|
||||||
|
@ -30,7 +30,7 @@ import {
|
|||||||
ViewStyle,
|
ViewStyle,
|
||||||
} from "react-native";
|
} from "react-native";
|
||||||
import { percent, useYoshiki } from "yoshiki/native";
|
import { percent, useYoshiki } from "yoshiki/native";
|
||||||
import { StyleList, YoshikiStyle } from "yoshiki/dist/type";
|
import { YoshikiStyle } from "yoshiki/dist/type";
|
||||||
import { Skeleton } from "./skeleton";
|
import { Skeleton } from "./skeleton";
|
||||||
import { LinearGradient, LinearGradientProps } from "expo-linear-gradient";
|
import { LinearGradient, LinearGradientProps } from "expo-linear-gradient";
|
||||||
import { alpha, ContrastArea } from "./themes";
|
import { alpha, ContrastArea } from "./themes";
|
||||||
|
@ -142,7 +142,7 @@ export const ProgressBar = ({ chapters }: { chapters?: Chapter[] }) => {
|
|||||||
setProgress={setProgress}
|
setProgress={setProgress}
|
||||||
subtleProgress={buffered}
|
subtleProgress={buffered}
|
||||||
max={duration}
|
max={duration}
|
||||||
markers={chapters?.map((x) => x.startTime * 1000)}
|
markers={chapters?.map((x) => x.startTime)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -132,6 +132,6 @@ const ProgressText = () => {
|
|||||||
const toTimerString = (timer?: number, duration?: number) => {
|
const toTimerString = (timer?: number, duration?: number) => {
|
||||||
if (timer === undefined) return "??:??";
|
if (timer === undefined) return "??:??";
|
||||||
if (!duration) duration = timer;
|
if (!duration) duration = timer;
|
||||||
if (duration >= 3600_000) return new Date(timer).toISOString().substring(11, 19);
|
if (duration >= 3600) return new Date(timer * 1000).toISOString().substring(11, 19);
|
||||||
return new Date(timer).toISOString().substring(14, 19);
|
return new Date(timer * 1000).toISOString().substring(14, 19);
|
||||||
};
|
};
|
||||||
|
@ -19,8 +19,9 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Font, Track } from "@kyoo/models";
|
import { Font, Track } from "@kyoo/models";
|
||||||
import { IconButton, tooltip, Menu, ts, A } from "@kyoo/primitives";
|
import { IconButton, tooltip, Menu, ts } from "@kyoo/primitives";
|
||||||
import { useAtom } from "jotai";
|
import { useAtom, useSetAtom } from "jotai";
|
||||||
|
import { useEffect } from "react";
|
||||||
import { Platform, View } from "react-native";
|
import { Platform, View } from "react-native";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import ClosedCaption from "@material-symbols/svg-400/rounded/closed_caption-fill.svg";
|
import ClosedCaption from "@material-symbols/svg-400/rounded/closed_caption-fill.svg";
|
||||||
@ -30,7 +31,7 @@ import { Stylable, useYoshiki } from "yoshiki/native";
|
|||||||
import { createParam } from "solito";
|
import { createParam } from "solito";
|
||||||
import { fullscreenAtom, subtitleAtom } from "../state";
|
import { fullscreenAtom, subtitleAtom } from "../state";
|
||||||
|
|
||||||
const { useParam } = createParam<{ subtitle?: (string) }>();
|
const { useParam } = createParam<{ subtitle?: string }>();
|
||||||
|
|
||||||
export const RightButtons = ({
|
export const RightButtons = ({
|
||||||
subtitles,
|
subtitles,
|
||||||
@ -47,8 +48,17 @@ export const RightButtons = ({
|
|||||||
const { css } = useYoshiki();
|
const { css } = useYoshiki();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [isFullscreen, setFullscreen] = useAtom(fullscreenAtom);
|
const [isFullscreen, setFullscreen] = useAtom(fullscreenAtom);
|
||||||
|
const setSubAtom = useSetAtom(subtitleAtom);
|
||||||
const [selectedSubtitle, setSubtitle] = useParam("subtitle");
|
const [selectedSubtitle, setSubtitle] = useParam("subtitle");
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const sub =
|
||||||
|
subtitles?.find(
|
||||||
|
(x) => x.language === selectedSubtitle || x.id.toString() === selectedSubtitle,
|
||||||
|
) ?? null;
|
||||||
|
setSubAtom(sub);
|
||||||
|
}, [subtitles, selectedSubtitle, setSubAtom]);
|
||||||
|
|
||||||
const spacing = css({ marginHorizontal: ts(1) });
|
const spacing = css({ marginHorizontal: ts(1) });
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -88,4 +98,3 @@ export const RightButtons = ({
|
|||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -187,7 +187,6 @@ export const Player: QueryPage<{ slug: string }> = ({ slug }) => {
|
|||||||
>
|
>
|
||||||
<Video
|
<Video
|
||||||
links={data?.link}
|
links={data?.link}
|
||||||
videoStyle={{ width: percent(100), height: percent(100) }}
|
|
||||||
setError={setPlaybackError}
|
setError={setPlaybackError}
|
||||||
{...css(StyleSheet.absoluteFillObject)}
|
{...css(StyleSheet.absoluteFillObject)}
|
||||||
// onEnded={() => {
|
// onEnded={() => {
|
||||||
|
@ -71,6 +71,8 @@ export const [privateFullscreen, fullscreenAtom] = bakedAtom(
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const subtitleAtom = atom<Track | null>(null);
|
||||||
|
|
||||||
let hls: Hls | null = null;
|
let hls: Hls | null = null;
|
||||||
|
|
||||||
export const Video = ({
|
export const Video = ({
|
||||||
@ -78,8 +80,6 @@ export const Video = ({
|
|||||||
setError,
|
setError,
|
||||||
...props
|
...props
|
||||||
}: { links?: WatchItem["link"]; setError: (error: string | undefined) => void } & VideoProps) => {
|
}: { links?: WatchItem["link"]; setError: (error: string | undefined) => void } & VideoProps) => {
|
||||||
// const [playMode, setPlayMode] = useAtom(playModeAtom);
|
|
||||||
|
|
||||||
const ref = useRef<NativeVideo | null>(null);
|
const ref = useRef<NativeVideo | null>(null);
|
||||||
const isPlaying = useAtomValue(playAtom);
|
const isPlaying = useAtomValue(playAtom);
|
||||||
const setLoad = useSetAtom(loadAtom);
|
const setLoad = useSetAtom(loadAtom);
|
||||||
@ -92,9 +92,9 @@ export const Video = ({
|
|||||||
const setPrivateProgress = useSetAtom(privateProgressAtom);
|
const setPrivateProgress = useSetAtom(privateProgressAtom);
|
||||||
const setBuffered = useSetAtom(bufferedAtom);
|
const setBuffered = useSetAtom(bufferedAtom);
|
||||||
const setDuration = useSetAtom(durationAtom);
|
const setDuration = useSetAtom(durationAtom);
|
||||||
// useEffect(() => {
|
useEffect(() => {
|
||||||
// ref.current?.setStatusAsync({ positionMillis: publicProgress });
|
ref.current?.seek(publicProgress);
|
||||||
// }, [publicProgress]);
|
}, [publicProgress]);
|
||||||
|
|
||||||
const volume = useAtomValue(volumeAtom);
|
const volume = useAtomValue(volumeAtom);
|
||||||
const isMuted = useAtomValue(mutedAtom);
|
const isMuted = useAtomValue(mutedAtom);
|
||||||
@ -109,6 +109,8 @@ export const Video = ({
|
|||||||
return () => document.removeEventListener("fullscreenchange", handler);
|
return () => document.removeEventListener("fullscreenchange", handler);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const subtitle = useAtomValue(subtitleAtom);
|
||||||
|
|
||||||
// useEffect(() => {
|
// useEffect(() => {
|
||||||
// setPlayMode(PlayMode.Direct);
|
// setPlayMode(PlayMode.Direct);
|
||||||
// }, [links, setPlayMode]);
|
// }, [links, setPlayMode]);
|
||||||
@ -134,139 +136,117 @@ export const Video = ({
|
|||||||
// }
|
// }
|
||||||
// }, [playMode, links, player]);
|
// }, [playMode, links, player]);
|
||||||
|
|
||||||
// useEffect(() => {
|
if (!links) return null;
|
||||||
// if (!player?.current?.duration) return;
|
|
||||||
// setDuration(player.current.duration);
|
|
||||||
// }, [player, setDuration]);
|
|
||||||
//
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NativeVideo
|
<NativeVideo
|
||||||
ref={ref}
|
ref={ref}
|
||||||
{...props}
|
{...props}
|
||||||
source={{ uri: links?.direct }}
|
source={{ uri: links.direct }}
|
||||||
paused={!isPlaying}
|
paused={!isPlaying}
|
||||||
muted={isMuted}
|
muted={isMuted}
|
||||||
volume={volume}
|
volume={volume}
|
||||||
// resizeMode={ResizeMode.CONTAIN}
|
resizeMode="contain"
|
||||||
// onPlaybackStatusUpdate={(status) => {
|
onBuffer={({ isBuffering }) => setLoad(isBuffering)}
|
||||||
// if (!status.isLoaded) {
|
onError={(status) => setError(status.error.errorString)}
|
||||||
// setLoad(true);
|
onProgress={(progress) => {
|
||||||
// if (status.error) setError(status.error);
|
setPrivateProgress(progress.currentTime);
|
||||||
// return;
|
setBuffered(progress.playableDuration);
|
||||||
// }
|
}}
|
||||||
|
onLoad={(info) => {
|
||||||
// setLoad(status.isPlaying !== status.shouldPlay);
|
console.log(info);
|
||||||
// setPrivateProgress(status.positionMillis);
|
setDuration(info.duration);
|
||||||
// setBuffered(status.playableDurationMillis ?? 0);
|
}}
|
||||||
// setDuration(status.durationMillis);
|
selectedTextTrack={
|
||||||
// }}
|
subtitle
|
||||||
|
? {
|
||||||
// ref: player,
|
type: "index",
|
||||||
// shouldPlay: isPlaying,
|
value: subtitle.trackIndex,
|
||||||
// onDoubleClick: () => {
|
}
|
||||||
// setFullscreen(!document.fullscreenElement);
|
: { type: "disabled" }
|
||||||
// },
|
}
|
||||||
// onPlay: () => setPlay(true),
|
// TODO: textTracks: external subtitles
|
||||||
// onPause: () => setPlay(false),
|
|
||||||
// onWaiting: () => setLoad(true),
|
|
||||||
// onCanPlay: () => setLoad(false),
|
|
||||||
// onError: () => {
|
// onError: () => {
|
||||||
// if (player?.current?.error?.code === MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED)
|
// if (player?.current?.error?.code === MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED)
|
||||||
// setPlayMode(PlayMode.Transmux);
|
// setPlayMode(PlayMode.Transmux);
|
||||||
// },
|
// },
|
||||||
// onTimeUpdate: () => setProgress(player?.current?.currentTime ?? 0),
|
|
||||||
// onDurationChange: () => setDuration(player?.current?.duration ?? 0),
|
|
||||||
// onProgress: () =>
|
|
||||||
// setBuffered(
|
|
||||||
// player?.current?.buffered.length
|
|
||||||
// ? player.current.buffered.end(player.current.buffered.length - 1)
|
|
||||||
// : 0,
|
|
||||||
// ),
|
|
||||||
// onVolumeChange: () => {
|
|
||||||
// if (!player.current) return;
|
|
||||||
// setVolume(player.current.volume * 100);
|
|
||||||
// setMuted(player?.current.muted);
|
|
||||||
// },
|
|
||||||
useNativeControls={false}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const htmlTrackAtom = atom<HTMLTrackElement | null>(null);
|
// const htmlTrackAtom = atom<HTMLTrackElement | null>(null);
|
||||||
const suboctoAtom = atom<SubtitleOctopus | null>(null);
|
// const suboctoAtom = atom<SubtitleOctopus | null>(null);
|
||||||
export const [_subtitleAtom, subtitleAtom] = bakedAtom<
|
// export const [_subtitleAtom, subtitleAtom] = bakedAtom<
|
||||||
Track | null,
|
// Track | null,
|
||||||
{ track: Track; fonts: Font[] } | null
|
// { track: Track; fonts: Font[] } | null
|
||||||
>(null, (get, set, value, baked) => {
|
// >(null, (get, set, value, baked) => {
|
||||||
const removeHtmlSubtitle = () => {
|
// const removeHtmlSubtitle = () => {
|
||||||
const htmlTrack = get(htmlTrackAtom);
|
// const htmlTrack = get(htmlTrackAtom);
|
||||||
if (htmlTrack) htmlTrack.remove();
|
// if (htmlTrack) htmlTrack.remove();
|
||||||
set(htmlTrackAtom, null);
|
// set(htmlTrackAtom, null);
|
||||||
};
|
// };
|
||||||
const removeOctoSub = () => {
|
// const removeOctoSub = () => {
|
||||||
const subocto = get(suboctoAtom);
|
// const subocto = get(suboctoAtom);
|
||||||
if (subocto) {
|
// if (subocto) {
|
||||||
subocto.freeTrack();
|
// subocto.freeTrack();
|
||||||
subocto.dispose();
|
// subocto.dispose();
|
||||||
}
|
// }
|
||||||
set(suboctoAtom, null);
|
// set(suboctoAtom, null);
|
||||||
};
|
// };
|
||||||
|
|
||||||
const player = get(playerAtom);
|
// const player = get(playerAtom);
|
||||||
if (!player?.current) return;
|
// if (!player?.current) return;
|
||||||
|
|
||||||
if (get(baked)?.id === value?.track.id) return;
|
// if (get(baked)?.id === value?.track.id) return;
|
||||||
|
|
||||||
set(baked, value?.track ?? null);
|
// set(baked, value?.track ?? null);
|
||||||
if (!value) {
|
// if (!value) {
|
||||||
removeHtmlSubtitle();
|
// removeHtmlSubtitle();
|
||||||
removeOctoSub();
|
// removeOctoSub();
|
||||||
} else if (value.track.codec === "vtt" || value.track.codec === "subrip") {
|
// } else if (value.track.codec === "vtt" || value.track.codec === "subrip") {
|
||||||
removeOctoSub();
|
// removeOctoSub();
|
||||||
if (player.current.textTracks.length > 0) player.current.textTracks[0].mode = "hidden";
|
// if (player.current.textTracks.length > 0) player.current.textTracks[0].mode = "hidden";
|
||||||
const track: HTMLTrackElement = get(htmlTrackAtom) ?? document.createElement("track");
|
// const track: HTMLTrackElement = get(htmlTrackAtom) ?? document.createElement("track");
|
||||||
track.kind = "subtitles";
|
// track.kind = "subtitles";
|
||||||
track.label = value.track.displayName;
|
// track.label = value.track.displayName;
|
||||||
if (value.track.language) track.srclang = value.track.language;
|
// if (value.track.language) track.srclang = value.track.language;
|
||||||
track.src = value.track.link! + ".vtt";
|
// track.src = value.track.link! + ".vtt";
|
||||||
track.className = "subtitle_container";
|
// track.className = "subtitle_container";
|
||||||
track.default = true;
|
// track.default = true;
|
||||||
track.onload = () => {
|
// track.onload = () => {
|
||||||
if (player.current) player.current.textTracks[0].mode = "showing";
|
// if (player.current) player.current.textTracks[0].mode = "showing";
|
||||||
};
|
// };
|
||||||
if (!get(htmlTrackAtom)) player.current.appendChild(track);
|
// if (!get(htmlTrackAtom)) player.current.appendChild(track);
|
||||||
set(htmlTrackAtom, track);
|
// set(htmlTrackAtom, track);
|
||||||
} else if (value.track.codec === "ass") {
|
// } else if (value.track.codec === "ass") {
|
||||||
removeHtmlSubtitle();
|
// removeHtmlSubtitle();
|
||||||
removeOctoSub();
|
// removeOctoSub();
|
||||||
set(
|
// set(
|
||||||
suboctoAtom,
|
// suboctoAtom,
|
||||||
new SubtitleOctopus({
|
// new SubtitleOctopus({
|
||||||
video: player.current,
|
// video: player.current,
|
||||||
subUrl: value.track.link!,
|
// subUrl: value.track.link!,
|
||||||
workerUrl: "/_next/static/chunks/subtitles-octopus-worker.js",
|
// workerUrl: "/_next/static/chunks/subtitles-octopus-worker.js",
|
||||||
legacyWorkerUrl: "/_next/static/chunks/subtitles-octopus-worker-legacy.js",
|
// legacyWorkerUrl: "/_next/static/chunks/subtitles-octopus-worker-legacy.js",
|
||||||
fonts: value.fonts?.map((x) => x.link),
|
// fonts: value.fonts?.map((x) => x.link),
|
||||||
renderMode: "wasm-blend",
|
// renderMode: "wasm-blend",
|
||||||
}),
|
// }),
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
});
|
// });
|
||||||
|
|
||||||
const { useParam } = createParam<{ subtitle: string }>();
|
// const { useParam } = createParam<{ subtitle: string }>();
|
||||||
|
|
||||||
export const useSubtitleController = (
|
// export const useSubtitleController = (
|
||||||
player: RefObject<HTMLVideoElement>,
|
// player: RefObject<HTMLVideoElement>,
|
||||||
subtitles?: Track[],
|
// subtitles?: Track[],
|
||||||
fonts?: Font[],
|
// fonts?: Font[],
|
||||||
) => {
|
// ) => {
|
||||||
const [subtitle] = useParam("subtitle");
|
// const [subtitle] = useParam("subtitle");
|
||||||
const selectSubtitle = useSetAtom(subtitleAtom);
|
// const selectSubtitle = useSetAtom(subtitleAtom);
|
||||||
|
|
||||||
const newSub = subtitles?.find((x) => x.language === subtitle);
|
// const newSub = subtitles?.find((x) => x.language === subtitle);
|
||||||
useEffect(() => {
|
// useEffect(() => {
|
||||||
if (newSub === undefined) return;
|
// if (newSub === undefined) return;
|
||||||
selectSubtitle({ track: newSub, fonts: fonts ?? [] });
|
// selectSubtitle({ track: newSub, fonts: fonts ?? [] });
|
||||||
}, [player.current?.src, newSub, fonts, selectSubtitle]);
|
// }, [player.current?.src, newSub, fonts, selectSubtitle]);
|
||||||
};
|
// };
|
||||||
|
Loading…
x
Reference in New Issue
Block a user