YouTube EXPERIMENT_FLAGS Tamer (Basic)

Adjust EXPERIMENT_FLAGS

  1. // ==UserScript==
  2. // @name YouTube EXPERIMENT_FLAGS Tamer (Basic)
  3. // @namespace UserScripts
  4. // @match https://www.youtube.com/*
  5. // @version 0.4.8.104
  6. // @license MIT
  7. // @author CY Fung
  8. // @icon https://raw.githubusercontent.com/cyfung1031/userscript-supports/main/icons/yt-engine.png
  9. // @description Adjust EXPERIMENT_FLAGS
  10. // @grant none
  11. // @unwrap
  12. // @run-at document-start
  13. // @allFrames true
  14. // @inject-into page
  15. // ==/UserScript==
  16.  
  17. ((__CONTEXT__) => {
  18.  
  19. // Purpose 1: Remove Obsolete Flags
  20. // Purpose 2: Remove Flags bring no visual difference
  21. // Purpose 3: Enable Flags bring performance boost
  22.  
  23. const DISABLE_CINEMATICS = false; // standard design
  24. const NO_SerializedExperiment = false;
  25. const KEEP_PLAYER_QUALITY_STICKY = true; // see https://gf.qytechs.cn/scripts/471033/
  26. const DISABLE_serializedExperimentIds = true;
  27. const DISABLE_serializedExperimentFlags = true;
  28.  
  29.  
  30. // const ALLOW_FLAGS_202404_flags11 = new Set([
  31. // // 'use_core_sm',
  32. // // 'use_new_cml',
  33. // // 'web_api_url',
  34. // ]);
  35.  
  36. const ENABLE_EXPERIMENT_FLAGS_MAINTAIN_STABLE_LIST = {
  37. defaultValue: true, // performance boost
  38. useExternal: () => typeof localStorage.EXPERIMENT_FLAGS_MAINTAIN_STABLE_LIST !== 'undefined',
  39. externalValue: () => (+localStorage.EXPERIMENT_FLAGS_MAINTAIN_STABLE_LIST ? true : false)
  40. };
  41. const ENABLE_EXPERIMENT_FLAGS_MAINTAIN_REUSE_COMPONENTS = {
  42. defaultValue: true, // not sure
  43. useExternal: () => typeof localStorage.EXPERIMENT_FLAGS_MAINTAIN_REUSE_COMPONENTS !== 'undefined',
  44. externalValue: () => (+localStorage.EXPERIMENT_FLAGS_MAINTAIN_REUSE_COMPONENTS ? true : false)
  45. };
  46. const ENABLE_EXPERIMENT_FLAGS_DEFER_DETACH = {
  47. defaultValue: true, // not sure
  48. useExternal: () => typeof localStorage.ENABLE_EXPERIMENT_FLAGS_DEFER_DETACH !== 'undefined',
  49. externalValue: () => (+localStorage.ENABLE_EXPERIMENT_FLAGS_DEFER_DETACH ? true : false)
  50. };
  51.  
  52. const ALLOW_ALL_LIVE_CHATS_FLAGS = true;
  53.  
  54.  
  55.  
  56. // TBC
  57. // kevlar_tuner_should_always_use_device_pixel_ratio
  58. // kevlar_tuner_should_clamp_device_pixel_ratio
  59. // kevlar_tuner_clamp_device_pixel_ratio
  60. // kevlar_tuner_should_use_thumbnail_factor
  61. // kevlar_tuner_thumbnail_factor
  62. // kevlar_tuner_min_thumbnail_quality
  63. // kevlar_tuner_max_thumbnail_quality
  64.  
  65. // kevlar_tuner_should_test_visibility_time_between_jobs
  66. // kevlar_tuner_visibility_time_between_jobs_ms
  67.  
  68. // kevlar_tuner_default_comments_delay
  69. // kevlar_tuner_run_default_comments_delay
  70.  
  71. let settled = null;
  72. // cinematic feature is no longer an experimential feature.
  73. // It has been officially implemented.
  74. // To disable cinematics, the user shall use other userscripts or just turn off the option in the video options.
  75.  
  76. const getSettingValue = (fm) => fm.useExternal() ? fm.externalValue() : fm.defaultValue;
  77.  
  78. const win = this instanceof Window ? this : window;
  79.  
  80. // Create a unique key for the script and check if it is already running
  81. const hkey_script = 'jmimcvowrlzl';
  82. if (win[hkey_script]) throw new Error('Duplicated Userscript Calling'); // avoid duplicated scripting
  83. win[hkey_script] = true;
  84.  
  85. /** @type {globalThis.PromiseConstructor} */
  86. const Promise = ((async () => { })()).constructor;
  87.  
  88. let isMainWindow = false;
  89. let mzFlagDetected = new Set();
  90. let zPlayerKevlar = false;
  91. try {
  92. isMainWindow = window.document === window.top.document
  93. } catch (e) { }
  94.  
  95. function fixSerializedExperiment(conf) {
  96.  
  97. if (DISABLE_serializedExperimentIds && typeof conf.serializedExperimentIds === 'string') {
  98. let ids = conf.serializedExperimentIds.split(',');
  99. let newIds = [];
  100. for (const id of ids) {
  101. let keep = false;
  102. if (keep) {
  103. newIds.push(id);
  104. }
  105. }
  106. conf.serializedExperimentIds = newIds.join(',');
  107. }
  108.  
  109. if (DISABLE_serializedExperimentFlags && typeof conf.serializedExperimentFlags === 'string') {
  110. const fg = conf.serializedExperimentFlags;
  111. const rx = /(^|&)(\w+)=([^=&|\s\{\}\[\]\(\)?]*)/g;
  112. let res = [];
  113. for (let m; m = rx.exec(fg);) {
  114. let key = m[2];
  115. let value = m[3];
  116. let keep = false;
  117. if (KEEP_PLAYER_QUALITY_STICKY) {
  118. if (key === 'html5_exponential_memory_for_sticky' || key.startsWith('h5_expr_')) {
  119. keep = true;
  120. }
  121. }
  122. if (!DISABLE_CINEMATICS) {
  123. if (key === 'web_cinematic_watch_settings') {
  124. keep = true;
  125. }
  126. }
  127. if (keep) res.push(`${key}=${value}`);
  128. }
  129. conf.serializedExperimentFlags = res.join('&');
  130. }
  131.  
  132. }
  133.  
  134. const cachedSetFn=(o) => {
  135.  
  136. const { use_maintain_stable_list, use_maintain_reuse_components, use_defer_detach } = o;
  137.  
  138. const BY_PASS = [
  139.  
  140. 'enable_profile_cards_on_comments',
  141.  
  142. 'suppress_error_204_logging',
  143. ...(!DISABLE_CINEMATICS ? [
  144.  
  145. 'kevlar_measure_ambient_mode_idle',
  146. 'kevlar_watch_cinematics_invisible',
  147. 'web_cinematic_theater_mode',
  148. 'web_cinematic_fullscreen',
  149.  
  150. 'enable_cinematic_blur_desktop_loading',
  151. 'kevlar_watch_cinematics',
  152. 'web_cinematic_masthead',
  153. 'web_watch_cinematics_preferred_reduced_motion_default_disabled'
  154.  
  155. ] : []),
  156.  
  157. 'live_chat_web_enable_command_handler',
  158. 'live_chat_channel_activity',
  159. 'live_chat_web_input_update',
  160.  
  161.  
  162. ...(ALLOW_ALL_LIVE_CHATS_FLAGS ? [
  163.  
  164. 'live_chat_banner_expansion_fix',
  165. 'live_chat_enable_mod_view',
  166. 'live_chat_enable_qna_banner_overflow_menu_actions',
  167. 'live_chat_enable_qna_channel',
  168. 'live_chat_enable_send_button_in_slow_mode',
  169. 'live_chat_filter_emoji_suggestions',
  170. 'live_chat_increased_min_height',
  171. 'live_chat_over_playlist',
  172. 'live_chat_web_use_emoji_manager_singleton',
  173. 'live_chat_whole_message_clickable',
  174.  
  175. 'live_chat_emoji_picker_toggle_state',
  176. 'live_chat_enable_command_handler_resolver_map',
  177. 'live_chat_enable_controller_extraction',
  178. 'live_chat_enable_rta_manager',
  179. 'live_chat_require_space_for_autocomplete_emoji',
  180. 'live_chat_unclickable_message',
  181.  
  182. ]:[]),
  183.  
  184. 'kevlar_rendererstamper_event_listener', // https://github.com/cyfung1031/userscript-supports/issues/11
  185.  
  186. // kevlar_enable_up_arrow - no use
  187. // kevlar_help_use_locale - might use
  188. // kevlar_refresh_gesture - might use
  189. // kevlar_smart_downloads - might use
  190. // kevlar_thumbnail_fluid
  191. 'kevlar_ytb_live_badges',
  192.  
  193. ...(!use_maintain_stable_list ? [
  194. 'kevlar_tuner_should_test_maintain_stable_list',
  195. 'kevlar_should_maintain_stable_list',
  196. 'kevlar_tuner_should_maintain_stable_list', // fallback
  197.  
  198.  
  199. ] : []),
  200.  
  201.  
  202. ...(!use_maintain_reuse_components ? [
  203.  
  204. 'kevlar_tuner_should_test_reuse_components',
  205. 'kevlar_tuner_should_reuse_components',
  206. 'kevlar_should_reuse_components' // fallback
  207.  
  208. ] : []),
  209.  
  210.  
  211. 'kevlar_system_icons',
  212.  
  213. // 'kevlar_prefetch_data_augments_network_data' continue;
  214.  
  215. // home page / watch page icons
  216. 'kevlar_three_dot_ink',
  217. 'kevlar_use_wil_icons',
  218. 'kevlar_home_skeleton',
  219.  
  220. 'kevlar_fluid_touch_scroll',
  221. 'kevlar_watch_color_update',
  222. 'kevlar_use_vimio_behavior', // home page - channel icon
  223.  
  224. // collapsed meta; no teaser, use latest collapsed meta design
  225. 'kevlar_structured_description_content_inline',
  226. 'kevlar_watch_metadata_refresh',
  227.  
  228. 'kevlar_watch_js_panel_height', // affect Tabview Youtube
  229.  
  230. 'shorts_desktop_watch_while_p2',
  231. 'web_button_rework',
  232. 'web_darker_dark_theme_live_chat',
  233. 'web_darker_dark_theme', // it also affect cinemtaics
  234.  
  235. // modern menu
  236. 'web_button_rework_with_live',
  237. 'web_fix_fine_scrubbing_drag',
  238.  
  239. // full screen -buggy
  240. 'external_fullscreen',
  241.  
  242. // minimize menu
  243. 'web_modern_buttons',
  244. 'web_modern_dialogs',
  245.  
  246. // Tabview Youtube - multiline transcript
  247. 'enable_mixed_direction_formatted_strings',
  248.  
  249.  
  250. // Notification Menu
  251. "kevlar_service_command_check",
  252.  
  253. // Live ChatRoom Visibility
  254. "live_chat_cow_visibility_set_up",
  255.  
  256. ].concat(
  257. [
  258. ]
  259. )
  260.  
  261. const s = new Set(BY_PASS);
  262.  
  263. return s;
  264.  
  265. };
  266. let cachedSet = null;
  267.  
  268. const hLooper = ((fn) => {
  269.  
  270. let nativeFnLoaded = false;
  271. let kc1 = 0;
  272.  
  273. const setIntervalW = setInterval;
  274. const clearIntervalW = clearInterval;
  275. let microDisconnectFn = null;
  276. let fStopLooper = false;
  277. const looperFn = () => {
  278. if (fStopLooper) return;
  279.  
  280. let config_ = null;
  281. let EXPERIMENT_FLAGS = null;
  282. try {
  283. config_ = yt.config_;
  284. EXPERIMENT_FLAGS = config_.EXPERIMENT_FLAGS
  285. } catch (e) { }
  286.  
  287. if (EXPERIMENT_FLAGS) {
  288.  
  289. fn(EXPERIMENT_FLAGS, config_);
  290.  
  291. if (microDisconnectFn) {
  292. let isYtLoaded = false;
  293. try {
  294. isYtLoaded = typeof ytcfg.set === 'function';
  295. } catch (e) { }
  296. if (isYtLoaded) {
  297. microDisconnectFn();
  298. }
  299. }
  300.  
  301. }
  302.  
  303. let playerKevlar = null;
  304.  
  305. try {
  306. playerKevlar = ytcfg.data_.WEB_PLAYER_CONTEXT_CONFIGS.WEB_PLAYER_CONTEXT_CONFIG_ID_KEVLAR_WATCH;
  307. } catch (e) { }
  308.  
  309. if (playerKevlar && !zPlayerKevlar) {
  310. zPlayerKevlar = true;
  311.  
  312. if (NO_SerializedExperiment && typeof playerKevlar.serializedExperimentFlags === 'string' && typeof playerKevlar.serializedExperimentIds === 'string') {
  313. fixSerializedExperiment(playerKevlar);
  314. }
  315.  
  316.  
  317. }
  318.  
  319.  
  320.  
  321. };
  322.  
  323. const controller = {
  324. start() {
  325. kc1 = setIntervalW(looperFn, 1);
  326. (async () => {
  327. while (true && !nativeFnLoaded) {
  328. looperFn();
  329. if (fStopLooper) break;
  330. await (new Promise(requestAnimationFrame));
  331. }
  332. })();
  333. looperFn();
  334. },
  335. /**
  336. *
  337. * @param {Window} __CONTEXT__
  338. */
  339. setupForCleanContext(__CONTEXT__) {
  340.  
  341. const { requestAnimationFrame, setInterval, clearInterval, setTimeout, clearTimeout } = __CONTEXT__;
  342.  
  343. (async () => {
  344. while (true) {
  345. looperFn();
  346. if (fStopLooper) break;
  347. await (new Promise(requestAnimationFrame));
  348. }
  349. })();
  350.  
  351. let kc2 = setInterval(looperFn, 1);
  352.  
  353. const marcoDisconnectFn = () => {
  354. if (fStopLooper) return;
  355. Promise.resolve().then(() => {
  356. if (kc1 || kc2) {
  357. kc1 && clearIntervalW(kc1); kc1 = 0;
  358. kc2 && clearInterval(kc2); kc2 = 0;
  359. looperFn();
  360. }
  361. fStopLooper = true;
  362. });
  363. document.removeEventListener('yt-page-data-fetched', marcoDisconnectFn, false);
  364. document.removeEventListener('yt-navigate-finish', marcoDisconnectFn, false);
  365. document.removeEventListener('spfdone', marcoDisconnectFn, false);
  366. };
  367. document.addEventListener('yt-page-data-fetched', marcoDisconnectFn, false);
  368. document.addEventListener('yt-navigate-finish', marcoDisconnectFn, false);
  369. document.addEventListener('spfdone', marcoDisconnectFn, false);
  370.  
  371.  
  372. function onReady() {
  373. if (!fStopLooper) {
  374. setTimeout(() => {
  375. !fStopLooper && marcoDisconnectFn();
  376. }, 1000);
  377. }
  378. }
  379.  
  380. Promise.resolve().then(() => {
  381. if (document.readyState !== 'loading') {
  382. onReady();
  383. } else {
  384. window.addEventListener("DOMContentLoaded", onReady, false);
  385. }
  386. });
  387.  
  388. nativeFnLoaded = true;
  389.  
  390. microDisconnectFn = () => Promise.resolve(marcoDisconnectFn).then(setTimeout);
  391.  
  392. }
  393. };
  394.  
  395. return controller;
  396. })((EXPERIMENT_FLAGS, config_) => {
  397.  
  398. if (!EXPERIMENT_FLAGS) return;
  399.  
  400. if (!settled) {
  401. settled = {
  402. use_maintain_stable_list: getSettingValue(ENABLE_EXPERIMENT_FLAGS_MAINTAIN_STABLE_LIST),
  403. use_maintain_reuse_components: getSettingValue(ENABLE_EXPERIMENT_FLAGS_MAINTAIN_REUSE_COMPONENTS),
  404. use_defer_detach: getSettingValue(ENABLE_EXPERIMENT_FLAGS_DEFER_DETACH),
  405. }
  406. if (settled.use_maintain_stable_list) Promise.resolve().then(() => console.debug("use_maintain_stable_list"));
  407. if (settled.use_maintain_reuse_components) Promise.resolve().then(() => console.debug("use_maintain_reuse_components"));
  408. if (settled.use_defer_detach) Promise.resolve().then(() => console.debug("use_defer_detach"));
  409. }
  410. const { use_maintain_stable_list, use_maintain_reuse_components, use_defer_detach } = settled;
  411.  
  412. cachedSet = cachedSet || cachedSetFn({ use_maintain_stable_list, use_maintain_reuse_components, use_defer_detach });
  413.  
  414. let mps = [];
  415.  
  416. setTimeout(async ()=>{
  417.  
  418. if(!mps.length) return;
  419. let ezz = new Set();
  420. let e1 = 999;
  421. let e2 = -999;
  422. for(const mp of mps){
  423. for(const k of mp){
  424. ezz.add(k);
  425. const kl= k.length;
  426. if(kl<e1) e1=kl;
  427. if(kl>e2) e2=kl;
  428. }
  429. }
  430. mps.length = 0;
  431.  
  432. if(!ezz.size) return;
  433.  
  434. await new Promise(r => window.setTimeout(r, 1));
  435. let qt = Date.now();
  436.  
  437. console.log('EXPERIMENT_FLAGS', [e1,e2, ezz.size]);
  438.  
  439. let mf = false;
  440. const obj = JSON.parse(localStorage['bpghn01'] || '{}');
  441. for(const e of ezz){
  442. if(obj[e])continue;
  443. obj[e] = qt;
  444. mf= true;
  445. }
  446. if(mf){
  447.  
  448. localStorage['bpghn01'] = JSON.stringify( obj);
  449. }
  450.  
  451. // await new Promise(r => window.setTimeout(r, 1));
  452.  
  453. const getEFT = function(after, offset){
  454.  
  455.  
  456. after = typeof after === 'string' ? new Date(after) : after;
  457. let afterValue = +after;
  458.  
  459.  
  460. let arr = Object.entries(obj).map(e=>{
  461. return {key: e[0], date: e[1], len:e[0].length };
  462. }).sort((a,b)=>{
  463. return a.date < b.date ? 1 : a.date>b.date ? -1 : a.len < b.len ? 1 :a.len>b.len ? -1 : `${a.key}`.localeCompare(`${b.key}`) ;
  464. });
  465.  
  466. if (afterValue > 0) {
  467. arr = arr.filter(e => {
  468. return e.date >= afterValue + offset;
  469. })
  470. }
  471.  
  472. return [arr, after, afterValue];
  473.  
  474. }
  475.  
  476. window.log_EXPERIMENT_FLAGS_Tamer = function(after, toString){
  477.  
  478. let [arr, after_, afterValue] =getEFT(after, -86400000);
  479.  
  480. const r = {
  481. "!log": arr,
  482. after: afterValue > 0 ? new Date(afterValue) : null
  483. };
  484. console.log("log_EXPERIMENT_FLAGS_Tamer", toString ? JSON.stringify(r) : r);
  485. }
  486.  
  487. window.kl_EXPERIMENT_FLAGS_Tamer = function (after, kl) {
  488.  
  489.  
  490. let [arr, after_, afterValue] =getEFT(after, -86400000);
  491.  
  492. arr = arr.filter(e => {
  493. return e.len === kl
  494. });
  495.  
  496. return arr.map(e => e.key).join('|')
  497.  
  498.  
  499. }
  500.  
  501.  
  502. }, 800);
  503.  
  504. const setFalseFn = (EXPERIMENT_FLAGS) => {
  505.  
  506. let ezz = new Set();
  507.  
  508. for (const [key, value] of Object.entries(EXPERIMENT_FLAGS)) {
  509.  
  510.  
  511. if (value === true) {
  512. // if(key.indexOf('modern')>=0 || key.indexOf('enable')>=0 || key.indexOf('theme')>=0 || key.indexOf('skip')>=0 || key.indexOf('ui')>=0 || key.indexOf('observer')>=0 || key.indexOf('polymer')>=0 )continue;
  513.  
  514. if (mzFlagDetected.has(key)) continue;
  515. mzFlagDetected.add(key);
  516. const kl = key.length;
  517. const kl7 = kl % 7;
  518. const kl5 = kl % 5;
  519. const kl3 = kl % 3;
  520. const kl2 = kl % 2;
  521.  
  522. if(cachedSet.has(key)) continue;
  523.  
  524. ezz.add(key);
  525.  
  526. // console.log(key)
  527. EXPERIMENT_FLAGS[key] = false;
  528. }
  529. }
  530. mps.push(ezz);
  531. ezz = null;
  532.  
  533. }
  534.  
  535. setFalseFn(EXPERIMENT_FLAGS);
  536. if (config_.EXPERIMENTS_FORCED_FLAGS) setFalseFn(config_.EXPERIMENTS_FORCED_FLAGS);
  537.  
  538. EXPERIMENT_FLAGS.desktop_delay_player_resizing = false;
  539. EXPERIMENT_FLAGS.web_animated_like = false;
  540. EXPERIMENT_FLAGS.web_animated_like_lazy_load = false;
  541.  
  542. if (use_maintain_stable_list) {
  543. EXPERIMENT_FLAGS.kevlar_tuner_should_test_maintain_stable_list = true;
  544. EXPERIMENT_FLAGS.kevlar_should_maintain_stable_list = true;
  545. EXPERIMENT_FLAGS.kevlar_tuner_should_maintain_stable_list = true; // fallback
  546. }
  547.  
  548. if (use_maintain_reuse_components) {
  549. EXPERIMENT_FLAGS.kevlar_tuner_should_test_reuse_components = true;
  550. EXPERIMENT_FLAGS.kevlar_tuner_should_reuse_components = true;
  551. EXPERIMENT_FLAGS.kevlar_should_reuse_components = true; // fallback
  552. }
  553.  
  554. if (use_defer_detach) {
  555. EXPERIMENT_FLAGS.kevlar_tuner_should_defer_detach = true;
  556. }
  557.  
  558. // EXPERIMENT_FLAGS.kevlar_prefetch_data_augments_network_data = true; // TBC
  559. });
  560.  
  561. hLooper.start();
  562.  
  563.  
  564. const cleanContext = async (win) => {
  565. const waitFn = requestAnimationFrame; // shall have been binded to window
  566. try {
  567. let mx = 16; // MAX TRIAL
  568. const frameId = 'vanillajs-iframe-v1'
  569. let frame = document.getElementById(frameId);
  570. let removeIframeFn = null;
  571. if (!frame) {
  572. frame = document.createElement('iframe');
  573. frame.id = 'vanillajs-iframe-v1';
  574. frame.sandbox = 'allow-same-origin'; // script cannot be run inside iframe but API can be obtained from iframe
  575. let n = document.createElement('noscript'); // wrap into NOSCRPIT to avoid reflow (layouting)
  576. n.appendChild(frame);
  577. while (!document.documentElement && mx-- > 0) await new Promise(waitFn); // requestAnimationFrame here could get modified by YouTube engine
  578. const root = document.documentElement;
  579. root.appendChild(n); // throw error if root is null due to exceeding MAX TRIAL
  580. removeIframeFn = (setTimeout) => {
  581. const removeIframeOnDocumentReady = (e) => {
  582. e && win.removeEventListener("DOMContentLoaded", removeIframeOnDocumentReady, false);
  583. win = null;
  584. setTimeout(() => {
  585. n.remove();
  586. n = null;
  587. }, 200);
  588. }
  589. if (document.readyState !== 'loading') {
  590. removeIframeOnDocumentReady();
  591. } else {
  592. win.addEventListener("DOMContentLoaded", removeIframeOnDocumentReady, false);
  593. }
  594. }
  595. }
  596. while (!frame.contentWindow && mx-- > 0) await new Promise(waitFn);
  597. const fc = frame.contentWindow;
  598. if (!fc) throw "window is not found."; // throw error if root is null due to exceeding MAX TRIAL
  599. const { requestAnimationFrame, setInterval, setTimeout, clearInterval, clearTimeout } = fc;
  600. const res = { requestAnimationFrame, setInterval, setTimeout, clearInterval, clearTimeout };
  601. for (let k in res) res[k] = res[k].bind(win); // necessary
  602. if (removeIframeFn) Promise.resolve(res.setTimeout).then(removeIframeFn);
  603. return res;
  604. } catch (e) {
  605. console.warn(e);
  606. return null;
  607. }
  608. };
  609.  
  610. cleanContext(win).then(__CONTEXT__ => {
  611.  
  612. const { requestAnimationFrame, setInterval, clearInterval, setTimeout, clearTimeout } = __CONTEXT__;
  613.  
  614. hLooper.setupForCleanContext(__CONTEXT__)
  615.  
  616. });
  617.  
  618.  
  619. if (isMainWindow) {
  620.  
  621. console.groupCollapsed(
  622. "%cYouTube EXPERIMENT_FLAGS Tamer",
  623. "background-color: #EDE43B ; color: #000 ; font-weight: bold ; padding: 4px ;"
  624. );
  625.  
  626. console.log("Script is loaded.");
  627. console.log("This might affect the new features when YouTube rolls them out to general users.");
  628. console.log("If you found any issue in using YouTube, please disable this script to check whether the issue is due to this script or not.");
  629.  
  630. console.groupEnd();
  631.  
  632. }
  633.  
  634. })(null);

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址