add WatchPage customization

This commit is contained in:
Lucy 2025-03-07 17:41:04 +00:00
parent 62d10aa608
commit 26d4b99d2d
2 changed files with 254 additions and 0 deletions

View file

@ -1 +1,3 @@
currently only supports a ``--debug``` flag
WatchpPage.tsx is a customized version of https://github.com/ragtag-archive/archive-browser/blob/master/modules/WatchPage.tsx to support webm delivery for Firefox Users - is wonky but works (kinda).

252
WatchPage.tsx Normal file
View file

@ -0,0 +1,252 @@
import React from "react";
import Image from "next/image";
import PageBase from "./shared/PageBase";
import { VideoMetadata } from "./shared/database.d";
import { formatDate } from "./shared/format";
import ChatReplayPanel from "./shared/ChatReplay/ChatReplayPanel";
import VideoCard from "./shared/VideoCard";
import Link from "next/link";
import ServiceUnavailablePage from "./ServiceUnavailablePage";
import VideoActionButtons from "./shared/VideoActionButtons";
import { useWindowSize } from "./shared/hooks/useWindowSize";
import MemoLinkify from "./shared/MemoLinkify";
import VideoPlayerHead from "./shared/VideoPlayerHead";
import ClientRender from "./shared/ClientRender";
import VideoPlayer2 from "./shared/VideoPlayer/VideoPlayer2";
import ExpandableContainer from "./ExpandableContainer";
import CommentSection from "./CommentSection";
const format = (n: number) => Intl.NumberFormat("en-US").format(n);
export type WatchPageProps = {
videoInfo: VideoMetadata;
hasChat: boolean;
relatedVideos: VideoMetadata[];
channelVideoCount: number;
channelProfileURL: string;
};
const getFile = (videoInfo: VideoMetadata, suffix: string) =>
videoInfo.files.find((file) => file.name.includes(suffix))?.url;
const WatchPage = (props: WatchPageProps) => {
if (!props.videoInfo) return <ServiceUnavailablePage />;
const { videoInfo, hasChat, relatedVideos } = props;
const [isChatVisible, setIsChatVisible] = React.useState(false);
const refMobileScrollTarget = React.useRef<HTMLDivElement>(null);
const { innerWidth, innerHeight } = useWindowSize();
React.useEffect(() => {
if (isChatVisible)
refMobileScrollTarget.current?.scrollIntoView({ behavior: "smooth" });
}, [isChatVisible]);
React.useEffect(() => {
if (!videoInfo || window.location.host.startsWith("localhost")) return;
fetch(
"/api/pv?channel_id=" +
videoInfo.channel_id +
"&video_id=" +
videoInfo.video_id
);
}, [videoInfo]);
const [playbackProgress, setPlaybackProgress] = React.useState(0);
/*const [fmtVideo, fmtAudio] = videoInfo.format_id.split("+");
const urlVideo = getFile(videoInfo, ".f" + fmtVideo);
const urlAudio = getFile(videoInfo, ".f" + fmtAudio);*/
const urlVideo = getFile(videoInfo, ".mkv");
const urlAudio = '';
// srcAudio={urlAudio}
const urlThumb = getFile(videoInfo, ".webp") || getFile(videoInfo, ".jpg");
const urlChat = getFile(videoInfo, ".chat.json");
const urlInfo = getFile(videoInfo, ".info.json");
// Function to check if the user agent is Firefox
const isFirefox = typeof window !== 'undefined' && navigator.userAgent.includes('Firefox');
// Update the video URL if it's Firefox and the URL ends with .mkv
let updatedVideoUrl = urlVideo;
if (isFirefox && urlVideo.endsWith('.mkv')) {
// For Firefox, rewrite to .webm and prepend /convert/
updatedVideoUrl = urlVideo.replace('/vpath/', '/convert/vpath/').replace('.mkv', '.webm');
}
React.useEffect(() => {
if (typeof window !== 'undefined') {
console.log('User Agent:', navigator.userAgent);
console.log('Is Firefox:', isFirefox);
console.log('Updated Video URL because Firefox beeing special (https://bugzilla.mozilla.org/show_bug.cgi?id=1422891):', updatedVideoUrl);
}
}, []);
return (
<PageBase>
<VideoPlayerHead videoInfo={videoInfo} />
<div
className={["flex lg:flex-row flex-col lg:h-auto"].join(" ")}
style={{
height: isChatVisible && innerWidth < 640 ? innerHeight : "auto",
}}
>
<div className="w-full lg:w-3/4">
<div className="lg:absolute lg:top-0" ref={refMobileScrollTarget} />
<div
className="relative bg-gray-400 h-0"
style={{ paddingBottom: "56.25%" }}
>
<div className="absolute inset-0 w-full h-full">
<VideoPlayer2
key={updatedVideoUrl}
videoId={videoInfo.id}
srcVideo={updatedVideoUrl}
srcPoster={urlThumb}
captions={videoInfo.files
.filter((file) => file.name.endsWith(".ytt"))
.map(({ name }) => {
const lang = name.split(".")[1];
return {
lang,
src: getFile(videoInfo, name),
};
})}
onPlaybackProgress={setPlaybackProgress}
autoplay
/>
</div>
</div>
</div>
<div
className={[
"w-full lg:w-1/4 lg:pl-4",
isChatVisible ? "flex-1" : "",
].join(" ")}
>
{!hasChat ? (
<div className="border border-gray-800 rounded p-4 text-center">
<p>Chat replay unavailable</p>
</div>
) : (
<ChatReplayPanel
src={urlChat}
currentTimeSeconds={playbackProgress}
onChatToggle={setIsChatVisible}
/>
)}
</div>
</div>
<div className="flex lg:flex-row flex-col">
<div className="w-full lg:w-3/4">
<div className="mt-4 mx-6">
<h1 className="text-2xl mb-2">{videoInfo.title}</h1>
<div className="flex flex-row justify-between">
<ClientRender enableSSR>
<p className="text-gray-400">
{format(videoInfo.view_count)} views &middot;{" "}
<span
title={
videoInfo.timestamps?.publishedAt
? new Date(
videoInfo.timestamps?.publishedAt
).toLocaleString()
: "(exact timestamp unknown)"
}
>
Uploaded{" "}
{formatDate(
new Date(
videoInfo.timestamps?.publishedAt ||
videoInfo.upload_date
)
)}
</span>{" "}
&middot;{" "}
<span
title={new Date(
videoInfo.archived_timestamp +
(videoInfo.archived_timestamp.endsWith("Z") ? "" : "Z")
).toLocaleString()}
>
Archived{" "}
{formatDate(
new Date(
videoInfo.archived_timestamp +
(videoInfo.archived_timestamp.endsWith("Z")
? ""
: "Z")
)
)}
</span>
</p>
</ClientRender>
<div>
<span className="text-green-500">
{format(videoInfo.like_count)} likes
</span>{" "}
/{" "}
<span className="text-red-500">
{format(videoInfo.dislike_count)} dislikes
</span>
</div>
</div>
<div className="flex flex-row mt-2">
<VideoActionButtons full video={videoInfo} />
</div>
<div className="mt-4 pb-4 border-b border-gray-900">
<div className="inline-block">
<Link href={"/channel/" + videoInfo.channel_id}>
<a className="mb-4 mb-4 hover:underline flex flex-row">
<div className="w-12 h-12 rounded-full overflow-hidden relative">
<Image
priority
alt="Channel thumbnail"
src={"https://i.imgur.com/rBvryFM.png"||props.channelProfileURL}
layout="fixed"
width={48}
height={48}
unoptimized
/>
</div>
<div className="ml-4">
<p className="font-bold text-lg leading-tight">
{videoInfo.channel_name}
</p>
<span className="text-gray-400 leading-tight">
{props.channelVideoCount} videos
</span>
</div>
</a>
</Link>
</div>
<ExpandableContainer>
<div className="whitespace-pre-line break-words text-gray-300">
<MemoLinkify linkClassName="text-blue-300 hover:underline focus:outline-none focus:underline">
{videoInfo.description}
</MemoLinkify>
</div>
</ExpandableContainer>
</div>
</div>
</div>
<div className="w-full lg:w-1/4 lg:pl-4">
<div className="mt-6">
<h2 className="text-xl font-bold mb-2">Related videos</h2>
<div>
{relatedVideos.map((video) => (
<div className="mb-4" key={video.video_id}>
<VideoCard small video={video} key={video.video_id} />
</div>
))}
</div>
</div>
</div>
</div>
</PageBase>
);
};
export default WatchPage;