"use client"; import Styles from "./MediaPlayer.module.css"; import { Button, Card } from "flowbite-react"; import { useEffect, useState } from "react"; import { MediaController, MediaControlBar, MediaTimeRange, MediaTimeDisplay, MediaVolumeRange, MediaPlayButton, MediaSeekForwardButton, MediaMuteButton, MediaFullscreenButton, MediaCastButton, MediaPreviewTimeDisplay, MediaPipButton, MediaAirplayButton, MediaChromeDialog, MediaLoadingIndicator, MediaSeekBackwardButton, MediaChromeButton, } from "media-chrome/react"; import { MediaPlaybackRateMenu, MediaRenditionMenu, MediaSettingsMenu, MediaSettingsMenuButton, MediaSettingsMenuItem, } from "media-chrome/react/menu"; import { VoiceoverSelectorMenu } from "./VoiceoverSelectorMenu"; import { SourceSelectorMenu } from "./SourceSelectorMenu"; import { _fetchAnilibriaManifest, _fetchKodikManifest, _fetchSibnetManifest, } from "./PlayerParsing"; import { Episode, EpisodeSelectorMenu } from "./EpisodeSelectorMenu"; import HlsVideo from "hls-video-element/react"; import VideoJS from "videojs-video-element/react"; import { ENDPOINTS } from "#/api/config"; import { getAnonEpisodesWatched, saveAnonEpisodeWatched, } from "./ReleasePlayer"; import { usePreferencesStore } from "#/store/preferences"; export const ReleasePlayerCustom = (props: { id: number; title: string; token: string | null; }) => { const [voiceover, setVoiceover] = useState({ selected: null, available: null, }); const [source, setSource] = useState({ selected: null, available: null, }); const [episode, setEpisode] = useState({ selected: null, available: null, }); const [playerProps, SetPlayerProps] = useState<{ src: string | null; poster: string | null; type: "hls" | "mp4" | null; }>({ src: null, poster: null, type: null, }); const [playerError, setPlayerError] = useState(null); const [playbackRate, setPlaybackRate] = useState(1); const [isErrorDetailsOpen, setIsErrorDetailsOpen] = useState(false); const [isEpLoadingTimeout, setIsEpLoadingTimeout] = useState(null); const [retryCount, setRetryCount] = useState(0); const preferenceStore = usePreferencesStore(); useEffect(() => { const __getInfo = async () => { if (source.selected.name == "Kodik") { const { manifest, poster } = await _fetchKodikManifest( episode.selected.url, setPlayerError ); if (manifest) { SetPlayerProps({ src: manifest, poster: poster, type: "hls", }); } return; } if (source.selected.name == "Libria") { const { manifest, poster } = await _fetchAnilibriaManifest( episode.selected.url, setPlayerError ); if (manifest) { SetPlayerProps({ src: manifest, poster: poster, type: "hls", }); } return; } if (source.selected.name == "Sibnet") { const { manifest, poster } = await _fetchSibnetManifest( episode.selected.url, setPlayerError ); if (manifest) { SetPlayerProps({ src: manifest, poster: poster, type: "mp4", }); } return; } setPlayerError({ message: `Источник "${source.selected.name}" не поддерживается`, detail: null, }); }; if (episode.selected) { if (isEpLoadingTimeout) { clearTimeout(isEpLoadingTimeout); } setPlayerError(null); SetPlayerProps({ src: null, poster: null, type: null, }); setIsEpLoadingTimeout( setTimeout(() => { __getInfo(); }, 250) ); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [episode.selected, retryCount]); useEffect(() => { if (document && document.querySelector("media-chrome-dialog")) { document .querySelector("media-chrome-dialog") .shadowRoot.querySelector("slot").style.width = "100%"; } }, []); function saveEpisodeToHistory() { if (props.id && source.selected && voiceover.selected && episode.selected) { const anonEpisodesWatched = getAnonEpisodesWatched( props.id, source.selected.id, voiceover.selected.id ); if ( preferenceStore.flags.saveWatchHistory && !episode.selected.is_watched && !Object.keys( anonEpisodesWatched[props.id][source.selected.id][ voiceover.selected.id ] ).includes(episode.selected.position.toString()) ) { const objectToReplace = episode.available.find( (arrayItem: Episode) => arrayItem.position === episode.selected.position ); const newObject = { ...episode.selected, is_watched: true, }; Object.assign(objectToReplace, newObject); saveAnonEpisodeWatched( props.id, source.selected.id, voiceover.selected.id, episode.selected.position ); if (props.token) { fetch( `${ENDPOINTS.statistic.addHistory}/${props.id}/${source.selected.id}/${episode.selected.position}?token=${props.token}` ); fetch( `${ENDPOINTS.statistic.markWatched}/${props.id}/${source.selected.id}/${episode.selected.position}?token=${props.token}` ); } } } } function _prevEpisode() { if (!episode.available || !episode.selected) return; const curEpIdx = episode.available.findIndex( (el) => el.position == episode.selected.position ); if (curEpIdx == -1 || curEpIdx == 0 || episode.available.length == 0) return; setEpisode({ selected: episode.available[curEpIdx - 1], available: episode.available, }); } function _nextEpisode() { if (!episode.available || !episode.selected) return; const curEpIdx = episode.available.findIndex( (el) => el.position == episode.selected.position ); if ( curEpIdx == -1 || episode.available.length == 0 || curEpIdx == episode.available.length - 1 ) return; setEpisode({ selected: episode.available[curEpIdx + 1], available: episode.available, }); } return (
saveEpisodeToHistory()} > {playerProps.type == "hls" && playerProps.src && ( { // @ts-ignore setPlaybackRate(e.target.playbackRate || 1); }} /> )} {playerProps.type == "mp4" && playerProps.src && ( { // @ts-ignore setPlaybackRate(e.target.playbackRate || 1); }} > )} {playerProps.type == null || playerProps.src == null ? <> {/* */} {!playerError && ( )} : !playerError ? : ""} {playerError ? <>

{playerError.message}

{!isErrorDetailsOpen ? :

{playerError.detail}

} {voiceover.selected && source.selected && episode.selected ? : ""}
: ""}

{props.title}

{voiceover.selected ?

{voiceover.selected.name}

: ""} {episode.selected ?

|{" "} {episode.selected.name ? episode.selected.name : ["Sibnet"].includes(source.selected.name) ? `${episode.selected.position + 1} Серия` : `${episode.selected.position} Серия`}

: ""}
{!voiceover.selected && !source.selected && !episode.selected ?

Нет доступных вариантов для выбора

: ""}

Выбор доступен только в горизонтальном режиме

_prevEpisode()} >
Предыдущая серия
Воспроизвести
Пауза
_nextEpisode()} >
Следующая серия
Вернуться на 10 секунд
Пропустить 10 секунд
Пропустить 1.5 минуты
Выбор озвучки, источника и серии
Выбор озвучки, источника и серии
Настройки
Настройки
Включить PiP
Выключить PiP
Включить трансляцию через Airplay
Остановить трансляцию через Airplay
Включить трансляцию через Google Cast
Остановить трансляцию через Google Cast
Войти в полный экран
Выйти из полного экрана
); };