Greasy Fork is available in English.
Updates main document title with speed-adjusted time from videos, including cross-origin iframes.
// ==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();
}
})();