Smart Video Title Timer

Updates main document title with speed-adjusted time from videos, including cross-origin iframes.

K instalaci tototo skriptu si budete muset nainstalovat rozšíření jako Tampermonkey, Greasemonkey nebo Violentmonkey.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

K instalaci tohoto skriptu si budete muset nainstalovat rozšíření jako Tampermonkey nebo Violentmonkey.

K instalaci tohoto skriptu si budete muset nainstalovat rozšíření jako Tampermonkey nebo Userscripts.

You will need to install an extension such as Tampermonkey to install this script.

K instalaci tohoto skriptu si budete muset nainstalovat manažer uživatelských skriptů.

(Už mám manažer uživatelských skriptů, nechte mě ho nainstalovat!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(Už mám manažer uživatelských stylů, nechte mě ho nainstalovat!)

// ==UserScript==
// @name         Smart Video Title Timer
// @name:tr Akıllı Video Başlık Zamanlayıcı
// @namespace    http://tampermonkey.net
// @version      3
// @description  Updates main document title with speed-adjusted time from videos, including cross-origin iframes.
// @description:tr Video hızı değiştirildiğinde, ana site başlığını iframe içindekiler dahil hıza göre ayarlanmış süreyle günceller.
// @author       Atilla
// @match        *://*/*
// @grant        none
// @run-at       document-start
// @allframes    true
// @icon https://gitlab.com/atilla-tr/tampermonkey-depolama/-/raw/main/Smart%20Video%20Title%20Timer.js/app-icon-1024.png
// @license      MIT
// ==/UserScript==
(function () {
  "use strict";

  const isMainFrame = window === window.top;
  let activeVideo = null;
  let originalTitle = isMainFrame
    ? document.title || window.location.hostname
    : "";

  // Saniyeyi SS:DD:SS formatına çevirir
  function formatTime(seconds) {
    if (isNaN(seconds) || seconds === Infinity) return "00:00";
    const h = Math.floor(seconds / 3600);
    const m = Math.floor((seconds % 3600) / 60);
    const s = Math.floor(seconds % 60);
    return h > 0
      ? `${h}:${m.toString().padStart(2, "0")}:${s.toString().padStart(2, "0")}`
      : `${m.toString().padStart(2, "0")}:${s.toString().padStart(2, "0")}`;
  }

  // ----------------------------------------------------
  // ANA SAYFA (MAIN FRAME) MANTIĞI
  // ----------------------------------------------------
  if (isMainFrame) {
    const titleObserver = new MutationObserver(() => {
      // Eğer başlık dışarıdan (site tarafından) değişirse orijinal başlığı güncelle
      const cleanTitle = document.title.replace(
        /^\(\d+:\d+(:\d+)? \/ \d+:\d+(:\d+)?\) /,
        "",
      );
      if (!document.title.startsWith("(")) {
        originalTitle = cleanTitle;
      }
    });

    if (document.querySelector("title")) {
      titleObserver.observe(document.querySelector("title"), {
        childList: true,
      });
    } else {
      // Title elementi henüz yoksa DOM yüklendiğinde bağlan
      document.addEventListener("DOMContentLoaded", () => {
        if (document.querySelector("title")) {
          titleObserver.observe(document.querySelector("title"), {
            childList: true,
          });
        }
      });
    }

    // Iframe'lerden gelen zaman mesajlarını dinle
    window.addEventListener("message", (event) => {
      if (event.data && event.data.type === "SMART_VIDEO_TIMER") {
        applyNewTitle(event.data.timeString);
      }
    });

    function applyNewTitle(timeString) {
      const cleanTitle = originalTitle.replace(
        /^\(\d+:\d+(:\d+)? \/ \d+:\d+(:\d+)?\) /,
        "",
      );
      const newTitle = timeString ? `${timeString} ${cleanTitle}` : cleanTitle;

      if (document.title !== newTitle) {
        titleObserver.disconnect();
        document.title = newTitle;
        if (document.querySelector("title")) {
          titleObserver.observe(document.querySelector("title"), {
            childList: true,
          });
        }
      }
    }

    // Ana sayfadaki videolar için yerel fonksiyon
    window._updateMainTitleDirectly = function (timeString) {
      applyNewTitle(timeString);
    };
  }

  // ----------------------------------------------------
  // VİDEO TAKİP MANTIĞI (Hem Ana Sayfa Hem Iframe Çalıştırır)
  // ----------------------------------------------------
  function calculateAndSend() {
    if (!activeVideo || activeVideo.paused || activeVideo.playbackRate === 1) {
      sendTitleUpdate(""); // Zamanı temizle, orijinal başlığa dön
      return;
    }

    const rate = activeVideo.playbackRate;
    const currentAdjusted = activeVideo.currentTime / rate;
    const durationAdjusted = activeVideo.duration / rate;
    const timeString = `(${formatTime(currentAdjusted)} / ${formatTime(durationAdjusted)})`;

    sendTitleUpdate(timeString);
  }

  function sendTitleUpdate(timeString) {
    if (isMainFrame) {
      if (typeof window._updateMainTitleDirectly === "function") {
        window._updateMainTitleDirectly(timeString);
      }
    } else {
      // Iframe içindeyse mesajı güvenli bir şekilde ana sayfaya fırlatır
      window.top.postMessage(
        {
          type: "SMART_VIDEO_TIMER",
          timeString: timeString,
        },
        "*",
      );
    }
  }

  function setupVideoListeners(video) {
    if (video.dataset.titleTimerTracked) return;
    video.dataset.titleTimerTracked = "true";

    const events = [
      "timeupdate",
      "play",
      "pause",
      "ratechange",
      "ended",
      "durationchange",
    ];
    events.forEach((evt) => {
      video.addEventListener(evt, () => {
        if (!video.paused) {
          activeVideo = video;
        }
        calculateAndSend();
      });
    });

    if (!video.paused) {
      activeVideo = video;
      calculateAndSend();
    }
  }

  function scanForVideos() {
    document.querySelectorAll("video").forEach(setupVideoListeners);
  }

  const domObserver = new MutationObserver((mutations) => {
    let shouldScan = false;
    for (let mutation of mutations) {
      if (mutation.addedNodes.length > 0) {
        shouldScan = true;
        break;
      }
    }
    if (shouldScan) scanForVideos();
  });

  function init() {
    if (isMainFrame && document.querySelector("title") && !originalTitle) {
      originalTitle = document.title;
    }
    scanForVideos();
    if (document.body) {
      domObserver.observe(document.body, { childList: true, subtree: true });
    }
  }

  if (document.readyState === "loading") {
    document.addEventListener("DOMContentLoaded", init);
  } else {
    init();
  }
})();