mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-06-03 13:44:33 -04:00
Handle audio settings
This commit is contained in:
parent
0be1bf4f15
commit
92a3c2945c
@ -63,7 +63,6 @@ const mapData = (
|
|||||||
fonts: info?.fonts,
|
fonts: info?.fonts,
|
||||||
previousSlug,
|
previousSlug,
|
||||||
nextSlug,
|
nextSlug,
|
||||||
qualitiesAvailables: !!data.links.hls,
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -139,6 +138,7 @@ export const Player = ({ slug, type }: { slug: string; type: "episode" | "movie"
|
|||||||
>
|
>
|
||||||
<Video
|
<Video
|
||||||
links={data?.links}
|
links={data?.links}
|
||||||
|
audios={info?.audios}
|
||||||
subtitles={info?.subtitles}
|
subtitles={info?.subtitles}
|
||||||
setError={setPlaybackError}
|
setError={setPlaybackError}
|
||||||
fonts={info?.fonts}
|
fonts={info?.fonts}
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Episode, Subtitle, useAccount } from "@kyoo/models";
|
import { Audio, Episode, Subtitle, useAccount } from "@kyoo/models";
|
||||||
import { atom, useAtom, useAtomValue, useSetAtom } from "jotai";
|
import { atom, useAtom, useAtomValue, useSetAtom } from "jotai";
|
||||||
import { useAtomCallback } from "jotai/utils";
|
import { useAtomCallback } from "jotai/utils";
|
||||||
import { ElementRef, memo, useEffect, useLayoutEffect, useRef, useState, useCallback } from "react";
|
import { ElementRef, memo, useEffect, useLayoutEffect, useRef, useState, useCallback } from "react";
|
||||||
@ -83,10 +83,12 @@ export const fullscreenAtom = atom(
|
|||||||
const privateFullscreen = atom(false);
|
const privateFullscreen = atom(false);
|
||||||
|
|
||||||
export const subtitleAtom = atom<Subtitle | null>(null);
|
export const subtitleAtom = atom<Subtitle | null>(null);
|
||||||
|
export const audioAtom = atom<Audio>({ index: 0 } as Audio);
|
||||||
|
|
||||||
export const Video = memo(function Video({
|
export const Video = memo(function Video({
|
||||||
links,
|
links,
|
||||||
subtitles,
|
subtitles,
|
||||||
|
audios,
|
||||||
setError,
|
setError,
|
||||||
fonts,
|
fonts,
|
||||||
startTime: startTimeP,
|
startTime: startTimeP,
|
||||||
@ -94,6 +96,7 @@ export const Video = memo(function Video({
|
|||||||
}: {
|
}: {
|
||||||
links?: Episode["links"];
|
links?: Episode["links"];
|
||||||
subtitles?: Subtitle[];
|
subtitles?: Subtitle[];
|
||||||
|
audios?: Audio[];
|
||||||
setError: (error: string | undefined) => void;
|
setError: (error: string | undefined) => void;
|
||||||
fonts?: string[];
|
fonts?: string[];
|
||||||
startTime?: number | null;
|
startTime?: number | null;
|
||||||
@ -152,6 +155,26 @@ export const Video = memo(function Video({
|
|||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [subtitles, setSubtitle, defaultSubLanguage, ref.current]);
|
}, [subtitles, setSubtitle, defaultSubLanguage, ref.current]);
|
||||||
|
|
||||||
|
const defaultAudioLanguage = account?.settings.audioLanguage ?? "default";
|
||||||
|
const setAudio = useSetAtom(audioAtom);
|
||||||
|
useEffect(() => {
|
||||||
|
if (!audios) return;
|
||||||
|
setAudio((audio) => {
|
||||||
|
if (audio) {
|
||||||
|
const ret = audios.find((x) => x.language === audio.language);
|
||||||
|
if (ret) return ret;
|
||||||
|
}
|
||||||
|
if (defaultAudioLanguage !== "default") {
|
||||||
|
const ret = audios.find((x) => x.language === defaultAudioLanguage);
|
||||||
|
if (ret) return ret;
|
||||||
|
}
|
||||||
|
return audios.find((x) => x.isDefault) ?? audios[0];
|
||||||
|
});
|
||||||
|
// When the video change, try to persist the subtitle language.
|
||||||
|
// Also include the player ref, it can be initalised after the subtitles.
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [audios, setAudio, defaultAudioLanguage, ref.current]);
|
||||||
|
|
||||||
const volume = useAtomValue(volumeAtom);
|
const volume = useAtomValue(volumeAtom);
|
||||||
const isMuted = useAtomValue(mutedAtom);
|
const isMuted = useAtomValue(mutedAtom);
|
||||||
|
|
||||||
|
@ -40,7 +40,7 @@ import { ComponentProps, forwardRef, useEffect, useRef } from "react";
|
|||||||
import { atom, useAtom, useAtomValue, useSetAtom } from "jotai";
|
import { atom, useAtom, useAtomValue, useSetAtom } from "jotai";
|
||||||
import NativeVideo, { OnLoadData, VideoProps } from "react-native-video";
|
import NativeVideo, { OnLoadData, VideoProps } from "react-native-video";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { PlayMode, playModeAtom, subtitleAtom } from "./state";
|
import { PlayMode, audioAtom, playModeAtom, subtitleAtom } from "./state";
|
||||||
import uuid from "react-native-uuid";
|
import uuid from "react-native-uuid";
|
||||||
import { View } from "react-native";
|
import { View } from "react-native";
|
||||||
import "@kyoo/primitives/src/types.d.ts";
|
import "@kyoo/primitives/src/types.d.ts";
|
||||||
@ -54,7 +54,6 @@ const MimeTypes: Map<string, string> = new Map([
|
|||||||
|
|
||||||
const infoAtom = atom<OnLoadData | null>(null);
|
const infoAtom = atom<OnLoadData | null>(null);
|
||||||
const videoAtom = atom(0);
|
const videoAtom = atom(0);
|
||||||
const audioAtom = atom(0);
|
|
||||||
|
|
||||||
const clientId = uuid.v4() as string;
|
const clientId = uuid.v4() as string;
|
||||||
|
|
||||||
@ -94,7 +93,7 @@ const Video = forwardRef<NativeVideo, VideoProps>(function Video(
|
|||||||
}}
|
}}
|
||||||
onBuffer={onBuffer}
|
onBuffer={onBuffer}
|
||||||
selectedVideoTrack={video === -1 ? { type: "auto" } : { type: "resolution", value: video }}
|
selectedVideoTrack={video === -1 ? { type: "auto" } : { type: "resolution", value: video }}
|
||||||
selectedAudioTrack={{ type: "index", value: audio }}
|
selectedAudioTrack={{ type: "index", value: audio.index }}
|
||||||
textTracks={subtitles?.map((x) => ({
|
textTracks={subtitles?.map((x) => ({
|
||||||
type: MimeTypes.get(x.codec) as any,
|
type: MimeTypes.get(x.codec) as any,
|
||||||
uri: x.link!,
|
uri: x.link!,
|
||||||
@ -130,8 +129,8 @@ export const AudiosMenu = ({ audios, ...props }: CustomMenu & { audios?: Audio[]
|
|||||||
<Menu.Item
|
<Menu.Item
|
||||||
key={x.index}
|
key={x.index}
|
||||||
label={audios?.[x.index].displayName ?? x.title}
|
label={audios?.[x.index].displayName ?? x.title}
|
||||||
selected={audio === x.index}
|
selected={audio!.index === x.index}
|
||||||
onSelect={() => setAudio(x.index)}
|
onSelect={() => setAudio(x as any)}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</Menu>
|
</Menu>
|
||||||
|
@ -26,14 +26,13 @@ import {
|
|||||||
useImperativeHandle,
|
useImperativeHandle,
|
||||||
useLayoutEffect,
|
useLayoutEffect,
|
||||||
useRef,
|
useRef,
|
||||||
useReducer,
|
|
||||||
ComponentProps,
|
ComponentProps,
|
||||||
} from "react";
|
} from "react";
|
||||||
import { VideoProps } from "react-native-video";
|
import { VideoProps } from "react-native-video";
|
||||||
import { useAtomValue, useSetAtom, useAtom } from "jotai";
|
import { useAtomValue, useSetAtom, useAtom } from "jotai";
|
||||||
import { useForceRerender, useYoshiki } from "yoshiki";
|
import { useForceRerender, useYoshiki } from "yoshiki";
|
||||||
import Jassub from "jassub";
|
import Jassub from "jassub";
|
||||||
import { playAtom, PlayMode, playModeAtom, progressAtom, subtitleAtom } from "./state";
|
import { audioAtom, playAtom, PlayMode, playModeAtom, progressAtom, subtitleAtom } from "./state";
|
||||||
import Hls, { Level, LoadPolicy } from "hls.js";
|
import Hls, { Level, LoadPolicy } from "hls.js";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { Menu, tooltip } from "@kyoo/primitives";
|
import { Menu, tooltip } from "@kyoo/primitives";
|
||||||
@ -180,6 +179,19 @@ const Video = forwardRef<{ seek: (value: number) => void }, VideoProps>(function
|
|||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [source.uri, source.hls]);
|
}, [source.uri, source.hls]);
|
||||||
|
|
||||||
|
const mode = useAtomValue(playModeAtom);
|
||||||
|
const audio = useAtomValue(audioAtom);
|
||||||
|
useEffect(() => {
|
||||||
|
if (!hls) return;
|
||||||
|
const update = () => {
|
||||||
|
if (!hls) return;
|
||||||
|
hls.audioTrack = audio.index;
|
||||||
|
};
|
||||||
|
update();
|
||||||
|
hls.on(Hls.Events.AUDIO_TRACKS_UPDATED, update);
|
||||||
|
return () => hls?.off(Hls.Events.AUDIO_TRACKS_UPDATED, update);
|
||||||
|
}, [audio, mode]);
|
||||||
|
|
||||||
const setPlay = useSetAtom(playAtom);
|
const setPlay = useSetAtom(playAtom);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!ref.current) return;
|
if (!ref.current) return;
|
||||||
@ -334,6 +346,7 @@ export const AudiosMenu = ({
|
|||||||
}: ComponentProps<typeof Menu<{ disabled?: boolean }>> & { audios?: Audio[] }) => {
|
}: ComponentProps<typeof Menu<{ disabled?: boolean }>> & { audios?: Audio[] }) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const rerender = useForceRerender();
|
const rerender = useForceRerender();
|
||||||
|
const [_, setAudio] = useAtom(audioAtom);
|
||||||
// force rerender when mode changes
|
// force rerender when mode changes
|
||||||
useAtomValue(playModeAtom);
|
useAtomValue(playModeAtom);
|
||||||
|
|
||||||
@ -343,8 +356,8 @@ export const AudiosMenu = ({
|
|||||||
return () => hls!.off(Hls.Events.AUDIO_TRACK_LOADED, rerender);
|
return () => hls!.off(Hls.Events.AUDIO_TRACK_LOADED, rerender);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!hls || hls.audioTracks.length < 2)
|
if (!hls) return <Menu {...props} disabled {...tooltip(t("player.notInPristine"))} />;
|
||||||
return <Menu {...props} disabled {...tooltip(t("player.notInPristine"))} />;
|
if (hls.audioTracks.length < 2) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Menu {...props}>
|
<Menu {...props}>
|
||||||
@ -353,7 +366,7 @@ export const AudiosMenu = ({
|
|||||||
key={i.toString()}
|
key={i.toString()}
|
||||||
label={audios?.[i].displayName ?? x.name}
|
label={audios?.[i].displayName ?? x.name}
|
||||||
selected={hls!.audioTrack === i}
|
selected={hls!.audioTrack === i}
|
||||||
onSelect={() => (hls!.audioTrack = i)}
|
onSelect={() => setAudio(audios?.[i] ?? ({ index: i } as any))}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</Menu>
|
</Menu>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user