mirror of
https://github.com/sussy-code/smov.git
synced 2024-12-29 16:07:40 +01:00
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:
parent
50c2a552ab
commit
1021237191
2 changed files with 39 additions and 42 deletions
|
@ -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 ? (
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue