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";
|
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.
|
* The index of this track on the episode.
|
||||||
* E.g. "AVC"
|
* 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(),
|
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
|
* The Quality of the Video
|
||||||
* E.g. "1080p"
|
* E.g. "1080p"
|
||||||
@ -55,37 +83,6 @@ export const VideoP = z.object({
|
|||||||
|
|
||||||
export type Video = z.infer<typeof VideoP>;
|
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 const AudioP = TrackP;
|
||||||
export type Audio = z.infer<typeof AudioP>;
|
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)..
|
* The url of this track (only if this is a subtitle)..
|
||||||
*/
|
*/
|
||||||
link: z.string().transform(imageFn).nullable(),
|
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>;
|
export type Subtitle = z.infer<typeof SubtitleP>;
|
||||||
|
|
||||||
@ -152,7 +153,7 @@ export const WatchInfoP = z
|
|||||||
/**
|
/**
|
||||||
* The video track.
|
* The video track.
|
||||||
*/
|
*/
|
||||||
video: VideoP.nullable(),
|
videos: z.array(VideoP),
|
||||||
/**
|
/**
|
||||||
* The list of audio tracks.
|
* The list of audio tracks.
|
||||||
*/
|
*/
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
type Video,
|
||||||
type Audio,
|
type Audio,
|
||||||
type QueryIdentifier,
|
type QueryIdentifier,
|
||||||
type Subtitle,
|
type Subtitle,
|
||||||
@ -33,8 +34,10 @@ import { useYoshiki } from "yoshiki/native";
|
|||||||
import { Fetch } from "../fetch";
|
import { Fetch } from "../fetch";
|
||||||
import { useDisplayName } from "../utils";
|
import { useDisplayName } from "../utils";
|
||||||
|
|
||||||
|
const formatBitrate = (b: number) => `${(b / 1000000).toFixed(2)} Mbps`;
|
||||||
|
|
||||||
const MediaInfoTable = ({
|
const MediaInfoTable = ({
|
||||||
mediaInfo: { path, video, container, audios, subtitles, duration, size },
|
mediaInfo: { path, videos, container, audios, subtitles, duration, size },
|
||||||
}: {
|
}: {
|
||||||
mediaInfo: Partial<WatchInfo>;
|
mediaInfo: Partial<WatchInfo>;
|
||||||
}) => {
|
}) => {
|
||||||
@ -42,22 +45,45 @@ const MediaInfoTable = ({
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { css } = useYoshiki();
|
const { css } = useYoshiki();
|
||||||
|
|
||||||
const formatBitrate = (b: number) => `${(b / 1000000).toFixed(2)} Mbps`;
|
|
||||||
const formatTrackTable = (trackTable: (Audio | Subtitle)[], type: "subtitles" | "audio") => {
|
const formatTrackTable = (trackTable: (Audio | Subtitle)[], type: "subtitles" | "audio") => {
|
||||||
if (trackTable.length === 0) {
|
if (trackTable.length === 0) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
const singleTrack = trackTable.length === 1;
|
const singleTrack = trackTable.length === 1;
|
||||||
return trackTable.reduce(
|
return trackTable.reduce(
|
||||||
(collected, audioTrack, index) => {
|
(collected, track, index) => {
|
||||||
// If there is only one track, we do not need to show an 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}`] =
|
collected[singleTrack ? t(`mediainfo.${type}`) : `${t(`mediainfo.${type}`)} ${index + 1}`] =
|
||||||
[
|
[
|
||||||
getDisplayName(audioTrack),
|
getDisplayName(track),
|
||||||
// Only show it if there is more than one track
|
// Only show it if there is more than one track
|
||||||
audioTrack.isDefault && !singleTrack ? t("mediainfo.default") : undefined,
|
track.isDefault && !singleTrack ? t("mediainfo.default") : undefined,
|
||||||
audioTrack.isForced ? t("mediainfo.forced") : undefined,
|
track.isForced ? t("mediainfo.forced") : undefined,
|
||||||
audioTrack.codec,
|
"isExternal" in track && track.isExternal ? t("mediainfo.external") : undefined,
|
||||||
|
track.codec,
|
||||||
|
]
|
||||||
|
.filter((x) => x !== undefined)
|
||||||
|
.join(" - ");
|
||||||
|
return collected;
|
||||||
|
},
|
||||||
|
{} 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)
|
.filter((x) => x !== undefined)
|
||||||
.join(" - ");
|
.join(" - ");
|
||||||
@ -74,15 +100,7 @@ const MediaInfoTable = ({
|
|||||||
[t("mediainfo.duration")]: duration,
|
[t("mediainfo.duration")]: duration,
|
||||||
[t("mediainfo.size")]: size,
|
[t("mediainfo.size")]: size,
|
||||||
},
|
},
|
||||||
{
|
videos === undefined ? { [t("mediainfo.video")]: undefined } : formatVideoTable(videos),
|
||||||
[t("mediainfo.video")]: video
|
|
||||||
? `${video.width}x${video.height} (${video.quality}) - ${formatBitrate(
|
|
||||||
video.bitrate,
|
|
||||||
)} - ${video.codec}`
|
|
||||||
: video === null
|
|
||||||
? t("mediainfo.novideo")
|
|
||||||
: undefined,
|
|
||||||
},
|
|
||||||
audios === undefined
|
audios === undefined
|
||||||
? { [t("mediainfo.audio")]: undefined }
|
? { [t("mediainfo.audio")]: undefined }
|
||||||
: formatTrackTable(audios, "audio"),
|
: formatTrackTable(audios, "audio"),
|
||||||
|
@ -14,6 +14,7 @@ export const useDisplayName = () => {
|
|||||||
if (lng && sub.title && sub.title !== lng) return `${lng} - ${sub.title}`;
|
if (lng && sub.title && sub.title !== lng) return `${lng} - ${sub.title}`;
|
||||||
if (lng) return lng;
|
if (lng) return lng;
|
||||||
if (sub.title) return sub.title;
|
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",
|
"subtitles": "Subtitles",
|
||||||
"forced": "Forced",
|
"forced": "Forced",
|
||||||
"default": "Default",
|
"default": "Default",
|
||||||
|
"external": "External",
|
||||||
"duration": "Duration",
|
"duration": "Duration",
|
||||||
"size": "Size",
|
"size": "Size",
|
||||||
"novideo": "No video",
|
"novideo": "No video",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user