diff --git a/src/video/components/actions/ThumbnailAction.tsx b/src/video/components/actions/ThumbnailAction.tsx
index e2538057..cbb72374 100644
--- a/src/video/components/actions/ThumbnailAction.tsx
+++ b/src/video/components/actions/ThumbnailAction.tsx
@@ -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 ? (
diff --git a/src/video/components/internal/ThumbnailGeneratorInternal.tsx b/src/video/components/internal/ThumbnailGeneratorInternal.tsx
index 69fc33e7..993b1d5a 100644
--- a/src/video/components/internal/ThumbnailGeneratorInternal.tsx
+++ b/src/video/components/internal/ThumbnailGeneratorInternal.tsx
@@ -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;
 }