Use a better display name for audios

This commit is contained in:
Zoe Roux 2023-10-11 19:18:34 +02:00
parent 4be4fa2c4f
commit 0d4b9fe488
6 changed files with 44 additions and 24 deletions

View File

@ -20,6 +20,17 @@
import { z } from "zod"; import { z } from "zod";
import { imageFn } from "../traits"; import { imageFn } from "../traits";
import i18next from "i18next";
const getDisplayName = (sub: Track) => {
const languageNames = new Intl.DisplayNames([i18next.language ?? "en"], { type: "language" });
const lng = sub.language ? languageNames.of(sub.language) : undefined;
if (lng && sub.title && sub.title !== lng) return `${lng} - ${sub.title}`;
if (lng) return lng;
if (sub.title) return sub.title;
return `Unknwon (${sub.index})`;
};
/** /**
* A audio or subtitle track. * A audio or subtitle track.
@ -50,14 +61,23 @@ export const TrackP = z.object({
*/ */
isForced: z.boolean(), isForced: z.boolean(),
}); });
export type Audio = z.infer<typeof TrackP>; export type Track = z.infer<typeof TrackP>;
export const AudioP = TrackP.transform((x) => ({
...x,
displayName: getDisplayName(x),
}));
export type Audio = z.infer<typeof AudioP>;
export const SubtitleP = TrackP.extend({ export const SubtitleP = TrackP.extend({
/* /*
* The url of this track (only if this is a subtitle).. * The url of this track (only if this is a subtitle)..
*/ */
link: z.string().transform(imageFn).nullable(), link: z.string().transform(imageFn).nullable(),
}); }).transform((x) => ({
...x,
displayName: getDisplayName(x),
}));
export type Subtitle = z.infer<typeof SubtitleP>; export type Subtitle = z.infer<typeof SubtitleP>;
export const ChapterP = z.object({ export const ChapterP = z.object({
@ -97,7 +117,7 @@ export const WatchInfoP = z.object({
/** /**
* The list of audio tracks. * The list of audio tracks.
*/ */
audios: z.array(TrackP), audios: z.array(AudioP),
/** /**
* The list of subtitles tracks. * The list of subtitles tracks.
*/ */

View File

@ -33,7 +33,7 @@ import {
tooltip, tooltip,
ts, ts,
} from "@kyoo/primitives"; } from "@kyoo/primitives";
import { Chapter, KyooImage, Subtitle } from "@kyoo/models"; import { Chapter, KyooImage, Subtitle, Audio } from "@kyoo/models";
import { useAtomValue, useSetAtom, useAtom } from "jotai"; import { useAtomValue, useSetAtom, useAtom } from "jotai";
import { ImageStyle, Platform, Pressable, View, ViewProps } from "react-native"; import { ImageStyle, Platform, Pressable, View, ViewProps } from "react-native";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
@ -52,6 +52,7 @@ export const Hover = ({
poster, poster,
chapters, chapters,
subtitles, subtitles,
audios,
fonts, fonts,
previousSlug, previousSlug,
nextSlug, nextSlug,
@ -68,6 +69,7 @@ export const Hover = ({
poster?: KyooImage | null; poster?: KyooImage | null;
chapters?: Chapter[]; chapters?: Chapter[];
subtitles?: Subtitle[]; subtitles?: Subtitle[];
audios?: Audio[];
fonts?: string[]; fonts?: string[];
previousSlug?: string | null; previousSlug?: string | null;
nextSlug?: string | null; nextSlug?: string | null;
@ -123,6 +125,7 @@ export const Hover = ({
<LeftButtons previousSlug={previousSlug} nextSlug={nextSlug} /> <LeftButtons previousSlug={previousSlug} nextSlug={nextSlug} />
<RightButtons <RightButtons
subtitles={subtitles} subtitles={subtitles}
audios={audios}
fonts={fonts} fonts={fonts}
onMenuOpen={onMenuOpen} onMenuOpen={onMenuOpen}
onMenuClose={onMenuClose} onMenuClose={onMenuClose}

View File

@ -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 { Subtitle } from "@kyoo/models"; import { Audio, Subtitle } from "@kyoo/models";
import { IconButton, tooltip, Menu, ts } from "@kyoo/primitives"; import { IconButton, tooltip, Menu, ts } from "@kyoo/primitives";
import { useAtom } from "jotai"; import { useAtom } from "jotai";
import { Platform, View } from "react-native"; import { Platform, View } from "react-native";
@ -33,23 +33,15 @@ import { fullscreenAtom, subtitleAtom } from "../state";
import { AudiosMenu, QualitiesMenu } from "../video"; import { AudiosMenu, QualitiesMenu } from "../video";
import i18next from "i18next"; import i18next from "i18next";
export const getDisplayName = (sub: Subtitle) => {
const languageNames = new Intl.DisplayNames([i18next.language ?? "en"], { type: "language" });
const lng = sub.language ? languageNames.of(sub.language) : undefined;
if (lng && sub.title) return `${lng} - ${sub.title}`;
if (lng) return lng;
if (sub.title) return sub.title;
return `Unknwon (${sub.index})`;
};
export const RightButtons = ({ export const RightButtons = ({
audios,
subtitles, subtitles,
fonts, fonts,
onMenuOpen, onMenuOpen,
onMenuClose, onMenuClose,
...props ...props
}: { }: {
audios?: Audio[];
subtitles?: Subtitle[]; subtitles?: Subtitle[];
fonts?: string[]; fonts?: string[];
onMenuOpen: () => void; onMenuOpen: () => void;
@ -81,7 +73,7 @@ export const RightButtons = ({
{subtitles.map((x) => ( {subtitles.map((x) => (
<Menu.Item <Menu.Item
key={x.index} key={x.index}
label={x.link ? getDisplayName(x) : `${getDisplayName(x)} (${x.codec})`} label={x.link ? x.displayName : `${x.displayName} (${x.codec})`}
selected={selectedSubtitle === x} selected={selectedSubtitle === x}
disabled={!x.link} disabled={!x.link}
onSelect={() => setSubtitle(x)} onSelect={() => setSubtitle(x)}
@ -94,6 +86,7 @@ export const RightButtons = ({
icon={MusicNote} icon={MusicNote}
onMenuOpen={onMenuOpen} onMenuOpen={onMenuOpen}
onMenuClose={onMenuClose} onMenuClose={onMenuClose}
audios={audios}
{...tooltip(t("player.audios"), true)} {...tooltip(t("player.audios"), true)}
{...spacing} {...spacing}
/> />

View File

@ -77,6 +77,7 @@ const mapData = (
href: data ? (data.type === "movie" ? `/movie/${data.slug}` : `/show/${data.show!.slug}`) : "#", href: data ? (data.type === "movie" ? `/movie/${data.slug}` : `/show/${data.show!.slug}`) : "#",
poster: data.type === "movie" ? data.poster : data.show!.poster, poster: data.type === "movie" ? data.poster : data.show!.poster,
subtitles: info.subtitles, subtitles: info.subtitles,
audios: info.audios,
chapters: info.chapters, chapters: info.chapters,
fonts: info.fonts, fonts: info.fonts,
previousSlug, previousSlug,

View File

@ -34,7 +34,7 @@ declare module "react-native-video" {
export * from "react-native-video"; export * from "react-native-video";
import { Subtitle, getToken } from "@kyoo/models"; import { Audio, Subtitle, getToken } from "@kyoo/models";
import { IconButton, Menu } from "@kyoo/primitives"; import { IconButton, Menu } from "@kyoo/primitives";
import { ComponentProps, forwardRef, useEffect, useRef } from "react"; import { ComponentProps, forwardRef, useEffect, useRef } from "react";
import { atom, useAtom, useAtomValue, useSetAtom } from "jotai"; import { atom, useAtom, useAtomValue, useSetAtom } from "jotai";
@ -119,7 +119,7 @@ const Video = forwardRef<NativeVideo, VideoProps>(function Video(
export default Video; export default Video;
type CustomMenu = ComponentProps<typeof Menu<ComponentProps<typeof IconButton>>>; type CustomMenu = ComponentProps<typeof Menu<ComponentProps<typeof IconButton>>>;
export const AudiosMenu = (props: CustomMenu) => { export const AudiosMenu = ({ audios, ...props }: CustomMenu & { audios?: Audio[] }) => {
const info = useAtomValue(infoAtom); const info = useAtomValue(infoAtom);
const [audio, setAudio] = useAtom(audioAtom); const [audio, setAudio] = useAtom(audioAtom);
@ -130,7 +130,7 @@ export const AudiosMenu = (props: CustomMenu) => {
{info.audioTracks.map((x) => ( {info.audioTracks.map((x) => (
<Menu.Item <Menu.Item
key={x.index} key={x.index}
label={x.title} label={audios?.[x.index].displayName ?? x.title}
selected={audio === x.index} selected={audio === x.index}
onSelect={() => setAudio(x.index)} onSelect={() => setAudio(x.index)}
/> />

View File

@ -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 { getToken, Subtitle } from "@kyoo/models"; import { getToken, Subtitle, Audio } from "@kyoo/models";
import { import {
forwardRef, forwardRef,
RefObject, RefObject,
@ -38,7 +38,6 @@ import Hls, { Level, LoadPolicy } from "hls.js";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { Menu } from "@kyoo/primitives"; import { Menu } from "@kyoo/primitives";
import toVttBlob from "srt-webvtt"; import toVttBlob from "srt-webvtt";
import { getDisplayName } from "./components/right-buttons";
let hls: Hls | null = null; let hls: Hls | null = null;
@ -256,7 +255,7 @@ const useSubtitle = (
const addSubtitle = async () => { const addSubtitle = async () => {
const track: HTMLTrackElement = htmlTrack ?? document.createElement("track"); const track: HTMLTrackElement = htmlTrack ?? document.createElement("track");
track.kind = "subtitles"; track.kind = "subtitles";
track.label = getDisplayName(value); track.label = value.displayName;
if (value.language) track.srclang = value.language; if (value.language) track.srclang = value.language;
track.src = value.codec === "subrip" ? await toWebVtt(value.link!) : value.link!; track.src = value.codec === "subrip" ? await toWebVtt(value.link!) : value.link!;
track.className = "subtitle_container"; track.className = "subtitle_container";
@ -300,14 +299,18 @@ const toWebVtt = async (srtUrl: string) => {
return await toVttBlob(srt); return await toVttBlob(srt);
}; };
export const AudiosMenu = (props: ComponentProps<typeof Menu>) => { export const AudiosMenu = ({
audios,
...props
}: ComponentProps<typeof Menu> & { audios?: Audio[] }) => {
if (!hls || hls.audioTracks.length < 2) return null; if (!hls || hls.audioTracks.length < 2) return null;
console.log(audios);
return ( return (
<Menu {...props}> <Menu {...props}>
{hls.audioTracks.map((x, i) => ( {hls.audioTracks.map((x, i) => (
<Menu.Item <Menu.Item
key={i.toString()} key={i.toString()}
label={x.name} label={audios?.[i].displayName ?? x.name}
selected={hls!.audioTrack === i} selected={hls!.audioTrack === i}
onSelect={() => (hls!.audioTrack = i)} onSelect={() => (hls!.audioTrack = i)}
/> />