Telegram Web — перемотка + сохранение прогресса видео

Перемотка видео стрелками и восстановление по автору и времени

// ==UserScript==
// @name         Telegram Web — перемотка + сохранение прогресса видео
// @version      1.0
// @description  Перемотка видео стрелками и восстановление по автору и времени
// @match        https://web.telegram.org/*
// @grant        none
// @namespace https://greasyfork.org/users/789838
// ==/UserScript==

(function() {
  'use strict';

  const STORAGE_KEY = 'tg_video_progress';
  const loadProgress = () => JSON.parse(localStorage.getItem(STORAGE_KEY) || '{}');
  const saveProgress = obj => localStorage.setItem(STORAGE_KEY, JSON.stringify(obj));

  const isVisible = el =>
    el.offsetParent !== null &&
    el.offsetWidth > 0 &&
    el.offsetHeight > 0 &&
    window.getComputedStyle(el).visibility !== 'hidden';

  // Обработчик стрелок
  document.addEventListener('keydown', e => {
    if (e.key === 'ArrowLeft' || e.key === 'ArrowRight') {
      const videos = Array.from(document.querySelectorAll('video')).filter(isVisible);
      if (videos.length) {
        const v = videos[0];
        v.currentTime += (e.key === 'ArrowRight' ? 5 : -5);
        e.stopPropagation();
        e.preventDefault();
      }
    }
  }, true);

  // Слежение за media-viewer
  const observer = new MutationObserver(() => {
    const nameEl = document.querySelector('.media-viewer-name .peer-title');
    const dateEl = document.querySelector('.media-viewer-date');
    const video = document.querySelector('video');

    if (!nameEl || !dateEl || !video) return;

    const name = nameEl.textContent.trim();
    const date = dateEl.textContent.trim();
    const key = `${name} @ ${date}`;
    const store = loadProgress();

    if (store[key] && !video.dataset.restored) {
      video.currentTime = store[key];
      video.dataset.restored = '1';
    }

    if (!video.dataset.listened) {
      video.dataset.listened = '1';
      setInterval(() => {
        if (!video.paused && !video.ended) {
          store[key] = video.currentTime;
          saveProgress(store);
        }
      }, 2000);
    }
  });

  observer.observe(document.body, { childList: true, subtree: true });
})();