mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-07-09 03:04:20 -04:00
Update media info display
This commit is contained in:
parent
2618650d27
commit
4e79f38d1e
@ -23,14 +23,42 @@ import { imageFn } from "../traits";
|
||||
import { QualityP } from "./quality";
|
||||
|
||||
/**
|
||||
* A Video track
|
||||
* A audio or subtitle track.
|
||||
*/
|
||||
export const VideoP = z.object({
|
||||
export const TrackP = z.object({
|
||||
/**
|
||||
* The Codec of the Video Track.
|
||||
* E.g. "AVC"
|
||||
* The index of this track on the episode.
|
||||
* NOTE: external subtitles can have a null index
|
||||
*/
|
||||
index: z.number().nullable(),
|
||||
/**
|
||||
* The title of the stream.
|
||||
*/
|
||||
title: z.string().nullable(),
|
||||
/**
|
||||
* The language of this stream (as a ISO-639-2 language code)
|
||||
*/
|
||||
language: z.string().nullable(),
|
||||
/**
|
||||
* The codec of this stream.
|
||||
*/
|
||||
codec: z.string(),
|
||||
/**
|
||||
* Is this stream the default one of it's type?
|
||||
*/
|
||||
isDefault: z.boolean(),
|
||||
/**
|
||||
* Is this stream tagged as forced?
|
||||
* NOTE: not available for videos
|
||||
*/
|
||||
isForced: z.boolean().optional(),
|
||||
});
|
||||
export type Track = z.infer<typeof TrackP>;
|
||||
|
||||
/**
|
||||
* A Video track
|
||||
*/
|
||||
export const VideoP = TrackP.extend({
|
||||
/**
|
||||
* The Quality of the Video
|
||||
* E.g. "1080p"
|
||||
@ -55,37 +83,6 @@ export const VideoP = z.object({
|
||||
|
||||
export type Video = z.infer<typeof VideoP>;
|
||||
|
||||
/**
|
||||
* A audio or subtitle track.
|
||||
*/
|
||||
export const TrackP = z.object({
|
||||
/**
|
||||
* The index of this track on the episode.
|
||||
*/
|
||||
index: z.number(),
|
||||
/**
|
||||
* The title of the stream.
|
||||
*/
|
||||
title: z.string().nullable(),
|
||||
/**
|
||||
* The language of this stream (as a ISO-639-2 language code)
|
||||
*/
|
||||
language: z.string().nullable(),
|
||||
/**
|
||||
* The codec of this stream.
|
||||
*/
|
||||
codec: z.string(),
|
||||
/**
|
||||
* Is this stream the default one of it's type?
|
||||
*/
|
||||
isDefault: z.boolean(),
|
||||
/**
|
||||
* Is this stream tagged as forced?
|
||||
*/
|
||||
isForced: z.boolean(),
|
||||
});
|
||||
export type Track = z.infer<typeof TrackP>;
|
||||
|
||||
export const AudioP = TrackP;
|
||||
export type Audio = z.infer<typeof AudioP>;
|
||||
|
||||
@ -94,6 +91,10 @@ export const SubtitleP = TrackP.extend({
|
||||
* The url of this track (only if this is a subtitle)..
|
||||
*/
|
||||
link: z.string().transform(imageFn).nullable(),
|
||||
/*
|
||||
* Is this an external subtitle (as in stored in a different file)
|
||||
*/
|
||||
isExternal: z.boolean(),
|
||||
});
|
||||
export type Subtitle = z.infer<typeof SubtitleP>;
|
||||
|
||||
@ -152,7 +153,7 @@ export const WatchInfoP = z
|
||||
/**
|
||||
* The video track.
|
||||
*/
|
||||
video: VideoP.nullable(),
|
||||
videos: z.array(VideoP),
|
||||
/**
|
||||
* The list of audio tracks.
|
||||
*/
|
||||
|
@ -19,6 +19,7 @@
|
||||
*/
|
||||
|
||||
import {
|
||||
type Video,
|
||||
type Audio,
|
||||
type QueryIdentifier,
|
||||
type Subtitle,
|
||||
@ -33,8 +34,10 @@ import { useYoshiki } from "yoshiki/native";
|
||||
import { Fetch } from "../fetch";
|
||||
import { useDisplayName } from "../utils";
|
||||
|
||||
const formatBitrate = (b: number) => `${(b / 1000000).toFixed(2)} Mbps`;
|
||||
|
||||
const MediaInfoTable = ({
|
||||
mediaInfo: { path, video, container, audios, subtitles, duration, size },
|
||||
mediaInfo: { path, videos, container, audios, subtitles, duration, size },
|
||||
}: {
|
||||
mediaInfo: Partial<WatchInfo>;
|
||||
}) => {
|
||||
@ -42,22 +45,22 @@ const MediaInfoTable = ({
|
||||
const { t } = useTranslation();
|
||||
const { css } = useYoshiki();
|
||||
|
||||
const formatBitrate = (b: number) => `${(b / 1000000).toFixed(2)} Mbps`;
|
||||
const formatTrackTable = (trackTable: (Audio | Subtitle)[], type: "subtitles" | "audio") => {
|
||||
if (trackTable.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
const singleTrack = trackTable.length === 1;
|
||||
return trackTable.reduce(
|
||||
(collected, audioTrack, index) => {
|
||||
(collected, track, index) => {
|
||||
// If there is only one track, we do not need to show an index
|
||||
collected[singleTrack ? t(`mediainfo.${type}`) : `${t(`mediainfo.${type}`)} ${index + 1}`] =
|
||||
[
|
||||
getDisplayName(audioTrack),
|
||||
getDisplayName(track),
|
||||
// Only show it if there is more than one track
|
||||
audioTrack.isDefault && !singleTrack ? t("mediainfo.default") : undefined,
|
||||
audioTrack.isForced ? t("mediainfo.forced") : undefined,
|
||||
audioTrack.codec,
|
||||
track.isDefault && !singleTrack ? t("mediainfo.default") : undefined,
|
||||
track.isForced ? t("mediainfo.forced") : undefined,
|
||||
"isExternal" in track && track.isExternal ? t("mediainfo.external") : undefined,
|
||||
track.codec,
|
||||
]
|
||||
.filter((x) => x !== undefined)
|
||||
.join(" - ");
|
||||
@ -66,6 +69,29 @@ const MediaInfoTable = ({
|
||||
{} as Record<string, string | undefined>,
|
||||
);
|
||||
};
|
||||
const formatVideoTable = (trackTable: Video[]) => {
|
||||
if (trackTable.length === 0) {
|
||||
return { [t("mediainfo.video")]: t("mediainfo.novideo") };
|
||||
}
|
||||
const singleTrack = trackTable.length === 1;
|
||||
return trackTable.reduce(
|
||||
(collected, video, index) => {
|
||||
// If there is only one track, we do not need to show an index
|
||||
collected[singleTrack ? t("mediainfo.video") : `${t("mediainfo.video")} ${index + 1}`] = [
|
||||
getDisplayName(video),
|
||||
`${video.width}x${video.height} (${video.quality})`,
|
||||
formatBitrate(video.bitrate),
|
||||
// Only show it if there is more than one track
|
||||
video.isDefault && !singleTrack ? t("mediainfo.default") : undefined,
|
||||
video.codec,
|
||||
]
|
||||
.filter((x) => x !== undefined)
|
||||
.join(" - ");
|
||||
return collected;
|
||||
},
|
||||
{} as Record<string, string | undefined>,
|
||||
);
|
||||
};
|
||||
const table = (
|
||||
[
|
||||
{
|
||||
@ -74,15 +100,7 @@ const MediaInfoTable = ({
|
||||
[t("mediainfo.duration")]: duration,
|
||||
[t("mediainfo.size")]: size,
|
||||
},
|
||||
{
|
||||
[t("mediainfo.video")]: video
|
||||
? `${video.width}x${video.height} (${video.quality}) - ${formatBitrate(
|
||||
video.bitrate,
|
||||
)} - ${video.codec}`
|
||||
: video === null
|
||||
? t("mediainfo.novideo")
|
||||
: undefined,
|
||||
},
|
||||
videos === undefined ? { [t("mediainfo.video")]: undefined } : formatVideoTable(videos),
|
||||
audios === undefined
|
||||
? { [t("mediainfo.audio")]: undefined }
|
||||
: formatTrackTable(audios, "audio"),
|
||||
|
@ -14,6 +14,7 @@ export const useDisplayName = () => {
|
||||
if (lng && sub.title && sub.title !== lng) return `${lng} - ${sub.title}`;
|
||||
if (lng) return lng;
|
||||
if (sub.title) return sub.title;
|
||||
return `Unknown (${sub.index})`;
|
||||
if (sub.index !== null) return `Unknown (${sub.index})`;
|
||||
return "Unknown";
|
||||
};
|
||||
};
|
||||
|
@ -260,6 +260,7 @@
|
||||
"subtitles": "Subtitles",
|
||||
"forced": "Forced",
|
||||
"default": "Default",
|
||||
"external": "External",
|
||||
"duration": "Duration",
|
||||
"size": "Size",
|
||||
"novideo": "No video",
|
||||
|
Loading…
x
Reference in New Issue
Block a user