YouTube Flag Tracker

Track changes in YouTube's experiment flags. (Stores added, modified, or removed flags)

اعتبارا من 31-10-2024. شاهد أحدث إصدار.

// ==UserScript==
// @name        YouTube Flag Tracker
// @namespace   Violentmonkey Scripts
// @version     1.0
// @description Track changes in YouTube's experiment flags. (Stores added, modified, or removed flags)
// @include     http*://www.youtube.com/*
// @grant       unsafeWindow
// @grant       GM_setValue
// @grant       GM_getValue
// @run-at      document-end
// ==/UserScript==

function save(key, data) {
    if (!Object.keys(data).length) return;

    if (key === 'changes') {
        const changes = GM_getValue('changes', []);
        changes.unshift(data);
        data = changes;
    }

    GM_setValue(key, data);
}

function deepEqual(a, b) {
    if (a === b) return true;

    if (a == null || b == null || typeof a !== 'object' || typeof b !== 'object') return false;

    if (Array.isArray(a) !== Array.isArray(b)) return false;

    const keysA = Object.keys(a);
    const keysB = Object.keys(b);

    if (keysA.length !== keysB.length) return false;

    return keysA.every(key => keysB.includes(key) && deepEqual(a[key], b[key]));
}

function diffChecker(prev = {}, curr = {}) {
    let changes = {};
    const allKeys = new Set([...Object.keys(prev), ...Object.keys(curr)]);

    allKeys.forEach(key => {
        const prevVal = prev[key];
        const currVal = curr[key];

        if (prevVal === undefined) {
            changes[key] = { value: currVal, type: "added" };
        }

        else if (currVal === undefined) {
            changes[key] = { value: null, type: "removed" };
        }

        else if (!deepEqual(prevVal, currVal)) {
            changes[key] = { value: currVal, type: "modified" };
        }
    });

    return changes;
}

function fetchFlags(retries = 10, delay = 400) {
    const curr = unsafeWindow.yt?.config_?.EXPERIMENT_FLAGS;

    if (curr) return curr;

    if (retries > 0) {
        setTimeout(() => fetchFlags(retries - 1), delay);
    }

    return null;
}

function updateFlags() {
    const prev = GM_getValue('flags', {});
    const curr = fetchFlags();

    if (!curr) {
        console.log('Current flags not found...');
        return;
    }

    const changes = diffChecker(prev, curr);
    save('changes', changes);
    save('flags', curr);
}

updateFlags();