refactor(thumbnails): add index to continue from where left off

- hls moved to ref
- block loading thumbnail if there is no thumbnail at all
This commit is contained in:
frost768 2023-06-24 02:21:48 +03:00
parent 50c2a552ab
commit 1021237191
2 changed files with 39 additions and 42 deletions

View File

@ -99,6 +99,7 @@ export default function ThumbnailAction({
const src = source.source?.thumbnails.find(
(x) => x.from < hoverTime && x.to > hoverTime
)?.imgUrl;
if (!source.source?.thumbnails.length) return null;
return (
<div className="pointer-events-none">
{!src ? (

View File

@ -8,52 +8,36 @@ import { updateSource, useSource } from "@/video/state/logic/source";
import { Thumbnail } from "@/video/state/types";
async function* generate(
videoUrl: string,
streamType: MWStreamType,
videoRef: RefObject<HTMLVideoElement>,
canvasRef: RefObject<HTMLCanvasElement>,
index = 0,
numThumbnails = 20
): AsyncGenerator<Thumbnail, Thumbnail> {
const video = videoRef.current;
const canvas = canvasRef.current;
if (!video) return { from: -1, to: -1, imgUrl: "" };
if (!canvas) return { from: -1, to: -1, imgUrl: "" };
console.log("extracting started", streamType.toString());
if (streamType === MWStreamType.HLS) {
const hls = new Hls();
console.log("new hls instance");
hls.attachMedia(video);
hls.loadSource(videoUrl);
}
await new Promise((resolve, reject) => {
video.addEventListener("loadedmetadata", resolve);
video.addEventListener("error", reject);
});
canvas.height = video.videoHeight * 1;
canvas.width = video.videoWidth * 1;
let i = 0;
while (i < numThumbnails) {
const from = i * video.duration;
const to = (i + 1) * video.duration;
// Seek to the specified time
canvas.height = video.videoHeight;
canvas.width = video.videoWidth;
const ctx = canvas.getContext("2d");
if (!ctx) return { from: -1, to: -1, imgUrl: "" };
let i = index;
const limit = numThumbnails - 1;
const step = video.duration / limit;
while (i < limit && !Number.isNaN(video.duration)) {
const from = i * step;
const to = (i + 1) * step;
video.currentTime = from;
console.log(from, to);
console.time("seek loaded");
await new Promise((resolve) => {
video.addEventListener("seeked", resolve);
});
console.timeEnd("seek loaded");
console.log("loaded", video.currentTime, streamType.toString());
const ctx = canvas.getContext("2d");
if (!ctx) return { from: -1, to: -1, imgUrl: "" };
// Draw the video frame on the canvas
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
// Convert the canvas to a data URL and add it to the list of thumbnails
const imgUrl = canvas.toDataURL();
i += 1;
yield {
@ -67,48 +51,60 @@ async function* generate(
}
export default function ThumbnailGeneratorInternal() {
const videoRef = useRef<HTMLVideoElement>(document.createElement("video"));
const canvasRef = useRef<HTMLCanvasElement>(document.createElement("canvas"));
const descriptor = useVideoPlayerDescriptor();
const source = useSource(descriptor);
const videoRef = useRef<HTMLVideoElement>(document.createElement("video"));
const canvasRef = useRef<HTMLCanvasElement>(document.createElement("canvas"));
const hlsRef = useRef<Hls>(new Hls());
const thumbnails = useRef<Thumbnail[]>([]);
const abortController = useRef<AbortController>(new AbortController());
const generator = useCallback(
async (url: string, type: MWStreamType) => {
for await (const thumbnail of generate(url, type, videoRef, canvasRef)) {
async (videoUrl: string, streamType: MWStreamType) => {
const prevIndex = thumbnails.current.length;
const video = videoRef.current;
if (streamType === MWStreamType.HLS) {
hlsRef.current.attachMedia(video);
hlsRef.current.loadSource(videoUrl);
} else {
video.crossOrigin = "anonymous";
video.src = videoUrl;
}
for await (const thumbnail of generate(videoRef, canvasRef, prevIndex)) {
if (abortController.current.signal.aborted) {
console.log("broke out of loop", type.toString());
if (streamType === MWStreamType.HLS) hlsRef.current.detachMedia();
abortController.current = new AbortController();
const state = getPlayerState(descriptor);
if (!state.source) return;
const { url, type } = state.source;
generator(url, type);
break;
}
if (thumbnail.from === -1) continue;
thumbnails.current = [...thumbnails.current, thumbnail];
const state = getPlayerState(descriptor);
if (!state.source) return;
console.log("ran");
state.source.thumbnails = thumbnails.current;
console.log(thumbnails.current);
updateSource(descriptor, state);
console.log("ran 2");
}
},
[descriptor]
);
useEffect(() => {
const controller = abortController.current;
const state = getPlayerState(descriptor);
if (!state.source) return;
const { url, type } = state.source;
generator(url, type);
}, [descriptor, generator, source.source?.url]);
useEffect(() => {
const controller = abortController.current;
return () => {
console.log("abort");
if (!source.source?.url) return;
controller.abort();
};
}, []);
}, [descriptor, generator, source.source?.url]);
return null;
}