Убери  IntersectionObserver 

```js

import { EventImpl } from "./EventImpl.js";
import debug from "./debug.js";

const adKeywords = new Set([
  "advertise",
  "promo",
  "sponsor",
  "banner",
  "commercial",
  "preroll",
  "midroll",
  "postroll",
  "ad-container",
  "sponsored",
]);

const adKeywordsRegex = (() => {
  const pattern = Array.from(adKeywords).join("|");
  return new RegExp(pattern, "i");
})();

/**
 * Determines if a video element is considered an advertisement based on its attributes.
 * @param {HTMLElement} video - The video element to check.
 * @returns {boolean} True if the video is identified as an advertisement, false otherwise.
 */
const isAdVideo = (video) => {
  while (video) {
    const attributes = [video.className, video.id, video.title];
    if (adKeywordsRegex.test(attributes.join(" "))) {
      return true;
    }
    video = video.parentElement;
  }
  return false;
};

/**
 * Checks if a video element is muted and not intended for playback.
 * @param {HTMLVideoElement} video - The video element to check.
 * @returns {boolean} True if the video is muted, false otherwise.
 */
const isMutedVideo = (video) =>
  (video.muted || video.hasAttribute("muted")) &&
  !video.classList.contains("vjs-tech") &&
  !video.preload;

/**
 * Checks if a video element is ready for playback.
 * @param {HTMLVideoElement} video - The video element to check.
 * @returns {boolean} True if the video is ready, false otherwise.
 */
const isVideoReady = (video) => video.readyState >= 3;

/**
 * Waits until the video is ready for processing.
 * @param {HTMLVideoElement} video - The video element to wait for.
 * @returns {Promise<HTMLVideoElement>} Promise that resolves when the video is ready.
 */
const waitForVideoReady = (video) =>
  new Promise((resolve) => {
    const checkVideoState = () => {
      if (isVideoReady(video) || isMutedVideo(video)) {
        resolve(video);
      } else {
        requestAnimationFrame(checkVideoState);
      }
    };
    checkVideoState();
  });

/**
 * Recursively traverses a DOM node, including its Shadow DOM if present, to find all video elements.
 * @param {Node} node - The root node to start traversal from.
 * @param {Set<Node>} visitedNodes - A set to track visited nodes and prevent redundant processing.
 * @returns {HTMLVideoElement[]} An array of found video elements.
 */
const traverseDOM = (node, visitedNodes) => {
  const videos = [];

  const traverse = (currentNode) => {
    if (visitedNodes.has(currentNode)) return;
    visitedNodes.add(currentNode);

    if (currentNode instanceof HTMLVideoElement) {
      videos.push(currentNode);
    } else if (currentNode instanceof HTMLElement) {
      // Collect video elements directly under the current node
      const videoElements = currentNode.querySelectorAll("video");
      for (let i = 0; i < videoElements.length; i++) {
        if (!visitedNodes.has(videoElements[i])) {
          videos.push(videoElements[i]);
          visitedNodes.add(videoElements[i]);
        }
      }

      // If the current node hosts a Shadow DOM, traverse it
      const shadow = currentNode.shadowRoot;
      if (shadow) {
        traverse(shadow);
      }
    }

    // Traverse child nodes
    const childNodes = currentNode.childNodes || [];
    for (let i = 0; i < childNodes.length; i++) {
      traverse(childNodes[i]);
    }
  };

  traverse(node);
  return videos;
};

/**
 * The VideoObserver class observes the DOM for video elements,
 * including those within Shadow DOMs, using IntersectionObserver.
 */
export class VideoObserver {
  constructor() {
    this.videoCache = new WeakSet();
    this.onVideoAdded = new EventImpl();
    this.onVideoRemoved = new EventImpl();
    this.visitedNodes = new Set();

    this.intersectionObserver = new IntersectionObserver(
      this.handleIntersection,
      { threshold: 0.1 }
    );

    this.domObserver = new MutationObserver(this.handleDOMChanges);
  }

  /**
   * Handles intersection changes for observed video elements.
   * @param {IntersectionObserverEntry[]} entries - Intersection entries.
   */
  handleIntersection = (entries) => {
    for (let i = 0; i < entries.length; i++) {
      const entry = entries[i];
      if (entry.isIntersecting) {
        this.checkAndHandleVideo(entry.target);
      } else {
        this.handleVideoRemoved(entry.target);
      }
    }
  };

  /**
   * Handles DOM changes to detect new video elements, including those in Shadow DOMs.
   * @param {MutationRecord[]} mutations - List of mutation records.
   */
  handleDOMChanges = (mutations) => {
    for (let i = 0; i < mutations.length; i++) {
      const mutation = mutations[i];
      if (mutation.type === 'childList') {
        for (let j = 0; j < mutation.addedNodes.length; j++) {
          const node = mutation.addedNodes[j];
          const videos = traverseDOM(node, this.visitedNodes);
          for (let k = 0; k < videos.length; k++) {
            if (!this.videoCache.has(videos[k])) {
              this.intersectionObserver.observe(videos[k]);
              this.checkAndHandleVideo(videos[k]);
            }
          }
        }
      } else if (mutation.type === 'attributes' && mutation.target instanceof HTMLVideoElement) {
        // Handle attribute changes on video elements
        this.checkAndHandleVideo(mutation.target);
      }
    }
    // Additional check for any missed videos after mutations
    this.checkForMissedVideos();
  };

  /**
   * Checks for any videos that might have been missed during initial observation.
   */
  checkForMissedVideos = () => {
    const allVideos = traverseDOM(document.body, new Set());
    for (let i = 0; i < allVideos.length; i++) {
      if (!this.videoCache.has(allVideos[i])) {
        this.intersectionObserver.observe(allVideos[i]);
        this.checkAndHandleVideo(allVideos[i]);
      }
    }
  };

  /**
   * Enables the observer by starting to observe the document and processing existing videos.
   */
  enable() {
    this.domObserver.observe(document.body, {
      childList: true,
      subtree: true,
      attributes: true,
      attributeFilter: ['src', 'poster']
    });
    const existingVideos = traverseDOM(document.body, new Set());
    for (let i = 0; i < existingVideos.length; i++) {
      this.intersectionObserver.observe(existingVideos[i]);
      this.checkAndHandleVideo(existingVideos[i]);
    }
    // Initial check for any missed videos
    setTimeout(this.checkForMissedVideos, 1000);
  }

  /**
   * Disables the observer by disconnecting both IntersectionObserver and MutationObserver.
   */
  disable() {
    this.intersectionObserver.disconnect();
    this.domObserver.disconnect();
  }

  /**
   * Checks if a video should be handled (i.e., not an ad or muted) and processes it accordingly.
   * @param {HTMLVideoElement} video - The video element to check and handle.
   */
  checkAndHandleVideo = async (video) => {
    console.log("checkAndHandleVideo", video);
    if (isAdVideo(video) || isMutedVideo(video)) {
      debug.log("The promotional/muted video was ignored", video);
      return;
    }
    if (this.videoCache.has(video)) return;
    this.videoCache.add(video);

    // Wait for the video to be ready before dispatching the event
    await waitForVideoReady(video);
    this.handleVideoAdded(video);
  };

  /**
   * Dispatches an event when a new video is added and ready.
   * @param {HTMLVideoElement} video - The video element that was added.
   */
  handleVideoAdded = (video) => {
    this.onVideoAdded.dispatch(video);
  };

  /**
   * Handles the removal of a video element by dispatching an event if it's no longer intersecting.
   * @param {HTMLVideoElement} video - The video element that was removed.
   */
  handleVideoRemoved = (video) => {
    if (this.videoCache.has(video)) {
      this.videoCache.delete(video);
      this.onVideoRemoved.dispatch(video);
    }
  };
}

```