import React, { useEffect } from "react"; import Head from "next/head"; import ChatReplayPanel from "../modules/ChatReplay/ChatReplayPanel"; import { useWindowSize } from "../modules/hooks/useWindowSize"; import PageBase from "../modules/PageBase"; import VideoPlayer2 from "../modules/VideoPlayer/VideoPlayer2"; import { buttonStyle } from "../modules/VideoActionButtons"; import { IconCheck } from "../modules/icons"; import { useRouter } from "next/router"; import Linkify from "linkify-react"; const CustomPlayerPage = () => { const router = useRouter(); const [isChatVisible, setIsChatVisible] = React.useState(false); const { innerWidth, innerHeight } = useWindowSize(); const [playbackProgress, setPlaybackProgress] = React.useState(0); const [urlVideo, setUrlVideo] = React.useState(""); const [urlChat, setUrlChat] = React.useState(""); const [urlYtt, setUrlYtt] = React.useState(""); const [captionFormat, setCaptionFormat] = React.useState<"srv3" | "srt" | undefined>(undefined); const [infoJson, setInfoJson] = React.useState({} as any); const [showPlayer, setShowPlayer] = React.useState(false); const [dataSet, setDataSet] = React.useState(false); const [base64Input, setBase64Input] = React.useState(""); const { query } = router; const jsonDataUrl = query.data as string; const timeSeconds = query.time as string; useEffect(() => { if (timeSeconds) { setPlaybackProgress(parseFloat(timeSeconds)); } }, [timeSeconds]); const applyJsonData = (data: any) => { const { info, video, chat, srv3, srt, captions } = data; if (info) { fetch(info) .then((res) => res.json()) .then((data) => { setInfoJson(data); }); } if (video) { setUrlVideo(video); } if (chat) { setUrlChat(chat); } if (srv3) { setUrlYtt(srv3); setCaptionFormat("srv3"); } else if (srt) { setUrlYtt(srt); setCaptionFormat("srt"); } else if (captions) { const cap = Array.isArray(captions) ? captions[0] : captions; if (cap?.src) { setUrlYtt(cap.src); setCaptionFormat(cap.format || (cap.src.toLowerCase().endsWith(".srt") ? "srt" : "srv3")); } } setDataSet(true); setShowPlayer(true); }; if (jsonDataUrl && !dataSet) { fetch(jsonDataUrl) .then((res) => res.json()) .then((data) => applyJsonData(data)) .catch((error) => { console.error("Error fetching JSON data:", error); setDataSet(true); setShowPlayer(true); }); } const handleLoadBase64 = () => { try { let trimmed = base64Input.trim(); const base64Match = trimmed.match(/^data:[^;]+;base64,(.*)$/); if (base64Match) { trimmed = base64Match[1]; } const jsonStr = atob(trimmed); const data = JSON.parse(jsonStr); applyJsonData(data); } catch (error) { console.error("Error decoding base64 JSON:", error); setDataSet(true); setShowPlayer(true); } }; const handleFile = ( e: React.ChangeEvent, setter: (newValue: string) => any, opts?: { onFormat?: (format: "srv3" | "srt") => void } ) => { const file = e.target.files?.[0]; if (!file) { console.error("No file selected"); return false; } const url = URL.createObjectURL(file).toString(); console.log(url); if (opts?.onFormat) { const name = file.name.toLowerCase(); opts.onFormat(name.endsWith(".srt") ? "srt" : "srv3"); } setter(url); }; return ( { infoJson && infoJson.title ? ( {infoJson.title} ) : a very nice video } {showPlayer ? (
{!urlChat ? (

Chat replay unavailable

) : ( )}
{infoJson && infoJson.description ? (

{infoJson.title}

{infoJson.view_count.toLocaleString()} views ·{" "} {infoJson.upload_date.slice(0, 4) + "-" + infoJson.upload_date.slice(4, 6) + "-" + infoJson.upload_date.slice(6, 8)}

{infoJson.uploader}

Description

{infoJson.description}
) : null} {urlVideo ? ( ) : null}
) : (

Moekyun Video Player

You can play locally-saved video files and chat replay JSON

Chat replay compatible with output from{" "} yt-dlp {" and "} chat-downloader .


This is a specialized version of the player that allows you to pass in a JSON data file.