asteriksme / Youtube Video Timestamp

// ==UserScript==
// @name         Youtube Video Timestamp
// @namespace    http://tampermonkey.net/
// @version      0.1
// @description  Add timestamps of video uploads in end video tiles
// @author       You
// @match        https://www.youtube.com/*
// @icon         data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
// @grant        none
// @license      MIT
// @updateURL    https://openuserjs.org/meta/asteriksme/Youtube_Video_Timestamp.meta.js
// @downloadURL  https://openuserjs.org/install/asteriksme/Youtube_Video_Timestamp.user.js
// ==/UserScript==

(function() {
    'use strict';

    const getRelativeTimeString = (date, lang = navigator.language) => {
        // Allow dates or times to be passed
        const timeMs = typeof date === "number" ? date : date.getTime();
        // Get the amount of seconds between the given date and now
        const deltaSeconds = Math.round((timeMs - Date.now()) / 1000);
        // Array reprsenting one minute, hour, day, week, month, etc in seconds
        const cutoffs = [60, 3600, 86400, 86400 * 7, 86400 * 30, 86400 * 365, Infinity];
        // Array equivalent to the above but in the string representation of the units
        const units = ["second", "minute", "hour", "day", "week", "month", "year"];
        // Grab the ideal cutoff unit
        const unitIndex = cutoffs.findIndex(cutoff => cutoff > Math.abs(deltaSeconds));
        // Get the divisor to divide from the seconds. E.g. if our unit is "day" our divisor
        // is one day in seconds, so we can divide our seconds by this to get the # of days
        const divisor = unitIndex ? cutoffs[unitIndex - 1] : 1;
        // Intl.RelativeTimeFormat do its magic
        const rtf = new Intl.RelativeTimeFormat(lang, { numeric: "auto" });
        return rtf.format(Math.floor(deltaSeconds / divisor), units[unitIndex]);
    };

    const add_timestamp = () => {
        if (timestampsAdded || !document.querySelector(".ytp-endscreen-content a .ytp-videowall-still-info-author")) {
            return;
        }
        timestampsAdded = true;
        document.querySelector(".ytp-endscreen-content").querySelectorAll("a").forEach(async (tag) => {
            await fetch(tag.href, {
                method: 'get',
            }).then((response) => {
                return response.text();
            }).then((res) => {
                const date = new Date(res.match(/publishDate":"([^"]*)"/)[1]);
                tag.querySelector(".ytp-videowall-still-info-author").innerHTML += ` • ${date.toLocaleDateString()} (${getRelativeTimeString(date)})`;
                console.log(tag.querySelector(".ytp-videowall-still-info-author"));
            });
        });
    }

    let timestampsAdded = false;
    setInterval(add_timestamp, 5000);

    const changeVideosPerRow = (changes, observer) => {
        const renderer = ".ytd-rich-grid-renderer";
        const itemsPerRow = "--ytd-rich-grid-items-per-row";
        const minSize = 5;
        if (document.querySelector(renderer) && parseInt(getComputedStyle(document.querySelector(renderer)).getPropertyValue(itemsPerRow), 10) < minSize) {
            console.log("detected");
            observer.disconnect();
            document.querySelectorAll(renderer).forEach((elt) => elt.style.setProperty(itemsPerRow, minSize.toString()));
            console.log("change");
        }
    };
    (new MutationObserver(changeVideosPerRow)).observe(document, {childList: true, subtree: true});
})();