YouTube Flag Tracker

Track changes in YouTube's experiment flags

目前为 2024-11-02 提交的版本。查看 最新版本

// ==UserScript==
// @name        YouTube Flag Tracker
// @namespace   Violentmonkey Scripts
// @version     1.2
// @description Track changes in YouTube's experiment 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);
}

// Comparison Cases: Boolean, Integer, Float, String, List.
function isEqual(a, b) {

    // Primitive Types: Boolean, Integer, Float, String
    if (a === b) {
        return true;
    }

    // Array Type
    if (Array.isArray(a) && Array.isArray(b)) {
        return (
            a.length === b.length &&
            JSON.stringify(a) === JSON.stringify(b)
        );
    }

    console.log("not equal");
    return false; // Other Type
}

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] = { type: "added", value: currVal };
        }

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

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

    return changes;
}

function updateStorage(curr) {

    const prev = GM_getValue("flags", {});
    const changes = diffChecker(prev, curr);

    save("changes", changes);
    save("flags", curr);
}

function observeFlags() {

    const observer = new MutationObserver(() => {

        const flags = unsafeWindow.yt?.config_?.EXPERIMENT_FLAGS;

        if (flags) {
            observer.disconnect();
            console.log(flags);
            updateStorage(flags);
        }
    });

    observer.observe(document.body, { childList: true, subtree: true });
}


observeFlags();