import ss from '../misc.module.css'; import style from './misc.module.css'; import {BackButton, InfoButton, PlayButton, Template} from "../../buttons/Buttons"; import {useRecoilState, useRecoilValue} from "recoil"; import React, {useEffect, useState} from "react"; import cd from "../frames.module.css"; import usePlayback, { differance, displaySidesAtom, FramesInformAtom, framesPlayer, framesPlayerStateSelector, framesSubtitlesAtom, framesVideoStateAtom, shareAndDownloadAtom, SubtitlesAndUpNextAtom, SubtitlesAtom, UpNextAtom, VideoListener } from "../../../../utils/playback"; import useCast, {VideoStateAtom} from "../../../../utils/castContext"; import {useAuth} from "../../../../utils/userTools"; import {useBasics, useClipboard, useEventListener} from "../../../../utils/customHooks"; import Media from "../../entities/singleEntity/media"; import styles from "../../buttons/Button.module.css"; import {AlreadyStreamingAtom, useNotification} from "../../../../utils/notificationConext"; import {useBase} from "../../../../utils/Providers"; import {Link} from "../../misc/Loader"; import {ManagePlaylist} from "../../misc/editPlaylist"; export const Toppers = () => { const response = useRecoilValue(framesVideoStateAtom); if (!response) return null; return ( <> <BackButton response={response}/> <div className={ss.ci}> {response?.logo ? <img src={response?.logo || ''} alt={response?.name}/> : <span>{response?.name}</span>} {response?.episodeName ? <div className={ss.ep}>{response?.episodeName}</div> : null} </div> </> ) } export const Buffer = () => { const state = useRecoilValue(framesPlayerStateSelector); return ( <div className={ss.vd} style={state === 'BUFFERING' ? {visibility: "visible"} : {visibility: "hidden"}}> <div className={ss.buffer}>Loading...</div> </div> ) } export const Overview = () => { const response = useRecoilValue(framesVideoStateAtom); const display = useRecoilValue(displaySidesAtom).info; const paused = useRecoilValue(framesPlayerStateSelector) === 'PAUSED'; return ( <div className={paused && display ? ss.vi : `${ss.hide} ${ss.vi}`}> <div className={paused && display ? `${ss.slideRight} ${ss.inf}` : `${ss.slideLeft} ${ss.inf}`}> <label className={ss.title}>{response?.name}</label> <div className={ss.spacer}/> <span className={ss.epi}>{response?.episodeName}</span> <div className={ss.spacer}/> <span className={ss.ovr}>{response?.overview}</span> </div> </div> ) } export const ShareFrame = () => { const {copy} = useClipboard(); const base = useBase(); const {getBaseUrl} = useBasics(); const data = useRecoilValue(framesVideoStateAtom); const [value, setValue] = useRecoilState(shareAndDownloadAtom); const {current, duration} = useRecoilValue(FramesInformAtom); const [frame, setFrame] = useState(base.generateKey(13, 1) || ''); const [shareUrl, setShareUrl] = useState(`${getBaseUrl()}/frame=${frame}`); const sendZero = async (inform: boolean) => { if (frame === '' && base) { setFrame(base.generateKey(13, 1)); setShareUrl(`${getBaseUrl()}/frame=${frame}`); } if (frame !== '' && base) { await copy(shareUrl, 'Video url copied successfully'); let position = 0; if (inform) position = Math.ceil((current / duration) * 1000); await base.makeRequest('/api/stream/genCypher', { cypher: frame, position, auth: data?.location }, 'POST'); setFrame(base.generateKey(13, 1)); setShareUrl(`${getBaseUrl()}/frame=${frame}`); } } return ( <div className={style.sc} style={value.share ? {left: '2vw', visibility: "visible"} : { left: '2vw', opacity: 0, visibility: "hidden" }} onClick={e => e.stopPropagation()}> <div className={style.sm}> <span>Share from current position ?</span> <input type="text" value={shareUrl} className={style.so} readOnly/> </div> <ul className={style.sl} onClick={() => setValue({...value, share: false})}> <li className={style.accept} onClick={() => sendZero(true)}>yes</li> <li className={style.reject} onClick={() => sendZero(false)}>no</li> </ul> </div> ) } export const DownloadHandler = () => { const base = useBase(); const data = useRecoilValue(framesVideoStateAtom); const [value, setValue] = useRecoilState(shareAndDownloadAtom); const {auth, setAuth, authError, valid, error} = useAuth(); useEventListener('keyup', async event => { if (event.code === 'Enter') await handleClick(); }) const handleClick = async () => { if (valid && base) { setAuth(''); const path = await base.makeRequest<{ location: string | null }>('/api/stream/getDown', { authKey: auth, auth: data?.location }, 'POST'); if (path?.location) window.location.href = data?.cdn + path.location; } } return ( <div className={style.sc} style={value.download ? {left: '10vw', visibility: "visible"} : { left: '10vw', opacity: 0, visibility: "hidden" }} onClick={e => e.stopPropagation()}> <div className={style.sm}> <span style={error ? {color: "rgba(245, 78, 78, .9)"} : {}}>{error ? error : !valid ? 'enter an auth key' : 'auth key accepted'}</span> <input style={valid ? {borderColor: "#3cab66d0"} : authError ? {borderColor: "rgba(245, 78, 78, .9)"} : {}} className={style.gwm} value={auth} maxLength={24} onChange={e => setAuth(e.currentTarget.value)}/> </div> <ul className={style.sl} onClick={() => setValue({...value, download: false})}> <li className={style.accept} onClick={handleClick}>download</li> <li className={style.reject}>dismiss</li> </ul> </div> ) } export const CastHolder = () => { const response = useRecoilValue(framesVideoStateAtom); const move = useRecoilValue(displaySidesAtom).controls; const diff = useRecoilValue(differance); const player = useRecoilValue(framesPlayer); const [state, setState] = useRecoilState(VideoStateAtom); const {connected, device, castMedia} = useCast(); useEffect(() => { if (connected && response && player) { castMedia({ currentTime: player.currentTime, src: response.cdn + response.location, paused: player.paused, volume: player.volume }, { backdrop: response.backdrop, location: response.cdn + response.location, name: response.name, logo: response.logo }); player.muted = true; player.pause(); } else if (!connected && player) { player.muted = false; player.currentTime = state?.time || player.currentTime; player.play(); setState(null); } }, [connected, castMedia, response, player]); if (connected) return ( <> <img style={{display: "block"}} className={diff ? cd.count : cd.pf} src={response?.backdrop} alt={response?.name}/> <div className={diff ? cd.count : style.ch}> {diff ? <div> <svg viewBox="0 0 24 24" className={style.diffSvg}> <path d="M2 16.1A5 5 0 0 1 5.9 20M2 12.05A9 9 0 0 1 9.95 20M2 8V6a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2h-6"/> <line x1="2" y1="20" x2="2.01" y2="20"/> </svg> <br/> <span>Currently casting to: {device}</span> </div> : <div className={style.cHold} style={move ? {bottom: '12vh'} : {}}> <div className={ss.ci}> {response?.logo ? <img src={response?.logo || ''} alt={response?.name}/> : <span>{response?.name}</span>} </div> <svg viewBox="0 0 24 24"> <path d="M2 16.1A5 5 0 0 1 5.9 20M2 12.05A9 9 0 0 1 9.95 20M2 8V6a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2h-6"/> <line x1="2" y1="20" x2="2.01" y2="20"/> </svg> <ul> <li style={{fontSize: "20px"}}> Currently casting {response?.episodeName || response?.name}</li> <li style={{fontSize: "small"}}>to: {device}</li> </ul> </div>} </div> </> ) else return null; } export const UpNextHolder = () => { const response = useRecoilValue(framesVideoStateAtom); const diff = useRecoilValue(differance); const data = useRecoilValue(UpNextAtom); if (data && response) return ( <> <img src={data.backdrop} className={cd.pf} alt={data.episodeName || data.name}/> <div className={cd.upHldr}/> <div className={cd.hldr}> <BackButton response={response}/> {data.logo ? <img className={cd.lgo} src={data.logo} alt={data.episodeName || data.name}/> : <div className={cd.nm}>{data.name}</div> } <div className={cd.epi}>Up next: {data.episodeName || data.name}</div> <p>{data.overview}</p> <div className={cd.but}> <PlayButton id={data.mediaId} name={'plays in: ' + diff} url={data.location}/> <InfoButton id={data.mediaId} name={data.name} type={data.episodeName ? 'show' : 'movie'}/> </div> </div> </> ) else return null; } export const AlreadyStreaming = () => { const {signOutEveryWhere} = useNotification(); const data = useRecoilValue(AlreadyStreamingAtom); if (data) return ( <> <img src={data.backdrop} className={cd.pf} alt={data.episodeName || data.name}/> <div className={cd.upHldr}/> <div className={cd.hldr}> <Link href={'/'}> <svg className={styles.bb} viewBox="0 0 512 512"> <path d="M256,0C114.844,0,0,114.844,0,256s114.844,256,256,256s256-114.844,256-256S397.156,0,256,0z M256,490.667 C126.604,490.667,21.333,385.396,21.333,256S126.604,21.333,256,21.333S490.667,126.604,490.667,256S385.396,490.667,256,490.667 z"/> <path d="M394.667,245.333H143.083l77.792-77.792c4.167-4.167,4.167-10.917,0-15.083c-4.167-4.167-10.917-4.167-15.083,0l-96,96 c-4.167,4.167-4.167,10.917,0,15.083l96,96c2.083,2.083,4.813,3.125,7.542,3.125c2.729,0,5.458-1.042,7.542-3.125 c4.167-4.167,4.167-10.917,0-15.083l-77.792-77.792h251.583c5.896,0,10.667-4.771,10.667-10.667S400.563,245.333,394.667,245.333 z"/> </svg> </Link> {data.logo ? <img className={cd.lgo} src={data.logo} alt={data.episodeName || data.name}/> : <div className={cd.nm}>{data.name}</div> } <div className={cd.epi}>Currently watching: {data.episodeName || data.name}</div> <p>Someone is currently streaming {data.episodeName || data.name} on another device. To continue streaming your favorite media, consider creating a new account, it is free!</p> <div className={cd.but}> <Link href={'/'}> <Template id={0} type={'none'} name={'return home'}/> </Link> <Template id={1} type={'info'} name={'sign out everywhere'} onClick={signOutEveryWhere}/> </div> </div> </> ) else return null; } export const Subtitles = () => { const [within, setWithin] = useState(false); const response = useRecoilValue(framesVideoStateAtom); const activeSub = useRecoilValue(framesSubtitlesAtom).activeSub; const sub = useRecoilValue(SubtitlesAndUpNextAtom).subtitles; const {switchLanguage} = usePlayback(); return ( <div onClick={(event) => event.stopPropagation()} onMouseEnter={() => setWithin(true)} onMouseLeave={() => setWithin(false)} className={ss.subH} style={sub || within ? {opacity: 1} : {visibility: "hidden"}}> <div className={`${ss.subE} ${activeSub === 'none' ? ss.ctv : ''}`} onClick={() => switchLanguage('none')}>None </div> {response && response.subs ? response.subs.map((e, v) => <Sub sub={e} key={v}/> ) : null} </div> ) } const Sub = ({sub}: { sub: { language: string, url: string } }) => { const activeSub = useRecoilValue(framesSubtitlesAtom).activeSub; const {switchLanguage} = usePlayback(); if (sub && sub.url !== '') return ( <> <div className={ss.subD}/> <div className={`${ss.subE} ${activeSub === sub.language ? ss.ctv : ''}`} onClick={() => switchLanguage(sub.language)}>{sub.language}</div> </> ) else return null; } export const UpNextMini = () => { const upNext = useRecoilValue(UpNextAtom); const next = useRecoilValue(SubtitlesAndUpNextAtom).upNext; const {playNext} = usePlayback(); if (upNext) return ( <div className={ss.un} style={next ? {opacity: "1"} : {visibility: "hidden"}} onClick={playNext}> <Media data={{...upNext, id: upNext.mediaId, type: 'SHOW'}} media={true}/> <div className={ss.p}><p>{upNext.overview}</p></div> </div> ) else return null; } export const Modals = () => { return ( <> <VideoListener/> <ManagePlaylist/> </> ) } export const SubDisplay = () => { const sub = useRecoilValue(SubtitlesAtom); if (sub) return ( <div className={cd.subs} style={sub.move ? {bottom: '20vh'} : {}}> <div style={sub.style}>{sub.display}</div> </div> ) else return null; }