🏠 Home 

YouTube Flag Tracker

Track changes in YouTube's experiment flags

// ==UserScript==
// @name        YouTube Flag Tracker
// @namespace   Violentmonkey Scripts
// @version     1.2.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);
}
function isEqual(a, b) {
if (Array.isArray(a) && Array.isArray(b)) {
return (
a.length === b.length &&
JSON.stringify(a) === JSON.stringify(b)
);
}
return a === b;
}
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(currentFlags) {
const previousFlags = GM_getValue("flags", {});
const changes = diffChecker(previousFlags, currentFlags);
const numChanges = Object.keys(changes).length;
const maxChanges = Object.keys(previousFlags).length / 2;
if (!maxChanges || numChanges <= maxChanges) {
save("changes", changes);
save("flags", currentFlags);
}
}
function observeFlags() {
const observer = new MutationObserver(() => {
const flags = unsafeWindow.yt?.config_?.EXPERIMENT_FLAGS;
if (flags) {
observer.disconnect();
updateStorage(flags);
}
});
observer.observe(document.body, { childList: true, subtree: true });
}
observeFlags();