···372372 videoElement.addEventListener("playing", () => emit("play", undefined));
373373 videoElement.addEventListener("pause", () => emit("pause", undefined));
374374 videoElement.addEventListener("canplay", () => {
375375- emit("loading", false);
375375+ // Check if video has enough buffered data to play smoothly (at least 5 seconds ahead)
376376+ const hasEnoughBuffer = (() => {
377377+ if (!videoElement) return false;
378378+ const currentTime = videoElement.currentTime ?? 0;
379379+ const buffered = videoElement.buffered;
380380+ if (buffered.length === 0) return false;
381381+382382+ // Find the buffered range that contains current time
383383+ for (let i = 0; i < buffered.length; i += 1) {
384384+ if (
385385+ currentTime >= buffered.start(i) &&
386386+ currentTime <= buffered.end(i)
387387+ ) {
388388+ const bufferedAhead = buffered.end(i) - currentTime;
389389+ return bufferedAhead >= 5; // At least 5 seconds buffered ahead
390390+ }
391391+ }
392392+ return false;
393393+ })();
394394+395395+ // Only set loading to false if we have enough buffer or if we're not at the start
396396+ if (hasEnoughBuffer || (videoElement?.currentTime ?? 0) > 0) {
397397+ emit("loading", false);
398398+ }
399399+376400 // Attempt autoplay if this was an autoplay transition (startAt = 0)
377401 if (shouldAutoplayAfterLoad && startAt === 0 && videoElement) {
378402 shouldAutoplayAfterLoad = false; // Reset the flag
379403 // Try to play - this will work on most platforms, but iOS may block it
380404 const playPromise = videoElement.play();
381405 if (playPromise !== undefined) {
382382- playPromise.catch(() => {
383383- // Play was blocked (likely iOS), emit that we're not playing
384384- // The AutoPlayStart component will show a play button
385385- emit("pause", undefined);
386386- });
406406+ playPromise
407407+ .then(() => {
408408+ // Autoplay succeeded
409409+ })
410410+ .catch((_error) => {
411411+ // Play was blocked (likely iOS), emit that we're not playing
412412+ // The AutoPlayStart component will show a play button
413413+ emit("pause", undefined);
414414+ });
387415 }
388416 }
389417 });
···428456 }
429457 });
430458 videoElement.addEventListener("progress", () => {
431431- if (videoElement)
432432- emit(
433433- "buffered",
434434- handleBuffered(videoElement.currentTime, videoElement.buffered),
459459+ if (videoElement) {
460460+ const bufferedTime = handleBuffered(
461461+ videoElement.currentTime,
462462+ videoElement.buffered,
435463 );
464464+ emit("buffered", bufferedTime);
465465+466466+ // Check if we now have enough buffer to stop loading
467467+ const hasEnoughBuffer = (() => {
468468+ const buffered = videoElement.buffered;
469469+ if (buffered.length === 0) return false;
470470+471471+ const currentTime = videoElement.currentTime ?? 0;
472472+ // Find the buffered range that contains current time
473473+ for (let i = 0; i < buffered.length; i += 1) {
474474+ if (
475475+ currentTime >= buffered.start(i) &&
476476+ currentTime <= buffered.end(i)
477477+ ) {
478478+ const bufferedAhead = buffered.end(i) - currentTime;
479479+ return bufferedAhead >= 5; // At least 5 seconds buffered ahead
480480+ }
481481+ }
482482+ return false;
483483+ })();
484484+485485+ // If we're still loading but now have enough buffer, stop loading
486486+ // This handles cases where canplay fired with insufficient buffer
487487+ if (hasEnoughBuffer && videoElement.readyState >= 3) {
488488+ emit("loading", false);
489489+ }
490490+ }
436491 });
437492 videoElement.addEventListener("webkitendfullscreen", () => {
438493 isFullscreen = false;