mirror of
https://github.com/Radiquum/AniX.git
synced 2025-04-05 07:44:38 +00:00
refactor: improve error handling for custom player
This commit is contained in:
parent
fc8fe97da7
commit
f94b551cdf
2 changed files with 328 additions and 187 deletions
|
@ -27,6 +27,46 @@ export async function tryCatch<T, E = Error>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function tryCatchPlayer<T, E = Error>(
|
||||||
|
promise: Promise<any>
|
||||||
|
): Promise<Result<any, any>> {
|
||||||
|
try {
|
||||||
|
const res: Awaited<Response> = await promise;
|
||||||
|
const data = await res.json();
|
||||||
|
if (!res.ok) {
|
||||||
|
if (data.message) {
|
||||||
|
return {
|
||||||
|
data: null,
|
||||||
|
error: {
|
||||||
|
message: data.message,
|
||||||
|
code: res.status,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
} else if (data.detail) {
|
||||||
|
return {
|
||||||
|
data: null,
|
||||||
|
error: {
|
||||||
|
message: data.detail,
|
||||||
|
code: res.status,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
data: null,
|
||||||
|
error: {
|
||||||
|
message: res.statusText,
|
||||||
|
code: res.status,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { data, error: null };
|
||||||
|
} catch (error) {
|
||||||
|
return { data: null, error: error as E };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export async function tryCatchAPI<T, E = Error>(
|
export async function tryCatchAPI<T, E = Error>(
|
||||||
promise: Promise<any>
|
promise: Promise<any>
|
||||||
): Promise<Result<any, any>> {
|
): Promise<Result<any, any>> {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { Card } from "flowbite-react";
|
import { Button, Card } from "flowbite-react";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { ENDPOINTS } from "#/api/config";
|
import { ENDPOINTS } from "#/api/config";
|
||||||
|
|
||||||
|
@ -14,6 +14,7 @@ import HlsVideo from "hls-video-element/react";
|
||||||
import VideoJS from "videojs-video-element/react";
|
import VideoJS from "videojs-video-element/react";
|
||||||
import MediaThemeSutro from "./MediaThemeSutro";
|
import MediaThemeSutro from "./MediaThemeSutro";
|
||||||
import { getAnonEpisodesWatched } from "./ReleasePlayer";
|
import { getAnonEpisodesWatched } from "./ReleasePlayer";
|
||||||
|
import { tryCatchPlayer, tryCatchAPI } from "#/api/utils";
|
||||||
|
|
||||||
export const ReleasePlayerCustom = (props: {
|
export const ReleasePlayerCustom = (props: {
|
||||||
id: number;
|
id: number;
|
||||||
|
@ -38,157 +39,207 @@ export const ReleasePlayerCustom = (props: {
|
||||||
useCustom: false,
|
useCustom: false,
|
||||||
});
|
});
|
||||||
const [playbackRate, setPlaybackRate] = useState(1);
|
const [playbackRate, setPlaybackRate] = useState(1);
|
||||||
|
const [playerError, setPlayerError] = useState(null);
|
||||||
|
const [isErrorDetailsOpen, setIsErrorDetailsOpen] = useState(false);
|
||||||
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
|
|
||||||
const playerPreferenceStore = useUserPlayerPreferencesStore();
|
const playerPreferenceStore = useUserPlayerPreferencesStore();
|
||||||
const preferredVO = playerPreferenceStore.getPreferredVoiceover(props.id);
|
const preferredVO = playerPreferenceStore.getPreferredVoiceover(props.id);
|
||||||
const preferredSource = playerPreferenceStore.getPreferredPlayer(props.id);
|
const preferredSource = playerPreferenceStore.getPreferredPlayer(props.id);
|
||||||
|
|
||||||
const _fetchVoiceover = async (release_id: number) => {
|
async function _fetchAPI(
|
||||||
let url = `${ENDPOINTS.release.episode}/${release_id}`;
|
url: string,
|
||||||
if (props.token) {
|
onErrorMsg: string,
|
||||||
url += `?token=${props.token}`;
|
onErrorCodes?: Record<number, string>
|
||||||
}
|
) {
|
||||||
const response = await fetch(url);
|
const { data, error } = await tryCatchAPI(fetch(url));
|
||||||
const data = await response.json();
|
if (error) {
|
||||||
return data;
|
let errorDetail = "Мы правда не знаем что произошло...";
|
||||||
};
|
|
||||||
|
|
||||||
const _fetchSource = async (release_id: number, voiceover_id: number) => {
|
if (error.name) {
|
||||||
const response = await fetch(
|
if (error.name == "TypeError") {
|
||||||
`${ENDPOINTS.release.episode}/${release_id}/${voiceover_id}`
|
errorDetail = "Не удалось подключиться к серверу";
|
||||||
);
|
} else {
|
||||||
const data = await response.json();
|
errorDetail = `Неизвестная ошибка ${error.name}: ${error.message}`;
|
||||||
return data;
|
}
|
||||||
};
|
}
|
||||||
|
if (error.code) {
|
||||||
|
if (Object.keys(onErrorCodes).includes(error.code.toString())) {
|
||||||
|
errorDetail = onErrorCodes[error.code.toString()];
|
||||||
|
} else {
|
||||||
|
errorDetail = `API вернуло ошибку: ${error.code}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const _fetchEpisode = async (
|
setPlayerError({
|
||||||
release_id: number,
|
message: onErrorMsg,
|
||||||
voiceover_id: number,
|
detail: errorDetail,
|
||||||
source_id: number
|
});
|
||||||
) => {
|
return null;
|
||||||
let url = `${ENDPOINTS.release.episode}/${release_id}/${voiceover_id}/${source_id}`;
|
|
||||||
if (props.token) {
|
|
||||||
url += `?token=${props.token}`;
|
|
||||||
}
|
}
|
||||||
const response = await fetch(url);
|
|
||||||
const data = await response.json();
|
|
||||||
return data;
|
return data;
|
||||||
};
|
}
|
||||||
|
|
||||||
|
async function _fetchPlayer(url: string) {
|
||||||
|
const { data, error } = (await tryCatchPlayer(fetch(url))) as any;
|
||||||
|
if (error) {
|
||||||
|
let errorDetail = "Мы правда не знаем что произошло...";
|
||||||
|
|
||||||
|
if (error.name) {
|
||||||
|
if (error.name == "TypeError") {
|
||||||
|
errorDetail = "Не удалось подключиться к серверу";
|
||||||
|
} else {
|
||||||
|
errorDetail = `Неизвестная ошибка ${error.name}: ${error.message}`;
|
||||||
|
}
|
||||||
|
} else if (error.message) {
|
||||||
|
errorDetail = error.message;
|
||||||
|
}
|
||||||
|
|
||||||
|
setPlayerError({
|
||||||
|
message: "Не удалось получить ссылку на видео",
|
||||||
|
detail: errorDetail,
|
||||||
|
});
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
const _fetchKodikManifest = async (url: string) => {
|
const _fetchKodikManifest = async (url: string) => {
|
||||||
const response = await fetch(
|
const data = await _fetchPlayer(
|
||||||
`https://anix-player.wah.su/?url=${url}&player=kodik`
|
`https://anix-player.wah.su/?url=${url}&player=kodik`
|
||||||
);
|
);
|
||||||
const data = await response.json();
|
if (data) {
|
||||||
let lowQualityLink = data.links["360"][0].src;
|
let lowQualityLink = data.links["360"][0].src;
|
||||||
if (lowQualityLink.includes("https://")) {
|
if (lowQualityLink.includes("https://")) {
|
||||||
lowQualityLink = lowQualityLink.replace("https://", "//");
|
lowQualityLink = lowQualityLink.replace("https://", "//");
|
||||||
|
}
|
||||||
|
let manifest = `https:${lowQualityLink.replace("360.mp4:hls:", "")}`;
|
||||||
|
let poster = `https:${lowQualityLink.replace("360.mp4:hls:manifest.m3u8", "thumb001.jpg")}`;
|
||||||
|
|
||||||
|
if (lowQualityLink.includes("animetvseries")) {
|
||||||
|
let blobTxt = "#EXTM3U\n";
|
||||||
|
|
||||||
|
if (data.links.hasOwnProperty("240")) {
|
||||||
|
blobTxt += "#EXT-X-STREAM-INF:RESOLUTION=427x240,BANDWIDTH=200000\n";
|
||||||
|
!data.links["240"][0].src.startsWith("https:") ?
|
||||||
|
(blobTxt += `https:${data.links["240"][0].src}\n`)
|
||||||
|
: (blobTxt += `${data.links["240"][0].src}\n`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.links.hasOwnProperty("360")) {
|
||||||
|
blobTxt += "#EXT-X-STREAM-INF:RESOLUTION=578x360,BANDWIDTH=400000\n";
|
||||||
|
!data.links["360"][0].src.startsWith("https:") ?
|
||||||
|
(blobTxt += `https:${data.links["360"][0].src}\n`)
|
||||||
|
: (blobTxt += `${data.links["360"][0].src}\n`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.links.hasOwnProperty("480")) {
|
||||||
|
blobTxt += "#EXT-X-STREAM-INF:RESOLUTION=854x480,BANDWIDTH=596000\n";
|
||||||
|
!data.links["480"][0].src.startsWith("https:") ?
|
||||||
|
(blobTxt += `https:${data.links["480"][0].src}\n`)
|
||||||
|
: (blobTxt += `${data.links["480"][0].src}\n`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.links.hasOwnProperty("720")) {
|
||||||
|
blobTxt +=
|
||||||
|
"#EXT-X-STREAM-INF:RESOLUTION=1280x720,BANDWIDTH=1280000\n";
|
||||||
|
!data.links["720"][0].src.startsWith("https:") ?
|
||||||
|
(blobTxt += `https:${data.links["720"][0].src}\n`)
|
||||||
|
: (blobTxt += `${data.links["720"][0].src}\n`);
|
||||||
|
}
|
||||||
|
|
||||||
|
let file = new File([blobTxt], "manifest.m3u8", {
|
||||||
|
type: "application/x-mpegURL",
|
||||||
|
});
|
||||||
|
manifest = URL.createObjectURL(file);
|
||||||
|
}
|
||||||
|
return { manifest, poster };
|
||||||
}
|
}
|
||||||
let manifest = `https:${lowQualityLink.replace("360.mp4:hls:", "")}`;
|
return { manifest: null, poster: null };
|
||||||
let poster = `https:${lowQualityLink.replace("360.mp4:hls:manifest.m3u8", "thumb001.jpg")}`;
|
|
||||||
|
|
||||||
if (lowQualityLink.includes("animetvseries")) {
|
|
||||||
let blobTxt = "#EXTM3U\n";
|
|
||||||
|
|
||||||
if (data.links.hasOwnProperty("240")) {
|
|
||||||
blobTxt += "#EXT-X-STREAM-INF:RESOLUTION=427x240,BANDWIDTH=200000\n";
|
|
||||||
!data.links["240"][0].src.startsWith("https:") ?
|
|
||||||
(blobTxt += `https:${data.links["240"][0].src}\n`)
|
|
||||||
: (blobTxt += `${data.links["240"][0].src}\n`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.links.hasOwnProperty("360")) {
|
|
||||||
blobTxt += "#EXT-X-STREAM-INF:RESOLUTION=578x360,BANDWIDTH=400000\n";
|
|
||||||
!data.links["360"][0].src.startsWith("https:") ?
|
|
||||||
(blobTxt += `https:${data.links["360"][0].src}\n`)
|
|
||||||
: (blobTxt += `${data.links["360"][0].src}\n`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.links.hasOwnProperty("480")) {
|
|
||||||
blobTxt += "#EXT-X-STREAM-INF:RESOLUTION=854x480,BANDWIDTH=596000\n";
|
|
||||||
!data.links["480"][0].src.startsWith("https:") ?
|
|
||||||
(blobTxt += `https:${data.links["480"][0].src}\n`)
|
|
||||||
: (blobTxt += `${data.links["480"][0].src}\n`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.links.hasOwnProperty("720")) {
|
|
||||||
blobTxt += "#EXT-X-STREAM-INF:RESOLUTION=1280x720,BANDWIDTH=1280000\n";
|
|
||||||
!data.links["720"][0].src.startsWith("https:") ?
|
|
||||||
(blobTxt += `https:${data.links["720"][0].src}\n`)
|
|
||||||
: (blobTxt += `${data.links["720"][0].src}\n`);
|
|
||||||
}
|
|
||||||
|
|
||||||
let file = new File([blobTxt], "manifest.m3u8", {
|
|
||||||
type: "application/x-mpegURL",
|
|
||||||
});
|
|
||||||
manifest = URL.createObjectURL(file);
|
|
||||||
}
|
|
||||||
return { manifest, poster };
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const _fetchAnilibriaManifest = async (url: string) => {
|
const _fetchAnilibriaManifest = async (url: string) => {
|
||||||
const id = url.split("?id=")[1].split("&ep=")[0];
|
const id = url.split("?id=")[1].split("&ep=")[0];
|
||||||
|
const data = await _fetchPlayer(
|
||||||
|
`https://api.anilibria.tv/v3/title?id=${id}`
|
||||||
|
);
|
||||||
|
if (data) {
|
||||||
|
const host = `https://${data.player.host}`;
|
||||||
|
const ep = data.player.list[episode.selected.position];
|
||||||
|
|
||||||
const response = await fetch(`https://api.anilibria.tv/v3/title?id=${id}`);
|
const blobTxt = `#EXTM3U\n${ep.hls.sd && `#EXT-X-STREAM-INF:RESOLUTION=854x480,BANDWIDTH=596000\n${host}${ep.hls.sd}\n`}${ep.hls.hd && `#EXT-X-STREAM-INF:RESOLUTION=1280x720,BANDWIDTH=1280000\n${host}${ep.hls.hd}\n`}${ep.hls.fhd && `#EXT-X-STREAM-INF:RESOLUTION=1920x1080,BANDWIDTH=2560000\n${host}${ep.hls.fhd}\n`}`;
|
||||||
const data = await response.json();
|
let file = new File([blobTxt], "manifest.m3u8", {
|
||||||
|
type: "application/x-mpegURL",
|
||||||
const host = `https://${data.player.host}`;
|
});
|
||||||
const ep = data.player.list[episode.selected.position];
|
let manifest = URL.createObjectURL(file);
|
||||||
|
let poster = `https://anixart.libria.fun${ep.preview}`;
|
||||||
const blobTxt = `#EXTM3U\n${ep.hls.sd && `#EXT-X-STREAM-INF:RESOLUTION=854x480,BANDWIDTH=596000\n${host}${ep.hls.sd}\n`}${ep.hls.hd && `#EXT-X-STREAM-INF:RESOLUTION=1280x720,BANDWIDTH=1280000\n${host}${ep.hls.hd}\n`}${ep.hls.fhd && `#EXT-X-STREAM-INF:RESOLUTION=1920x1080,BANDWIDTH=2560000\n${host}${ep.hls.fhd}\n`}`;
|
return { manifest, poster };
|
||||||
let file = new File([blobTxt], "manifest.m3u8", {
|
}
|
||||||
type: "application/x-mpegURL",
|
return { manifest: null, poster: null };
|
||||||
});
|
|
||||||
let manifest = URL.createObjectURL(file);
|
|
||||||
let poster = `https://anixart.libria.fun${ep.preview}`;
|
|
||||||
return { manifest, poster };
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const _fetchSibnetManifest = async (url: string) => {
|
const _fetchSibnetManifest = async (url: string) => {
|
||||||
const response = await fetch(
|
const data = await _fetchPlayer(
|
||||||
`https://sibnet.anix-player.wah.su/?url=${url}`
|
`https://sibnet.anix-player.wah.su/?url=${url}`
|
||||||
);
|
);
|
||||||
const data = await response.json();
|
if (data) {
|
||||||
|
let manifest = data.video;
|
||||||
let manifest = data.video;
|
let poster = data.poster;
|
||||||
let poster = data.poster;
|
return { manifest, poster };
|
||||||
return { manifest, poster };
|
}
|
||||||
|
return { manifest: null, poster: null };
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const __getInfo = async () => {
|
const __getInfo = async () => {
|
||||||
const vo = await _fetchVoiceover(props.id);
|
let url = `${ENDPOINTS.release.episode}/${props.id}`;
|
||||||
const selectedVO =
|
if (props.token) {
|
||||||
vo.types.find((voiceover: any) => voiceover.name === preferredVO) ||
|
url += `?token=${props.token}`;
|
||||||
vo.types[0];
|
}
|
||||||
setVoiceover({
|
const vo = await _fetchAPI(
|
||||||
selected: selectedVO,
|
url,
|
||||||
available: vo.types,
|
"Не удалось получить информацию о озвучках",
|
||||||
});
|
{ 1: "Просмотр запрещён" }
|
||||||
|
);
|
||||||
|
if (vo) {
|
||||||
|
const selectedVO =
|
||||||
|
vo.types.find((voiceover: any) => voiceover.name === preferredVO) ||
|
||||||
|
vo.types[0];
|
||||||
|
setVoiceover({
|
||||||
|
selected: selectedVO,
|
||||||
|
available: vo.types,
|
||||||
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
__getInfo();
|
__getInfo();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const __getInfo = async () => {
|
const __getInfo = async () => {
|
||||||
const src = await _fetchSource(props.id, voiceover.selected.id);
|
let url = `${ENDPOINTS.release.episode}/${props.id}/${voiceover.selected.id}`;
|
||||||
const selectedSrc =
|
const src = await _fetchAPI(
|
||||||
src.sources.find((source: any) => source.name === preferredSource) ||
|
url,
|
||||||
src.sources[0];
|
"Не удалось получить информацию о источниках"
|
||||||
if (selectedSrc.episodes_count == 0) {
|
);
|
||||||
const remSources = src.sources.filter(
|
if (src) {
|
||||||
(source: any) => source.id !== selectedSrc.id
|
const selectedSrc =
|
||||||
);
|
src.sources.find((source: any) => source.name === preferredSource) ||
|
||||||
|
src.sources[0];
|
||||||
|
if (selectedSrc.episodes_count == 0) {
|
||||||
|
const remSources = src.sources.filter(
|
||||||
|
(source: any) => source.id !== selectedSrc.id
|
||||||
|
);
|
||||||
|
setSource({
|
||||||
|
selected: remSources[0],
|
||||||
|
available: remSources,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
setSource({
|
setSource({
|
||||||
selected: remSources[0],
|
selected: selectedSrc,
|
||||||
available: remSources,
|
available: src.sources,
|
||||||
});
|
});
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
setSource({
|
|
||||||
selected: selectedSrc,
|
|
||||||
available: src.sources,
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
if (voiceover.selected) {
|
if (voiceover.selected) {
|
||||||
__getInfo();
|
__getInfo();
|
||||||
|
@ -197,34 +248,38 @@ export const ReleasePlayerCustom = (props: {
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const __getInfo = async () => {
|
const __getInfo = async () => {
|
||||||
const episodes = await _fetchEpisode(
|
let url = `${ENDPOINTS.release.episode}/${props.id}/${voiceover.selected.id}/${source.selected.id}`;
|
||||||
props.id,
|
if (props.token) {
|
||||||
voiceover.selected.id,
|
url += `?token=${props.token}`;
|
||||||
source.selected.id
|
}
|
||||||
|
const episodes = await _fetchAPI(
|
||||||
|
url,
|
||||||
|
"Не удалось получить информацию о эпизодах"
|
||||||
);
|
);
|
||||||
|
if (episodes) {
|
||||||
|
let anonEpisodesWatched = getAnonEpisodesWatched(
|
||||||
|
props.id,
|
||||||
|
source.selected.id,
|
||||||
|
voiceover.selected.id
|
||||||
|
);
|
||||||
|
let lastEpisodeWatched = Math.max.apply(
|
||||||
|
0,
|
||||||
|
Object.keys(
|
||||||
|
anonEpisodesWatched[props.id][source.selected.id][
|
||||||
|
voiceover.selected.id
|
||||||
|
]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
let selectedEpisode =
|
||||||
|
episodes.episodes.find(
|
||||||
|
(episode: any) => episode.position == lastEpisodeWatched
|
||||||
|
) || episodes.episodes[0];
|
||||||
|
|
||||||
let anonEpisodesWatched = getAnonEpisodesWatched(
|
setEpisode({
|
||||||
props.id,
|
selected: selectedEpisode,
|
||||||
source.selected.id,
|
available: episodes.episodes,
|
||||||
voiceover.selected.id
|
});
|
||||||
);
|
}
|
||||||
let lastEpisodeWatched = Math.max.apply(
|
|
||||||
0,
|
|
||||||
Object.keys(
|
|
||||||
anonEpisodesWatched[props.id][source.selected.id][
|
|
||||||
voiceover.selected.id
|
|
||||||
]
|
|
||||||
)
|
|
||||||
);
|
|
||||||
let selectedEpisode =
|
|
||||||
episodes.episodes.find(
|
|
||||||
(episode: any) => episode.position == lastEpisodeWatched
|
|
||||||
) || episodes.episodes[0];
|
|
||||||
|
|
||||||
setEpisode({
|
|
||||||
selected: selectedEpisode,
|
|
||||||
available: episodes.episodes,
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
if (source.selected) {
|
if (source.selected) {
|
||||||
__getInfo();
|
__getInfo();
|
||||||
|
@ -237,36 +292,45 @@ export const ReleasePlayerCustom = (props: {
|
||||||
const { manifest, poster } = await _fetchKodikManifest(
|
const { manifest, poster } = await _fetchKodikManifest(
|
||||||
episode.selected.url
|
episode.selected.url
|
||||||
);
|
);
|
||||||
SetPlayerProps({
|
if (manifest) {
|
||||||
src: manifest,
|
SetPlayerProps({
|
||||||
poster: poster,
|
src: manifest,
|
||||||
useCustom: true,
|
poster: poster,
|
||||||
type: "hls",
|
useCustom: true,
|
||||||
});
|
type: "hls",
|
||||||
|
});
|
||||||
|
setIsLoading(false);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (source.selected.name == "Libria") {
|
if (source.selected.name == "Libria") {
|
||||||
const { manifest, poster } = await _fetchAnilibriaManifest(
|
const { manifest, poster } = await _fetchAnilibriaManifest(
|
||||||
episode.selected.url
|
episode.selected.url
|
||||||
);
|
);
|
||||||
SetPlayerProps({
|
if (manifest) {
|
||||||
src: manifest,
|
SetPlayerProps({
|
||||||
poster: poster,
|
src: manifest,
|
||||||
useCustom: true,
|
poster: poster,
|
||||||
type: "hls",
|
useCustom: true,
|
||||||
});
|
type: "hls",
|
||||||
|
});
|
||||||
|
setIsLoading(false);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (source.selected.name == "Sibnet") {
|
if (source.selected.name == "Sibnet") {
|
||||||
const { manifest, poster } = await _fetchSibnetManifest(
|
const { manifest, poster } = await _fetchSibnetManifest(
|
||||||
episode.selected.url
|
episode.selected.url
|
||||||
);
|
);
|
||||||
SetPlayerProps({
|
if (manifest) {
|
||||||
src: manifest,
|
SetPlayerProps({
|
||||||
poster: poster,
|
src: manifest,
|
||||||
useCustom: true,
|
poster: poster,
|
||||||
type: "mp4",
|
useCustom: true,
|
||||||
});
|
type: "mp4",
|
||||||
|
});
|
||||||
|
setIsLoading(false);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
SetPlayerProps({
|
SetPlayerProps({
|
||||||
|
@ -275,6 +339,7 @@ export const ReleasePlayerCustom = (props: {
|
||||||
useCustom: false,
|
useCustom: false,
|
||||||
type: null,
|
type: null,
|
||||||
});
|
});
|
||||||
|
setIsLoading(false);
|
||||||
};
|
};
|
||||||
if (episode.selected) {
|
if (episode.selected) {
|
||||||
__getInfo();
|
__getInfo();
|
||||||
|
@ -283,31 +348,47 @@ export const ReleasePlayerCustom = (props: {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card className="aspect-video min-h-min-h-[300px] sm:min-h-[466px] md:min-h-[540px] lg:min-h-[512px] xl:min-h-[608px] 2xl:min-h-[712px]">
|
<Card className="aspect-video min-h-min-h-[300px] sm:min-h-[466px] md:min-h-[540px] lg:min-h-[512px] xl:min-h-[608px] 2xl:min-h-[712px]">
|
||||||
{(
|
<div className="flex flex-wrap gap-4">
|
||||||
!voiceover.selected ||
|
{voiceover.selected && (
|
||||||
!source.selected ||
|
<VoiceoverSelector
|
||||||
!episode.selected ||
|
availableVoiceover={voiceover.available}
|
||||||
!playerProps.src
|
voiceover={voiceover.selected}
|
||||||
) ?
|
setVoiceover={setVoiceover}
|
||||||
<div className="flex items-center justify-center w-full aspect-video">
|
release_id={props.id}
|
||||||
<Spinner />
|
/>
|
||||||
</div>
|
)}
|
||||||
: <div className="flex flex-col gap-4">
|
{source.selected && (
|
||||||
<div className="flex flex-wrap gap-4">
|
<SourceSelector
|
||||||
<VoiceoverSelector
|
availableSource={source.available}
|
||||||
availableVoiceover={voiceover.available}
|
source={source.selected}
|
||||||
voiceover={voiceover.selected}
|
setSource={setSource}
|
||||||
setVoiceover={setVoiceover}
|
release_id={props.id}
|
||||||
release_id={props.id}
|
/>
|
||||||
/>
|
)}
|
||||||
<SourceSelector
|
</div>
|
||||||
availableSource={source.available}
|
|
||||||
source={source.selected}
|
<div className="flex items-center justify-center w-full h-full">
|
||||||
setSource={setSource}
|
{isLoading ?
|
||||||
release_id={props.id}
|
!playerError ?
|
||||||
/>
|
<Spinner />
|
||||||
</div>
|
: <div className="flex flex-col gap-2">
|
||||||
{playerProps.useCustom ?
|
<p className="text-lg font-bold">Ошибка: {playerError.message}</p>
|
||||||
|
{!isErrorDetailsOpen ?
|
||||||
|
<Button
|
||||||
|
color="light"
|
||||||
|
size="xs"
|
||||||
|
onClick={() => setIsErrorDetailsOpen(true)}
|
||||||
|
>
|
||||||
|
Подробнее
|
||||||
|
</Button>
|
||||||
|
: <p className="text-gray-600 dark:text-gray-100">
|
||||||
|
{playerError.detail}
|
||||||
|
</p>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
: playerProps.useCustom ?
|
||||||
|
!playerError ?
|
||||||
<MediaThemeSutro className="object-none w-full aspect-video">
|
<MediaThemeSutro className="object-none w-full aspect-video">
|
||||||
{playerProps.type == "hls" ?
|
{playerProps.type == "hls" ?
|
||||||
<HlsVideo
|
<HlsVideo
|
||||||
|
@ -334,7 +415,27 @@ export const ReleasePlayerCustom = (props: {
|
||||||
></VideoJS>
|
></VideoJS>
|
||||||
}
|
}
|
||||||
</MediaThemeSutro>
|
</MediaThemeSutro>
|
||||||
: <iframe src={playerProps.src} className="w-full aspect-video" />}
|
: <div className="flex flex-col gap-2">
|
||||||
|
<p className="text-lg font-bold">Ошибка: {playerError.message}</p>
|
||||||
|
{!isErrorDetailsOpen ?
|
||||||
|
<Button
|
||||||
|
color="light"
|
||||||
|
size="xs"
|
||||||
|
onClick={() => setIsErrorDetailsOpen(true)}
|
||||||
|
>
|
||||||
|
Подробнее
|
||||||
|
</Button>
|
||||||
|
: <p className="text-gray-600 dark:text-gray-100">
|
||||||
|
{playerError.detail}
|
||||||
|
</p>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
: <iframe src={playerProps.src} className="w-full aspect-video" />}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
{episode.selected && source.selected && voiceover.selected && (
|
||||||
<EpisodeSelector
|
<EpisodeSelector
|
||||||
availableEpisodes={episode.available}
|
availableEpisodes={episode.available}
|
||||||
episode={episode.selected}
|
episode={episode.selected}
|
||||||
|
@ -344,8 +445,8 @@ export const ReleasePlayerCustom = (props: {
|
||||||
voiceover={voiceover.selected}
|
voiceover={voiceover.selected}
|
||||||
token={props.token}
|
token={props.token}
|
||||||
/>
|
/>
|
||||||
</div>
|
)}
|
||||||
}
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
Loading…
Add table
Reference in a new issue