Smart Video Title Timer

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

Du musst eine Erweiterung wie Tampermonkey, Greasemonkey oder Violentmonkey installieren, um dieses Skript zu installieren.

Um dieses Skript zu installieren, müssen Sie eine Erweiterung wie Tampermonkey oder Violentmonkey installieren.

Um dieses Skript zu installieren, müssen Sie eine Erweiterung wie Tampermonkey oder Violentmonkey installieren.

Um dieses Skript zu installieren, müssen Sie eine Erweiterung wie Tampermonkey oder Userscripts installieren.

Um dieses Skript zu installieren, müssen Sie eine Erweiterung wie Tampermonkey installieren.

Sie müssten eine Skript Manager Erweiterung installieren damit sie dieses Skript installieren können

(Ich habe schon ein Skript Manager, Lass mich es installieren!)

Um dieses Design zu installieren, müssen Sie eine Erweiterung wie Stylus installieren.

Um dieses Design zu installieren, müssen Sie eine Erweiterung wie Stylus installieren.

Um dieses Design zu installieren, müssen Sie eine Erweiterung wie Stylus installieren.

Um diesen Stil zu installieren, müssen Sie eine Erweiterung für den Benutzerstil Verwaltung installieren.

Um diesen Stil zu installieren, müssen Sie eine Erweiterung für den Benutzerstil Verwaltung installieren.

Um diesen Stil zu installieren, müssen Sie eine Erweiterung für den Benutzerstil Verwaltung installieren.

(Ich habe bereits einen Benutzerstil Verwaltung, ich möchte ihn installieren!)

// ==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();
  }
})();