// ==UserScript== // @name YouTube JS Engine Tamer // @name:ja YouTube JS Engine Tamer // @name:zh-TW YouTube JS Engine Tamer // @name:zh-CN YouTube JS Engine Tamer // @namespace UserScripts // @version 0.30.10 // @match https://www.youtube.com/* // @match https://www.youtube-nocookie.com/embed/* // @match https://studio.youtube.com/live_chat* // @license MIT // @author CY Fung // @icon https://raw.githubusercontent.com/cyfung1031/userscript-supports/main/icons/yt-engine.png // @grant none // @require https://cdn.jsdelivr.net/gh/cyfung1031/userscript-supports@c2b707e4977f77792042d4a5015fb188aae4772e/library/nextBrowserTick.min.js // @run-at document-start // @unwrap // @inject-into page // @allFrames true // @exclude /^https?://\S+\.(txt|png|jpg|jpeg|gif|xml|svg|manifest|log|ini)[^\/]*$/ // // @description To enhance YouTube performance by modifying YouTube JS Engine // @description:ja YouTubeのJSエンジンを変更してパフォーマンスを向上させる // @description:zh-TW 修改 YouTube 的 JS 引擎以提升效能 // @description:zh-CN 修改 YouTube 的 JS 引擎以提升性能 // // @downloadURL none // ==/UserScript== (() => { /** @type {WeakMapConstructor} */ const WeakMap = window.WeakMapOriginal || window.WeakMap; const NATIVE_CANVAS_ANIMATION = false; // for #cinematics const FIX_schedulerInstanceInstance = 2 | 4; const FIX_yt_player = true; // DONT CHANGE const FIX_Animation_n_timeline = true; const NO_PRELOAD_GENERATE_204 = false; const ENABLE_COMPUTEDSTYLE_CACHE = true; const NO_SCHEDULING_DUE_TO_COMPUTEDSTYLE = true; const CHANGE_appendChild = true; // discussions#236759 const FIX_bind_self_this = false; // EXPERIMENTAL !!!!! this affect page switch after live ends const FIX_weakMap_weakRef = false; // EXPERIMENTAL !!!!! Might Incompatible to some userscripts (as the strong relationship is removed) const FIX_error_many_stack = true; // should be a bug caused by uBlock Origin // const FIX_error_many_stack_keepAliveDuration = 200; // ms // const FIX_error_many_stack_keepAliveDuration_check_if_n_larger_than = 8; const FIX_Iframe_NULL_SRC = false; const IGNORE_bindAnimationForCustomEffect = true; // prevent `v.bindAnimationForCustomEffect(this);` being executed const FIX_ytdExpander_childrenChanged = true; const FIX_paper_ripple_animate = true; const FIX_avoid_incorrect_video_meta = true; // omit the incorrect yt-animated-rolling-number const FIX_avoid_incorrect_video_meta_emitterBehavior = true; const FIX_doIdomRender = true; const FIX_Shady = true; // [[ 2024.04.24 ]] const MODIFY_ShadyDOM_OBJ = true; // << if MODIFY_ShadyDOM_OBJ >> const WEAKREF_ShadyDOM = true; const OMIT_ShadyDOM_EXPERIMENTAL = 1 | 0; // 1 => enable; 2 => composedPath const OMIT_ShadyDOM_settings = 0 | 0 | 0; // 1: inUse; 2: handlesDynamicScoping; 4: force // {{ PRELIM TESTING PURPOSE }} // << end >> const WEAK_REF_BINDING_CONTROL = 1 | 2; // 2 - conflict control with ShadyDOM weakref const FIX_ytAction_ = true; // ytd-app const FIX_onVideoDataChange = false; // const FIX_onClick = true; const FIX_onStateChange = true; const FIX_onLoopRangeChange = true; // const FIX_maybeUpdateFlexibleMenu = true; // ytd-menu-renderer const FIX_VideoEVENTS_v2 = true; // true might cause bug in switching page const ENABLE_discreteTasking = false; // removed since 0.20.0 const FIX_stampDomArray_ = true; // v0.30.0 const FIX_stampDomArray = FIX_stampDomArray_ && typeof WeakRef === "function" && typeof FinalizationRegistry === "function"; const stampDomArray_MemoryFix_Flag001 = false; const XFlag = true; // DON'T CHANGE const MemoryFix_Flag002 = 1 | 2 | 4 | 8 | 0 | 32 | 64 | 0 | 256; // 32 required for new stampDomArray // 128 to be tested const FIX_perfNow = true; // history state issue; see https://bugzilla.mozilla.org/show_bug.cgi?id=1756970 const ENABLE_ASYNC_DISPATCHEVENT = false; // problematic const FIX_Polymer_dom = true; const FIX_Polymer_AF = true; const SCRIPTLET_REMOVE_PRUNE_propNeedles = true; // brave scriptlet related const DEBUG_removePrune = false; // true for DEBUG const FIX_XHR_REQUESTING = true; const LOG_FETCHMETA_UPDATE = false; // for DEBUG const IGNORE_bufferhealth_CHECK = false; // experimental; true will make "Stats for nerds" no info. const DENY_requestStorageAccess = true; // remove document.requestStorageAccess const DISABLE_IFRAME_requestStorageAccess = true; // no effect if DENY_requestStorageAccess is true const DISABLE_COOLDOWN_SCROLLING = true; // YT cause scroll hang in MacOS const FIX_removeChild = true; const FIX_fix_requestIdleCallback_timing = true; const HOOK_CSSPD_LEFT = true; // global css hack for style.left const FORCE_NO_REUSEABLE_ELEMENT_POOL = true; const FIX_TRANSCRIPT_SEGMENTS = true; // Based on Tabview Youtube's implementation const FIX_POPUP_UNIQUE_ID = true; // currently only for channel about popup; // ----------------------------- POPUP UNIQUE ID ISSUE ----------------------------- // example. https://www.youtube.com/channel/UCgPev1KKSCMbnNRsvN83Hag/about // first tp-yt-paper-dialog: show once the page is loaded. // second tp-yt-paper-dialog: click "...more" // third tp-yt-paper-dialog: click "... and 3 more links" // check with document.querySelectorAll('ytd-popup-container tp-yt-paper-dialog').length // currently, uniqueId is preassigned by the network resolveCommand. // so don't modify the source side, just modify the display side (popup display) via handleOpenPopupAction // other related functions e.g. handleClosePopupCommand_, getAndMaybeCreatePopup_, handleClosePopupAction_, getAndMaybeCreatePopup_ // handleOpenPopupAction -> createCacheKey // handleClosePopupAction_ -> createCacheKey // handleGetPopupOpenedAction_ -> createCacheKey // getAndMaybeCreatePopup_ -> createCacheKey // closePopup -> createCacheKey // yt-close-popup-command -> handleClosePopupCommand_ // ensurePopup_ -> getAndMaybeCreatePopup_ // yt-close-popup-action -> handleClosePopupAction_ // closePopup -> handleClosePopupAction_ // handleOpenPopupAction -> handleClosePopupAction_ // handleClosePopupCommand_ -> handleClosePopupAction_ // closeSheet -> handleClosePopupAction_("yt-sheet-view-model") // yt-open-popup-action -> handleOpenPopupAction // yt-close-popup-action -> handleClosePopupAction_ -> createCacheKey // yt-close-popup-command -> handleClosePopupCommand_ -> handleClosePopupAction_ -> createCacheKey // Experimental flag "ytpopup_disable_default_html_caching" is disabled by default. // Not sure enabling it can make GC or not (Yt Components are usually not GC-able) // ----------------------------- POPUP UNIQUE ID ISSUE ----------------------------- const FIX_DOM_IF_REPEAT = true; // semi-experimental (added in 0.17.0) const FIX_DOM_IF_TEMPLATE = true; // const FIX_DOM_REPEAT_TEMPLATE = true; // to be implemented const FIX_DOM_IFREPEAT_RenderDebouncerChange = false; // semi-experimental (added in 0.17.0) // found buggy for chat ticker sizing const DEBUG_DBR847 = false; const DEBUG_xx847 = false; const FIX_DOM_IFREPEAT_RenderDebouncerChange_SET_TO_PROPNAME = true; // default true. false might be required for future change const DEBUG_renderDebounceTs = false; const FIX_ICON_RENDER = true; const FIX_VIDEO_PLAYER_MOUSEHOVER_EVENTS = true; // avoid unnecessary reflows due to cursor moves on the web player. /* FIX_DOM_IFREPEAT_RenderDebouncerChange avoid Polymer.flush // https://www.youtube.com/s/desktop/26a583e4/jsbin/live_chat_polymer.vflset/live_chat_polymer.js var Is = function() { do { var a = window.ShadyDOM && ShadyDOM.flush(); window.ShadyCSS && window.ShadyCSS.ScopingShim && window.ShadyCSS.ScopingShim.flush(); var b = NNa() } while (a || b) }; , NNa = function() { var a = !!ts.size; ts.forEach(function(b) { try { b.flush() } catch (c) { setTimeout(function() { throw c }) } }); return a }; // why flush twice after all ts are completed? (!!ts.size => true => loop again) // this coding logic should be incorrect (mistake). */ // ----------------------------- Shortkey Keyboard Control ----------------------------- // dependency: FIX_yt_player const FIX_SHORTCUTKEYS = 2; // 0 - no fix; 1 - basic fix; 2 - advanced fix // [0] no fix - not recommended // [1] basic fix - just fix the global focus detection variable // [2] advanced fix - call the shortcut actions directly, auto foucs change, direct control of spacebar behavior, etc // (note) 0 or 1 if you find conflict with other userscripts/plugin const CHANGE_SPEEDMASTER_SPACEBAR_CONTROL = 0; // 0 - disable; 1 - force true; 2 - force false const USE_IMPROVED_PAUSERESUME_UNDER_NO_SPEEDMASTER = true; // only for SPEEDMASTER = false & FIX_SHORTCUTKEYS = 2 const PROP_OverReInclusion_AVOID = true; const PROP_OverReInclusion_DEBUGLOG = false; const PROP_OverReInclusion_LIST = new Set([ 'hostElement72', 'parentComponent72', 'localVisibilityObserver_72', 'cachedProviderNode_72', '__template72', '__templatizeOwner72', '__templateInfo72', '__dataHost72', '__CE_shadowRoot72', 'elements_72', 'ky36', 'kz62', 'm822', // To be reviewed. // chat messages 'disabled', 'allowedProps', 'filledButtonOverrides', 'openPopupConfig', 'supportsInlineActionButtons', 'allowedProps', 'dimension', 'loadTime', 'pendingPaint', 'countdownDurationMs', 'countdownMs', 'lastCountdownTimeMs', 'rafId', 'playerProgressSec', 'detlaSincePausedSecs', 'behaviorActionMap', 'selected', 'maxLikeCount', 'maxReplyCount', 'isMouseOver', 'respectLangDir', 'noEndpoints', 'objectURL', 'buttonOverrides', 'queuedMessages', 'STEP', 'BLOCK_ON', 'MIN_PROGESS', 'MAX_PROGESS', 'DISMISSED_CONTENT_KEYSPACE', 'followUpDialogPromise', 'followUpDialogPromiseResolve', 'followUpDialogPromiseReject', 'hoverJobId', 'JSC$14573_touched', // tbc 'toggleable', 'isConnected', 'scrollDistance', 'dragging', 'dragMouseStart', 'dragOffsetStart', 'containerWidthDiff', 'disableDeselectEvent', 'emojiSize', 'buttonOverride', 'shouldUseStickyPreferences', 'longPressTimeoutId', // others 'observeVisibleOption', 'observeHiddenOption', 'observePrescanOption', 'visibilityMonitorKeys', // 'filledButtonOverrides', 'openPopupConfig', 'supportsInlineActionButtons', 'observeVisibleOption', 'observeHiddenOption', 'observePrescanOption', 'visibilityMonitorKeys', // 'dimension', 'loadTime', 'pendingPaint', // 'disabled', 'allowedProps', // 'enableMssLazyLoad', 'popupContainerConfig', 'actionRouterNode', 'actionRouterIsRoot', 'actionMap', 'dynamicActionMap', // 'actionMap', // 'sharedTooltipPosition', 'sharedTooltipAnimationDelay', 'disableEmojiPickerIncrementalLoading', 'useResolveCommand', 'activeRequest', 'popoutWindowCheckIntervalId', 'supportedTooltipTargets', 'closeActionPanelTimerId', 'delayCloseActionPanelTimerId', 'tooltipTimerIds', 'queuedTooltips', 'isPopupConfigReady', 'popoutWindow', 'actionMap', 'clearTimeout', 'switchTemplateAtRegistration', 'hasUnmounted', 'switchTemplateAtRegistration', 'stopKeyboardEventPropagation', 'tangoConfiguration', 'itemIdToDockDurationMap', 'actionMap', 'emojiManager', 'inputMethodEditorActive', 'suggestionIndex', 'JSC$10745_lastSuggestionRange', 'actionMap', 'asyncHandle', 'shouldAnimateIn', 'lastFrameTimestamp', 'scrollClampRaf', 'scrollRatePixelsPerSecond', 'scrollStartTime', 'scrollStopHandle' // 'buttonOverrides', 'queuedMessages', 'clearTimeout', 'actionMap', // 'stopKeyboardEventPropagation', 'emojiSize', // 'switchTemplateAtRegistration', 'hasUnmounted', // 'buttonOverrides', 'queuedMessages', 'clearTimeout', 'actionMap', // 'isReusable', 'tangoConfiguration', // 'itemIdToDockDurationMap', 'bottomAlignMessages', 'actionMap', // */ ]); // const CAN_TUNE_VOLUMN_AFTER_RESUME_OR_PAUSE = false; // NO USE; TO BE REVIEWED // ----------------------------- Shortkey Keyboard Control ----------------------------- /* window.addEventListener('edm',()=>{ let p = [...this.onerror.errorTokens][0].token; (()=>{ console.log(p); throw new Error(p);console.log(334,p) })() }); window.addEventListener('edn',()=>{ let p = [...this.onerror.errorTokens][0].token+"X"; (()=>{ console.log(p); throw new Error(p);console.log(334,p) })() }); window.addEventListener('edr',()=>{ let p = '123'; (()=>{ console.log(p); throw new Error(p);console.log(334,p) })() }); */ // only for macOS with Chrome/Firefox 100+ const advanceLogging = typeof AbortSignal !== 'undefined' && typeof (AbortSignal || 0).timeout === 'function' && typeof navigator !== 'undefined' && /\b(Macintosh|Mac\s*OS)\b/i.test((navigator || 0).userAgent || ''); const win = this instanceof Window ? this : window; // Create a unique key for the script and check if it is already running const hkey_script = 'jswylcojvzts'; if (win[hkey_script]) throw new Error('Duplicated Userscript Calling'); // avoid duplicated scripting win[hkey_script] = true; const wk = Symbol(); // const [setTimeoutX0, clearTimeoutX0] = [setTimeout, clearTimeout]; let BY_PASS_KEYBOARD_CONTROL = false; // const setImmediate = ((self || 0).jmt || 0).setImmediate; /** @type {(f: ()=>{})=>{}} */ const nextBrowserTick_ = nextBrowserTick; if (typeof nextBrowserTick_ !== "function" || (nextBrowserTick_.version || 0) < 2) { console.log('nextBrowserTick is not found.'); return; } let p59 = 0; const Promise = (async () => { })().constructor; const PromiseExternal = ((resolve_, reject_) => { const h = (resolve, reject) => { resolve_ = resolve; reject_ = reject }; return class PromiseExternal extends Promise { constructor(cb = h) { super(cb); if (cb === h) { /** @type {(value: any) => void} */ this.resolve = resolve_; /** @type {(reason?: any) => void} */ this.reject = reject_; } } }; })(); const FinalizationRegistry_ = typeof FinalizationRegistry !== "undefined" ? FinalizationRegistry : class FinalizationRegistry__ { constructor(callback = undefined) { } register(target, heldValue, unregisterToken = undefined) { } unregister(unregisterToken) { } } let ttpHTML = (s) => { ttpHTML = s => s; if (typeof trustedTypes !== 'undefined' && trustedTypes.defaultPolicy === null) { let s = s => s; trustedTypes.createPolicy('default', { createHTML: s, createScriptURL: s, createScript: s }); } return s; } /** @type { typeof HTMLElement } */ const HTMLElement_ = Reflect.getPrototypeOf(HTMLTitleElement); const nativeAppendE = HTMLElement_.prototype.append; const nativeRemoveE = HTMLElement_.prototype.remove; const DocumentFragment_ = DocumentFragment; const nativeAppendD = DocumentFragment_.prototype.append; const Node_ = Node; /** @param {number} x @param {number} d */ const toFixed2 = (x, d) => { let t = x.toFixed(d); let y = `${+t}`; return y.length > t.length ? t : y; } const isChatRoomURL = location.pathname.startsWith('/live_chat'); const TRANSLATE_DEBUG = false; let xdeadc00 = null; // a deteched node with __domApi let xlivec00 = null; // a deteched node with __domApi let removeTNodeRM = null; let removeTNodeBP = false; let FORCE_NO_REUSEABLE_ELEMENT_POOL_fired = false; const FORCE_NO_REUSEABLE_ELEMENT_POOL_fn = (mainCnt) => { if (FORCE_NO_REUSEABLE_ELEMENT_POOL_fired) return; FORCE_NO_REUSEABLE_ELEMENT_POOL_fired = true; if (typeof mainCnt.createComponent_ !== 'function' || mainCnt.createComponent_.length != 3) { console.warn('FORCE_NO_REUSEABLE_ELEMENT_POOL_fn failed.') return; } const mapGet = Map.prototype.get; const setHas = Set.prototype.has; /** @type {Map | null} */ let qcMap = null; Set.prototype.has = function (a) { if (a === 'dummy-4718') return false; // false to allow re-use? return setHas.call(this, a); } Map.prototype.get = function (a) { if (a === 'dummy-4718') qcMap = this; return mapGet.call(this, a); }; let r; try { r = mainCnt.createComponent_('dummy-4718', {}, true); } catch (e) { } Map.prototype.get = mapGet; Set.prototype.has = setHas; if (r && (r.nodeName || '').toLowerCase() === 'dummy-4718') { // clearInterval(ckId); // ckId = 0; if (qcMap !== null && qcMap instanceof Map) { console.log('[yt-js-engine-tamer] qcMap', qcMap); qcMap.__qcMap8781__ = true; const setArrayC = (c) => { if (c instanceof Array) { c.length = 0; c.push = function () { }; c.pop = function () { }; c.shift = function () { }; c.unshift = function () { }; c.splice = function () { }; c.sort = function () { }; c.reverse = function () { }; } } const cleaning = function (m) { m.forEach(setArrayC); m.clear(); } qcMap.set = function (b, c) { if (!this.__qcMap8781__) return Map.prototype.set.call(this, b, c); setArrayC(c); // console.log('qcMap.set', b, c); if (this.size > 0) { // play safe console.log('[yt-js-engine-tamer] qcMap', 'clear 01') cleaning(this); } } qcMap.get = function (b) { if (!this.__qcMap8781__) return Map.prototype.get.call(this, b); // console.log('qcMap.get', b); if (this.size > 0) { // play safe console.log('[yt-js-engine-tamer] qcMap', 'clear 02') cleaning(this); } } if (qcMap.size > 0) { console.log('[yt-js-engine-tamer] qcMap', 'clear 03') cleaning(qcMap); } } } r = null; qcMap = null; } const dispatchYtEvent = function (a, b, c, d) { d || (d = { bubbles: !0, cancelable: !1, composed: !0 }); c !== null && c !== void 0 && (d.detail = c); b = new CustomEvent(b, d); a.dispatchEvent(b); return b }; if (XFlag) { const cMap = new Set(); cMap.add = cMap.addOriginal || cMap.add; const yMap = new Set(); yMap.add = yMap.addOriginal || yMap.add; const ydMap = new Set(); ydMap.add = ydMap.addOriginal || ydMap.add; window.yMap = yMap; window.cMap = cMap; window.ydMap = ydMap; const constructAts = new Set(); constructAts.add = constructAts.addOriginal || constructAts.add; window.constructAts = constructAts; const kMap = new WeakMap(); const kRefProp = (wr, prop)=>{ let o = kRef(wr); return o ? o[prop] : null; } const wrObj = (objRef, props) => { let wr = mWeakRef(objRef); if (wr === objRef || !props || !props.length) return wr; let properties = {}; props.forEach(k => { properties[k] = { get() { return kRefProp(this, k) }, enumerable: false, configurable: true }; }); Object.defineProperties(wr, properties); return wr; } const sProtos = {}; const setupCProto = function (cProto) { if(cProto === Object.prototype) return; if (!kMap.get(cProto)) kMap.set(cProto, `protoKey0_${Math.floor(Math.random() * 314159265359 + 314159265359).toString(36)}_${Date.now()}`); cProto[kMap.get(cProto)] = true; // debugger; const constructAt = `\n\n${new Error().stack}\n\n`.replace(/[\r\n]([^\r\n]*?\.user\.js[^\r\n]*?[\r\n]+)+/g, '\n').replace(/[\r\n]([^\r\n.]+[\r\n]+)+/g, '\n').trim().split(/[\r\n]+/)[0]; constructAts.add(constructAt) if (MemoryFix_Flag002 & 32) { if (!cProto.dk322 && (cProto.__attachInstance || cProto.__detachInstance)) { fixDetachFn(cProto); } } if (cProto.stampDomArray_ && !cProto.__stampDX38__) { cProto.__stampDX38__ = true; if (cProto.stampDomArray_.length === 6) { const pendingFinishContainers = new Map(); const producerMap2 = new WeakMap(); const mutObs = new MutationObserver((mutations) => { if (pendingFinishContainers.size === 0) return; let dirtyProducers = new Map(); pendingFinishContainers.forEach((p, containerWr) => { const container = kRef(containerWr); if (!container) return pendingFinishContainers.delete(containerWr); const producerWr = producerMap2.get(container); if (!producerWr) return pendingFinishContainers.delete(containerWr); const producer = kRef(producerWr); if (!producer) return pendingFinishContainers.delete(containerWr); let dirtyVal = dirtyProducers.get(producer); if (dirtyVal === false) return; if (!dirtyVal) { const hostElement = producer.hostElement; if (!hostElement) return pendingFinishContainers.delete(containerWr); if (hostElement.querySelector('rp[yt-element-placholder], [ytx-stamping], [ytx-flushing]')) { dirtyProducers.set(producer, false); return; } } pendingFinishContainers.delete(containerWr); if (!dirtyVal) dirtyProducers.set(producer, (dirtyVal = new Set())); let o = dirtyVal; if (p === 'e') { o.add(container); } }); if (dirtyProducers.size === 0) return; dirtyProducers.forEach((o, producer) => { if (o instanceof Set) { const hostElement = producer.hostElement; hostElement.removeAttribute('ytx-debug-0173'); producer.markDirty && producer.markDirty(); for (const container of o) { dispatchYtEvent(hostElement, "yt-rendererstamper-finished", { container }); } o.clear(); } }); dirtyProducers.clear(); dirtyProducers = null; }); cProto.stampDomArray8581_ = cProto.stampDomArray_; cProto.stampDomArray_ = function (dataList, containerId, typeOrConfig, bReuse, bEventCb, bStableList) { const b = this.flushRenderStamperComponentBindings_; this.flushRenderStamperComponentBindings_ = function () { b.call(this); throw new Error('82919'); } let r, e_; try { r = this.stampDomArray8581_(dataList, containerId, typeOrConfig, bReuse, bEventCb, bStableList); } catch (e) { e_ = e; } if (Reflect.getPrototypeOf(this).flushRenderStamperComponentBindings_ === b) { delete this.flushRenderStamperComponentBindings_; } if (this.flushRenderStamperComponentBindings_ !== b) { this.flushRenderStamperComponentBindings_ = b; } if (e_ && e_.message !== '82919') throw e_; if (e_ && e_.message === '82919') { if (this.__byPass7409__ === true) { const producer = this; const container = producer.getStampContainer_(containerId); const hostElement = producer.hostElement; hostElement.setAttribute('ytx-debug-0173', '') if (!producer[wk]) producer[wk] = mWeakRef(producer); if (!container[wk]) container[wk] = mWeakRef(container); producerMap2.set(container, producer[wk]); const currentVal = pendingFinishContainers.get(container[wk]); const newVal = bEventCb ? 'e' : '1'; if (newVal === 'e' && currentVal !== newVal) { pendingFinishContainers.set(container[wk], newVal); } else if (newVal === '1' && !currentVal) { pendingFinishContainers.set(container[wk], newVal); } mutObs.observe(hostElement, { subtree: true, childList: true }); hostElement.appendChild(document.createComment('-')).remove(); } } return r; } if (MemoryFix_Flag002 & 1) { cProto.stampDomArray_.bind = sProtos.stampDomArray_$bind || (sProtos.stampDomArray_$bind = function (obj, ...args) { let wobj = obj[wk] || (obj[wk] = mWeakRef(obj)); return () => { const obj = kRef(wobj); let u = Reflect.apply(this, obj, args); args.length = 0; wobj = null; return u; }; }); } } } if (MemoryFix_Flag002 & 2) { if (cProto._setPendingProperty && !cProto.__setPropDX38__) { cProto.__setPropDX38__ = true; if (cProto._setPendingProperty.length === 3) { cProto._setPendingProperty.bind = sProtos._setPendingProperty$bind || (sProtos._setPendingProperty$bind = function (obj, ...args) { let wobj = obj[wk] || (obj[wk] = mWeakRef(obj)); return () => { const obj = kRef(wobj); let u = Reflect.apply(this, obj, args); args.length = 0; wobj = null; return u; }; }); } } } if (MemoryFix_Flag002 & 4) { if (cProto._runEffectsForTemplate && !cProto.__runEffectDX38__) { cProto.__runEffectDX38__ = true; if (cProto._runEffectsForTemplate.length === 4) { cProto._runEffectsForTemplate3858 = cProto._runEffectsForTemplate; cProto._runEffectsForTemplate = sProtos._runEffectsForTemplate || (sProtos._runEffectsForTemplate = function (c, d, e, g) { if (c && c.runEffects) { let wr = wrObj(c, ['propertyEffects', 'nodeList', 'firstChild']); // console.log(12837) if (!this[wk]) this[wk] = mWeakRef(this); if ((typeof (e || 0) === "object") && !e[wk]) e[wk] = mWeakRef(e); let cntWr = this[wk]; let eWr = (typeof (e || 0) === "object") ? e[wk] : e; c.runEffects((n, r) => { // console.log(12838) const cnt = kRef(cntWr); const e = kRef(eWr); if (cnt) { cnt._runEffectsForTemplate3858(wr, n, e, r); } wr = cntWr = d = e = g = null; }, d, g); } else { let { propertyEffects, nodeList, firstChild } = c; let o = { propertyEffects, nodeList, firstChild } this._runEffectsForTemplate3858(o, d, e, g); o.propertyEffects = o.nodeList = o.firstChild = null; propertyEffects = nodeList = firstChild = null; o = null; } }); cProto._runEffectsForTemplate.bind = sProtos._runEffectsForTemplate$bind || (sProtos._runEffectsForTemplate$bind = function (obj, ...args) { // console.log(12993, args) let wobj = obj[wk] || (obj[wk] = mWeakRef(obj)); return () => { const obj = kRef(wobj); let u = Reflect.apply(this, obj, args); args.length = 0; wobj = null; return u; }; }); } } } const cProtoConstructor = cProto.constructor; if (MemoryFix_Flag002 & 8) { if (cProtoConstructor._parseBindings && !cProtoConstructor.__parseBindingsDX38__) { cProtoConstructor.__parseBindingsDX38__ = true; ydMap.add(cProtoConstructor); if (cProtoConstructor._parseBindings.length === 2) { cProtoConstructor._parseBindings3858 = cProtoConstructor._parseBindings; cProtoConstructor._parseBindings = sProtos._parseBindings || (sProtos._parseBindings = function (c, d) { let p = this._parseBindings3858(c, d); this.__bindingsArrs__ = this.__bindingsArrs__ || []; if (p) this.__bindingsArrs__.push(p); return p; }); } } } /* a.prototype._initializeProperties = function() { if (Em && this.hasAttribute("disable-upgrade")) this.__isUpgradeDisabled = !0; else { var e = Object.getPrototypeOf(this); e.hasOwnProperty("__hasRegisterFinished") || (this._registered(), e.__hasRegisterFinished = !0); b.prototype._initializeProperties.call(this); this.root = this; this.created(); fpb && !this._legacyForceObservedAttributes && (this.hasAttributes() ? this._takeAttributes() : this.parentNode || (this.__needsAttributesAtConnected = !0)); this._applyListeners() } } */ /* bOa = function(a, b, c) { var d = bya(a.prototype, $Na, a.prototype.behaviors); d.prototype.is = b; d.prototype.localName = b; c && aOa(d, c); return function(e) { e && (d.prototype.hostElement = e); var g = new d; g.root = g; g.hostElement = e; return g } } */ } const symDH = Symbol(); const wfStore = new WeakMap(); const wrapF = function (f, key) { if (wfStore.get(f)) return wfStore.get(f); let g = function () { const o = kRef(this); if (!o) return; const cnt = insp(o); // if (cnt === o) return; // if (!('ready' in cnt)) return; return f.apply(o, arguments); }; g.key38 = key; g.originalFunc38 = f; g.__wrapF84__ = true; wfStore.set(f, g); wfStore.set(g, g); return g; }; if (MemoryFix_Flag002 & 16) { ['_createPropertyAccessor', '_addPropertyToAttributeMap', '_definePropertyAccessor', 'ready', '_initializeProperties', '_initializeInstanceProperties', '_setProperty', '_getProperty', '_setPendingProperty', '_isPropertyPending', '_invalidateProperties', '_enableProperties', '_flushProperties', '_shouldPropertiesChange', '_propertiesChanged', '_shouldPropertyChange', 'attributeChangedCallback', '_attributeToProperty', '_propertyToAttribute', '_valueToNodeAttribute', '_serializeValue', '_deserializeValue'].forEach(key => { Object.defineProperty(Node.prototype, key, { get() { return this[`__a0939${key}__`]; }, set(nv) { if (typeof nv !== 'function') return; const g = (nv.__wrapF84__) ? nv : wrapF(nv, key); this[`__a0939${key}__`] = g; return true; } }); }); } const fragQ = document.createDocumentFragment(); // for cleaup removal const fixDetachFn = (tpProto) => { if (tpProto.dk322) return; tpProto.dk322 = true; tpProto.__detachInstance994 = tpProto.__detachInstance; if (typeof tpProto.__detachInstance994 === 'function' && tpProto.__detachInstance994.length === 1) { tpProto.__detachInstance = function (a) { const u = this.__instances[a]; if (u && u.root) { const children = u ? u.children : null; if (children && children.length >= 1) { this.__detachInstance994(a); } } else if (u && !u.root) { const children = u ? u.children : null; if (children && children.length >= 1) { for (let i = 0, l = children.length; i < l; i++) { const node = children[i]; fragQ.appendChild(node); // for cleanup Promise.resolve(node).then(node => (node.parentNode === fragQ) && !!node.remove()); } } } return u; } } tpProto.__attachInstance994 = tpProto.__attachInstance; if (typeof tpProto.__attachInstance994 === 'function' && tpProto.__attachInstance994.length === 2) { tpProto.__attachInstance = function (a, b) { const u = this.__instances[a]; if (u && u.root && b) { fragQ.appendChild(u.root); // for cleanup return this.__attachInstance994(a, b); } } } } const ytTemplateDomEntry = (tpProto) => { console.log('ytTemplateDomEntry triggered') const convertToWeakArr = (arr) => { if (arr.isWeak) return; for (let i = 0, l = arr.length; i < l; i++) { const o = arr[i] if (o) { let p = kRef(o) if (!p) arr[i] = null; else { if (!o[wk]) o[wk] = mWeakRef(o); arr[i] = o[wk]; } } } arr.isWeak = true; } const convertToNormalArr = (arr) => { if (!arr.isWeak) return; for (let i = 0, l = arr.length; i < l; i++) { const o = arr[i] if (o) { let p = kRef(o) arr[i] = p; } } arr.isWeak = false; } if (MemoryFix_Flag002 & 256) { Object.defineProperty(tpProto, '__instances', { get() { this.dk322 || fixDetachFn(Reflect.getPrototypeOf(this)); let arr = this.__instances_actual471__; convertToNormalArr(arr); Promise.resolve(arr).then(convertToWeakArr); return arr; }, set(nv) { this.dk322 || fixDetachFn(Reflect.getPrototypeOf(this)); this.__instances_actual471__ = nv; Promise.resolve(nv).then(convertToWeakArr); return true; }, enumerable: false, configurable: true }); } // console.log(91901, tpProto.__detachInstance) if (MemoryFix_Flag002 & 32) { if (tpProto.__detachInstance) { fixDetachFn(tpProto); } else { Promise.resolve(tpProto).then((tpProto) => { // console.log(91902, tpProto.__detachInstance) fixDetachFn(tpProto); }) } } } if (MemoryFix_Flag002 & 32) { Object.defineProperty(Object.prototype, '__ensureTemplatized', { set(nv) { if (nv === true) return false; tpProto = this; if ('connectedCallback' in tpProto && tpProto !== Node.prototype && !tpProto.__domDX37__) { tpProto.__domDX37__ = true; ytTemplateDomEntry(tpProto); } this.__ensureTemplatized949__ = nv; return true; }, get() { return this.__ensureTemplatized949__; } }); } if (MemoryFix_Flag002 & 64) { HTMLElement_.prototype.__dataHostBinding374 = true; Object.defineProperty(HTMLElement_.prototype, '__dataHost', { get() { return kRef(this.__dataHostWr413__); }, set(nv) { if (nv && typeof nv === 'object' && !nv.deref) { if (!nv[wk]) nv[wk] = mWeakRef(nv); nv = nv[wk]; } this.__dataHostWr413__ = nv; return true; } }); } const setupYProto = function (yProto) { if(yProto === Object.prototype) return; if (!kMap.get(yProto)) kMap.set(yProto, `protoKey1_${Math.floor(Math.random() * 314159265359 + 314159265359).toString(36)}_${Date.now()}`); yProto[kMap.get(yProto)] = true; if (MemoryFix_Flag002 & 32) { if (!yProto.dk322 && (yProto.__attachInstance || yProto.__detachInstance)) { fixDetachFn(yProto); } } if (MemoryFix_Flag002 & 128) { if (!yProto.__dataHostBinding374) { yProto.__dataHostBinding374 = true; Object.defineProperty(yProto, '__dataHost', { set(nv) { let dh = nv; if (dh && typeof dh === 'object' && !dh.deref) { const wr = dh[wk] || (dh[wk] = mWeakRef(dh)); dh = wr; } this[symDH] = dh; return true; }, get() { let wr = this[symDH]; let obj = typeof wr === 'object' ? kRef(wr) : wr; return obj; }, enumerable: false, configurable: true }); } if (yProto._registerHost && yProto._enqueueClient && yProto.__enableOrFlushClients && !yProto._registerHostDX38__) { yProto._registerHostDX38__ = true; // yProto._registerHost7133 = yProto._registerHost; yProto.__enableOrFlushClients = function () { const c_ = this.__dataPendingClients; if (c_) { const c = c_.slice(); c_.length = 0; for (let d = 0, l = c.length; d < l; d++) { let e = kRef(c[d]); if (e) { e.__dataEnabled ? e.__dataPending && e._flushProperties() : e._enableProperties() } } } } yProto._enqueueClient = function (c) { if (c === this || !c || typeof c !== 'object') return; if (c.deref) c = kRef(c); const m = this.__dataPendingClients || (this.__dataPendingClients = []); if (!c[wk]) c[wk] = mWeakRef(c); m.push(c[wk]); } } } } const setupRendering = function () { const cnt = this; const cProto = Reflect.getPrototypeOf(cnt); let yProto = Reflect.getPrototypeOf(cProto); const yProtos = new Set(); if (!yMap.has(yProto)) { // capture all ancenstor constructors of a and b (non-specific component type) do { if (yProto === Object.prototype || yProto === null) break; if (yProto === Element.prototype || yProto === Node.prototype || yProto === EventTarget.prototype || yProto === HTMLElement_.prototype) break; yProtos.add(yProto); yProto = Reflect.getPrototypeOf(yProto); } while (!yProtos.has(yProto)); for (const yProto of yProtos) { yMap.add(yProto); setupYProto(yProto) } } if (!cMap.has(cProto)) { cMap.add(cProto); // cProto constrcutor is either a or b? (specific component type) setupCProto(cProto); } } const selfRef = {}; // no change. just key let wm3 = new WeakMap(); Object.defineProperty(Object.prototype, 'root', { get() { const p = this ? kRef(this) : null; const r = p ? wm3.get(p) : null; const r2 = r ? kRef(r) : null; if (r === selfRef) return p; return r2; }, set(nv) { const p = this ? kRef(this) : null; const mv = nv ? kRef(nv) : null; if (typeof mv !== 'object') return; if (mv && (mv instanceof Node) && !p.__setupRendered399__) { p.__setupRendered399__ = true; setupRendering.call(p); } if (mv && mv.is && !mv.__setupRendered399__) { mv.__setupRendered399__ = true; setupRendering.call(mv); } if (mv === p) { wm3.set(p, selfRef) return true; } let gv = nv; if (nv && !nv[wk]) { gv = nv[wk] = mWeakRef(nv); } if (p) { wm3.set(p, gv); } return true; }, enumerable: false, configurable: true }); } function getTranslate() { pLoad.then(() => { let nonce = document.querySelector('style[nonce]'); nonce = nonce ? nonce.getAttribute('nonce') : null; const st = document.createElement('style'); if (typeof nonce === 'string') st.setAttribute('nonce', nonce); st.textContent = ".yt-formatted-string-block-line{display:block;}"; let parent; if (parent = document.head) parent.appendChild(st); else if (parent = (document.body || document.documentElement)) parent.insertBefore(st, parent.firstChild); }); const snCache = new Map(); if (TRANSLATE_DEBUG) { console.log(11) } /** @type {(str: string?) => string} */ function _snippetText(str) { // str can be underfinded if (!str || typeof str !== 'string') return ''; let res = snCache.get(str); if (res === undefined) { let b = false; res = str.replace(/[\s\u3000\u200b]*[\u200b\xA0\x20\n]+[\s\u3000\u200b]*/g, (m) => { b = true; return m.includes('\n') ? '\n' : m.replace(/\u200b/g, '').replace(/[\xA0\x20]+/g, ' '); }); res = res.replace(/^[\s\u3000]+|[\u3000\s]+$/g, () => { b = true; return ''; }); if (b) { snCache.set(str, res); snCache.set(res, null); } else { res = null; snCache.set(str, null); } } return res === null ? str : res; } /** @type {(snippet: Object) => string} */ function snippetText(snippet) { let runs = snippet.runs; const n = runs.length; if (n === 1) return _snippetText(runs[0].text); let res = new Array(n); let ci = 0; for (const s of runs) { res[ci++] = _snippetText(s.text); } return res.join('\n'); } const _DEBUG_szz = (t) => t.map(x => { const tsr = x.transcriptSegmentRenderer; return ({ t: tsr.snippet.runs.map(x => x.text).join('//'), a: tsr.startMs, b: tsr.endMs }); }); const fixRuns = (runs) => { if (runs.length === 1 && runs[0]?.text?.includes('\n')) { // https://www.youtube.com/watch?v=dmHJJ5k_G-A const text = runs[0].text; const nlc = text.includes('\r\n') ? '\r\n' : text.includes('\n\r') ? '\n\r' : text.includes('\r') ? '\r' : '\n'; const s = text.split(nlc); let bi = 0; runs.length = s.length; for (const text of s) { runs[bi++] = { ...runs[0], text, ...{blockLine: true} }; } } for (const s of runs) { s.text = _snippetText(s.text); } } function translate(initialSegments) { // 2023.07.13 - fix initialSegments with transcriptSectionHeaderRenderer if (!initialSegments) return initialSegments; if (TRANSLATE_DEBUG) { console.log(12); Promise.resolve(JSON.stringify(initialSegments)).then((r) => { let obj = JSON.parse(r); console.log(7558, 1, obj) return obj; }).then(p => { let obj = _DEBUG_szz(p) console.log(7558, 2, obj) }) } //let mapRej = new WeakSet(); const n1 = initialSegments.length; if (!n1) return fRes; let n2 = 0; const fRes = new Array(n1); // ----------------------------------------------------------------------------------------- const s8 = Symbol(); { /** @type {Map} */ let cacheTexts = new Map(); // avoid duplicate with javascript object properties // /-* * @type {Map} *-/ // let mh1 = new Map(); // avoid duplicate with javascript object properties // 1: ok // 2: abandoned effect text for (const initialSegment of initialSegments) { const transcript = (initialSegment || 0).transcriptSegmentRenderer; if (!transcript) { // https://www.youtube.com/watch?v=dmHJJ5k_G-A - transcriptSectionHeaderRenderer fRes[n2++] = initialSegment; continue; } const runs = transcript.snippet.runs if (!runs || runs.length === 0) { initialSegment[s8] = true; continue; } let startMs = (+transcript.startMs || 0); //integer let endMs = (+transcript.endMs || 0); //integer if (startMs === endMs) { // effect text // https://www.youtube.com/watch?v=Ud73fm4Uoq0 //mapRej.add(initialSegment) continue; } if (endMs - startMs < 30) { continue; } const text = snippetText(transcript.snippet); const hEntry = cacheTexts.get(text); const mh1e = hEntry === undefined ? 0 : hEntry === null ? 2 : 1; if (mh1e === 2) continue; const entry = { startMs, endMs, initialSegment, text }; if (mh1e === 0) { if (/^[,.\x60\x27\x22\u200b\xA0\x20;-]*$/.test(text)) { initialSegment[s8] = true; cacheTexts.set(text, null); //effect only // https://www.youtube.com/watch?v=zLak0dxBKpM //mapRej.add(initialSegment) continue; } } else if (hEntry) { const timeDiff = entry.startMs - hEntry.endMs; let shouldMerge = false; if (timeDiff >= 0) { if (timeDiff < 25) { shouldMerge = true; } else if (timeDiff < 450 && entry.endMs - entry.startMs < 900) { shouldMerge = true; } else if (timeDiff < 150 && entry.endMs - entry.startMs > 800) { shouldMerge = true; } if (shouldMerge && hEntry.endMs <= endMs && startMs <= endMs) { // abandon the current entry. // absorbed by previous entry hEntry.endMs = entry.endMs; hEntry.initialSegment.transcriptSegmentRenderer.endMs = entry.initialSegment.transcriptSegmentRenderer.endMs; // update fRes & initialSegments as well using object reference //mapRej.add(entry.initialSegment); continue; } } else if (entry.startMs < hEntry.startMs && hEntry.startMs < entry.endMs) { // abandon the current entry. // absorbed by previous entry if (entry.endMs > hEntry.endMs) { hEntry.endMs = entry.endMs; hEntry.initialSegment.transcriptSegmentRenderer.endMs = entry.initialSegment.transcriptSegmentRenderer.endMs; // update fRes & initialSegments as well using object reference } //mapRej.add(entry.initialSegment); continue; } } //if not abandoned cacheTexts.set(text, entry); //replace the previous valid entry object if any // for (const s of runs) { // s.text = _snippetText(s.text); // } fixRuns(runs); fRes[n2++] = initialSegment; } // cacheTexts.clear(); // let GC do it. cacheTexts = null; // mh1.clear(); // let GC do it. // mh1 = null; } const si_length = fRes.length = n2; const sj_length = n1; if (si_length !== sj_length) { // for equal length, no fix is required & ignore spacing fix // collect the abandon text to become second subtitle let sj_start = 0; let invalid_sj = -1; for (let si = 0; si < si_length; si++) { const segment = fRes[si]; let transcript = segment.transcriptSegmentRenderer; if (!transcript) continue; // e.g. transcriptSectionHeaderRenderer const runs = transcript.snippet.runs; // fixRuns(runs); if (runs.length > 1 || runs[0].text.includes('\n')) continue; // skip multi lines const main_startMs = (+transcript.startMs || 0); const main_endMs = (+transcript.endMs || 0); transcript = null; /** @type {Map} */ let tMap = new Map(); // avoid duplicate with javascript object properties // assume that it is asc-ordered array of key startMs; for (let sj = sj_start; sj < sj_length; sj++) { const initialSegment = initialSegments[sj]; if (!initialSegment || initialSegment[s8]) continue; // should invalid_sj be set ? const tSegment = initialSegment.transcriptSegmentRenderer; if (!tSegment) { // https://www.youtube.com/watch?v=dmHJJ5k_G-A - transcriptSectionHeaderRenderer invalid_sj = sj; // should invalid_sj be set ? continue; } const startMs = (+tSegment.startMs || 0) const isStartValid = startMs >= main_startMs; if (!isStartValid) { invalid_sj = sj; continue; } // isStartValid must be true if (startMs > main_endMs) { sj_start = invalid_sj + 1; break; } const endMs = (+tSegment.endMs || 0) if (endMs <= main_endMs) { const mt = snippetText(tSegment.snippet); const prev = tMap.get(mt); if (endMs >= startMs) { tMap.set(mt, (prev || 0) + 1 + (endMs - startMs)); } } } if (tMap.size <= 1) continue; // no second line let rg = [...tMap.entries()]; // N x 2 2D-array [string,number][] tMap = null; // https://www.youtube.com/watch?v=Ud73fm4Uoq0 rg.sort((a, b) => b[1] - a[1]); //descending order of number let targetZ = rg[1][1]; if (targetZ > 4) { let az = 0; let fail = false; for (let idx = 2, rgl = rg.length; idx < rgl; idx++) { az += rg[idx][1]; if (az >= targetZ) { fail = true; break; } } if (!fail) { const rgA = rg[0][0]; const rgB = rg[1][0]; const isDiff = rgB.replace(/\s/g, '') !== rgA.replace(/\s/g, ''); if (isDiff && rgA === _snippetText(runs[0].text)) { if (runs[0] && runs[0].text) runs[0].blockLine = true; runs.push({ text: rgB, blockLine: true }); } } } rg = null; } TRANSLATE_DEBUG && Promise.resolve(fRes).then((r) => { let obj = r; console.log(7559, 1, obj) return obj; }).then(p => { let obj = _DEBUG_szz(p) console.log(7559, 2, obj) }); } // ----------------------------------------------------------------------------------------- snCache.clear(); return fRes; } return translate } let translateFn = null; FIX_TRANSCRIPT_SEGMENTS && !isChatRoomURL && (() => { const wmx = new WeakMap(); function fixSegments(ytObj) { let a, b; let seg = ((a = ytObj.data) == null ? void 0 : a[b = 'searchResultSegments']) || ((a = ytObj.data) == null ? void 0 : a[b = 'initialSegments']) || []; if (!seg || !a || !b || typeof (seg || 0) !== 'object' || !Number.isFinite(seg.length * 1)) return; translateFn = translateFn || getTranslate(); let cSeg; cSeg = wmx.get(seg); if (!cSeg) { let vSeg = null; try { vSeg = translateFn(seg); } catch (e) { } if (seg && typeof seg === 'object' && seg.length >= 1 && vSeg && typeof vSeg === 'object' && vSeg.length >= 1) { // console.log('translated', vSeg); cSeg = vSeg; wmx.set(seg, cSeg); wmx.set(cSeg, cSeg); } } if (cSeg && cSeg !== seg) { a[b] = cSeg; } } const dfn = Symbol(); const Object_ = Object; Object_[dfn] = Object_.defineProperties; let activation = true; Object_.defineProperties = function (obj, pds) { let segments, get_; if (activation && pds && (segments = pds.segments) && (get_ = segments.get)) { activation = false; segments.get = function () { fixSegments(this); return get_.call(this); }; } return Object_[dfn](obj, pds); }; })(); let pf31 = new PromiseExternal(); // native RAF let __requestAnimationFrame__ = typeof webkitRequestAnimationFrame === 'function' ? window.webkitRequestAnimationFrame.bind(window) : window.requestAnimationFrame.bind(window); // 1st wrapped RAF const baseRAF = (callback) => { return p59 ? __requestAnimationFrame__(callback) : __requestAnimationFrame__((hRes) => { pf31.then(() => { callback(hRes); }); }); }; // 2nd wrapped RAF window.requestAnimationFrame = baseRAF; const insp = o => o ? (o.polymerController || o.inst || o || 0) : (o || 0); const indr = o => insp(o).$ || o.$ || 0; const prototypeInherit = (d, b) => { const m = Object.getOwnPropertyDescriptors(b); for (const p in m) { if (!Object.getOwnPropertyDescriptor(d, p)) { Object.defineProperty(d, p, m[p]); } } }; const firstObjectKey = (obj) => { for (const key in obj) { if (obj.hasOwnProperty(key) && typeof obj[key] === 'object') return key; } return null; }; function searchNestedObject(obj, predicate, maxDepth = 64) { // normal case: depth until 36 const result = []; const visited = new WeakSet(); function search(obj, depth) { visited.add(obj); for (const [key, value] of Object.entries(obj)) { // Recursively search nested objects and arrays if (value !== null && typeof value === 'object') { // Prevent infinite loops by checking if the object is already visited or depth exceeded if (depth + 1 <= maxDepth && !visited.has(value)) { search(value, depth + 1); } } else if (predicate(value)) { result.push([obj, key]); } } } typeof (obj || 0) === 'object' && search(obj, 0); return result; } /** @type {(o: Object | null) => WeakRef | null} */ const mWeakRef = typeof WeakRef === 'function' ? (o => o ? new WeakRef(o) : null) : (o => o || null); /** @type {(wr: Object | null) => Object | null} */ const kRef = (wr => (wr && wr.deref) ? wr.deref() : wr); const isIterable = (obj) => (Symbol.iterator in Object_(obj)); if (typeof Document.prototype.requestStorageAccessFor === 'function') { if (DENY_requestStorageAccess) { // https://developer.mozilla.org/en-US/docs/Web/API/Document/requestStorageAccessFor Document.prototype.requestStorageAccessFor = undefined; console.log('[yt-js-engine-tamer]', 'requestStorageAccessFor is removed.'); } else if (DISABLE_IFRAME_requestStorageAccess && window !== top) { Document.prototype.requestStorageAccessFor = function () { return new Promise((resolve, reject) => { reject(); }); }; } } const traceStack = (stack) => { let result = new Set(); let p = new Set(); let u = '' for (const s of stack.split('\n')) { if (s.split(':').length < 3) continue; let m = /(([\w-_\.]+):\d+:\d+)[^:\r\n]*/.exec(s); if (!m) continue; p.add(m[2]); if (p.size >= 3) break; if(!u) u = m[2]; else if(p.size === 2 && u && u=== m[2]) break; result.add(s); } return [...result].join('\n'); } if (FIX_bind_self_this && !Function.prototype.bind488 && !Function.prototype.bind588) { // window.m3bb = new Set(); // const smb = Symbol(); const vmb = 'dtz02' // Symbol(); // return kThis for thisArg const vmc = 'dtz04' // Symbol(); // whether it is proxied fn const vmd = 'dtz08' // Symbol(); // self fn proxy (fn--fn) const thisConversionFn = (thisArg) => { if (!thisArg) return null; const kThis = thisArg[vmb]; if (kThis) { const ref = kThis.ref; return (ref ? kRef(ref) : null) || null; } return thisArg; } const pFnHandler2 = { get(target, prop) { if (prop === vmc) return target; return Reflect.get(target, prop); }, apply(target, thisArg, argumentsList) { thisArg = thisConversionFn(thisArg); if (thisArg) return Reflect.apply(target, thisArg, argumentsList); } } const proxySelfHandler = { get(target, prop) { if(prop === vmb) return target; const ref = target.ref; const cnt = kRef(ref); if (!cnt) return; if (typeof cnt[prop] === 'function' && !cnt[prop][vmc] && !cnt[prop][vmb]) { if (!cnt[prop][vmd]) cnt[prop][vmd] = new Proxy(cnt[prop], pFnHandler2); return cnt[prop][vmd]; } return cnt[prop]; }, set(target, prop, value) { const cnt = kRef(target.ref); if (!cnt) return true; if(value && (value[vmc] || value[vmb])){ cnt[prop] = value[vmc] || thisConversionFn(value); return true; } cnt[prop] = value; return true; } }; const weakWrap = (thisArg) => { thisArg = thisConversionFn(thisArg); if (!thisArg) { console.error('thisArg is not found'); return null; } return new Proxy({ ref: mWeakRef(thisArg) }, proxySelfHandler); } if (!window.getComputedStyle533 && typeof window.getComputedStyle === 'function') { window.getComputedStyle533 = window.getComputedStyle; window.getComputedStyle = function (a, ...args) { a = thisConversionFn(a); if (a) { return getComputedStyle533(a, ...args); } return null; } } Function._count_bind_00 = 0; // Function._count_bind_01 = 0; // let matchNativeCode = (Object+""); // let matchNativeCode1 = matchNativeCode.includes("[native code]"); // let matchNativeLen = matchNativeCode.length - Object.name.length; // const matchConstructor = (thisArg) => { // const f = `${(thisArg || 0).constructor}`; // if (f.length > 45) return true; // if (matchNativeCode1 && f.length - thisArg.constructor.name.length === matchNativeLen) { // if (f.includes('[native code]')){ // return false; // } // return true; // } // return false; // } // const acceptThis = (thisArg)=>{ // // if(!thisArg || typeof thisArg !=='object') return false; // // // if((((thisArg||0).constructor||0).name || 'XXXXXXXX').length < 3) return true; // // if(typeof thisArg.path === 'string') return true; // // if(typeof thisArg.fn === 'function') return true; // // if(typeof thisArg.id === 'string') return true; // // if(typeof thisArg.isLoaded === 'boolean') return true; // return false; // } const patchFn = (fn) => { let s = `${fn}`; if (s.length < 11 || s.includes('\n')) return false; if(s.includes('bind(this')) return true; if(s.includes('=this') && /[,\s][a-zA-Z_][a-zA-Z0-9_]*=this[;,]/.test(s) ) return true; // var a=this; // f.bind(this) return false; } Function.prototype.bind488 = Function.prototype.bind; Function.prototype.bind = function(thisArg, ...args){ if (thisConversionFn(thisArg) !== thisArg) { return this.bind488(thisArg, ...args); } if( thisArg && patchFn(this) ){ // console.log(599,`${this}`) try { // let b1 = thisArg && typeof thisArg === 'object' && typeof thisArg.isAttached === 'boolean' && !thisArg.dtz06; // ready cnt // let b2 = !b1 && thisArg && (thisArg instanceof Node) && typeof thisArg.nodeName === 'string' && !thisArg.dtz06; // dom // let b3 = !b1 && !b2 && thisArg && typeof thisArg === 'object' && typeof thisArg.is === 'string' && !thisArg.dtz06; // init stage ? // // let b4 = !b1 && !b2 && !b3 && thisArg && typeof thisArg === 'object' && !thisArg.dtz06 && matchConstructor(thisArg); // // let b5 = !b1 && !b2 && !b3 && !b4 && thisArg && typeof thisArg === 'object' && !thisArg.dtz06 && acceptThis(thisArg); // // let b5 = !b1 && !b2 && !b3 && thisArg && typeof thisArg === 'object' && !thisArg.dtz06 && !(thisArg instanceof Window); // // let b4 = false; // let b4 = !b1 && !b2 && !b3 && thisArg && !thisArg.dtz06; // // b3 = false; // // b4 = false; // // b5 = false; // if (b1 || b2 || b3 ||b4 ) { const f = this; const ps = thisArg.__proxySelf0__ || (thisArg.__proxySelf0__ = weakWrap(thisArg)); if (ps && ps[vmb]) { Function._count_bind_00++; return f.bind488(ps, ...args) } // } } catch (e) { console.warn(e) } } return this.bind488(thisArg, ...args); } Function.prototype.bind588 = 1; } if (FIX_weakMap_weakRef && !window.WeakMapOriginal && typeof window.WeakMap === 'function' && typeof WeakRef === 'function') { const WeakMapOriginal = window.WeakMapOriginal = window.WeakMap; const wm6 = new WeakMapOriginal(); const skipW = new WeakSet(); window.WeakMap = class WeakMap extends WeakMapOriginal { constructor(iterable = undefined) { super(); if (iterable && iterable[Symbol.iterator]) { for (const entry of iterable) { entry && this.set(entry[0], entry[1]); } } } delete(a) { if (!this.has(a)) return false; super.delete(a); return true; } get(a) { const p = super.get(a); if (p && typeof p === 'object' && p.deref && !skipW.has(p)) { let v = kRef(p); if (!v) { super.delete(a); } return v || undefined; } return p; } has(a) { if (!super.has(a)) return false; const p = super.get(a); if (p && typeof p === 'object' && p.deref && !skipW.has(p)) { if (!kRef(p)) { super.delete(a); return false; } } return true; } set(a, b) { let wq = b; if (b && (typeof b === 'function' || typeof b === 'object')) { if (b.deref) { skipW.add(b); wq = b; } else { wq = wm6.get(b); if (!wq) { wq = mWeakRef(b); wm6.set(b, wq); } } } super.set(a, wq); return this; } } Object.defineProperty(window.WeakMap, Symbol.toStringTag, { configurable: true, enumerable: false, value: "WeakMap", writable: false }); } const isWatchPageURL = (url) => { url = url || location; return location.pathname === '/watch' || location.pathname.startsWith('/live/') }; const isCustomElementsProvided = typeof customElements !== "undefined" && typeof (customElements || 0).whenDefined === "function"; const promiseForCustomYtElementsReady = isCustomElementsProvided ? Promise.resolve(0) : new Promise((callback) => { const EVENT_KEY_ON_REGISTRY_READY = "ytI-ce-registry-created"; if (typeof customElements === 'undefined') { if (!('__CE_registry' in document)) { // https://github.com/webcomponents/polyfills/ Object.defineProperty(document, '__CE_registry', { get() { // return undefined }, set(nv) { if (typeof nv == 'object') { delete this.__CE_registry; this.__CE_registry = nv; this.dispatchEvent(new CustomEvent(EVENT_KEY_ON_REGISTRY_READY)); } return true; }, enumerable: false, configurable: true }) } let eventHandler = (evt) => { document.removeEventListener(EVENT_KEY_ON_REGISTRY_READY, eventHandler, false); const f = callback; callback = null; eventHandler = null; f(); }; document.addEventListener(EVENT_KEY_ON_REGISTRY_READY, eventHandler, false); } else { callback(); } }); const whenCEDefined = isCustomElementsProvided ? (nodeName) => customElements.whenDefined(nodeName) : (nodeName) => promiseForCustomYtElementsReady.then(() => customElements.whenDefined(nodeName)); FIX_perfNow && performance.timeOrigin > 9 && (() => { if (performance.now23 || performance.now16 || typeof Performance.prototype.now !== 'function') return; const f = performance.now23 = Performance.prototype.now; let k = 0; // 0 <= k < 9998m let u = 0; let s = ((performance.timeOrigin % 7) + 1) / 7 - 1e-2 / 7; // s > 0.14 // By definition, performance.now() is mono increasing. // Fixing in YouTube.com is required to ensure performance.now() is strictly increasing. performance.now = performance.now16 = function () { /** * Bug 1842437 - When attempting to go back on youtube.com, the content remains the same * * If consecutive session history entries had history.state.entryTime set to same value, * back button doesn't work as expected. The entryTime value is coming from performance.now() * and modifying its return value slightly to make sure two close consecutive calls don't * get the same result helped with resolving the issue. */ // see https://bugzilla.mozilla.org/show_bug.cgi?id=1756970 // see https://bugzilla.mozilla.org/show_bug.cgi?id=1842437 const v = typeof (this || 0).now23 === 'function' ? this.now23() + s : f.call(performance) + s; // v > 0.14 if (u + 0.0015 < (u = v)) k = 0; // note: hRes should be accurate to 5 µs in isolated contexts else if (k < 0.001428) k += 1e-6 / 7; // M = 10000 * m; m * 9996 = 0.001428 else { // more than 9998 consecutive calls /** * * max no. of consecutive calls * * Sample Size: 4800 * Sample Avg = 1565.375 * Sample Median = 1469.5 * Sample Max = 5660 << 7500 << 9999 * * * */ k = 0; s += 1 / 7; } return v + k; // 0 < v - M < v - M + k < v } let loggerMsg = ''; if (`${performance.now()}` === `${performance.now()}`) { const msg1 = 'performance.now is modified but performance.now() is not strictly increasing.'; const msg2 = 'performance.now cannot be modified and performance.now() is not strictly increasing.'; loggerMsg = performance.now !== performance.now16 ? msg1 : msg2; // might not able to set in Firefox } loggerMsg && console.warn(loggerMsg); })(); FIX_removeChild && (() => { if (typeof Node.prototype.removeChild === 'function' && typeof Node.prototype.removeChild062 !== 'function') { const fragD = document.createDocumentFragment(); Node.prototype.removeChild062 = Node.prototype.removeChild; Node.prototype.removeChild = function (child) { if (typeof this.__shady_native_removeChild !== 'function' || ((child instanceof Node) && child.parentNode === this)) { let ok = false; try { this.removeChild062(child); ok = true; } catch (e) { } if (!ok) { try { fragD.appendChild(child) } catch (e) { console.warn(e); } try { child.remove(); } catch (e) { console.warn(e); } } } else if ((child instanceof Element) && child.is === 'tp-yt-paper-tooltip') { // tooltip bug } else { console.warn('[yt-js-engine-tamer] Node is not removed from parent', { parent: this, child: child }) } return child; } } })(); FIX_VIDEO_PLAYER_MOUSEHOVER_EVENTS && !isChatRoomURL && (() => { const [setIntervalX0, clearIntervalX0] = [setInterval, clearInterval]; // let cid = 0; let mousemoveFn = null; let mousemoveDT = 0; let mousemoveCount = 0; // let qv = false; const cif = () => { if (!mousemoveFn) return; const ct = Date.now(); if (mousemoveDT + 1200 > ct) { // avoid setTimeout delay too long without execution mousemoveFn && mousemoveFn(); } mousemoveFn = null; }; let mousemoveCId = 0; let mouseoverFn = null; HTMLElement_.prototype.addEventListener4882 = HTMLElement_.prototype.addEventListener; HTMLElement_.prototype.addEventListener = function (a, b, c) { if (this.id == 'movie_player' && `${a}`.startsWith('mouse') && c === undefined) { const bt = `${b}`; if (bt.length >= 61 && bt.length <= 71 && bt.startsWith('function(){try{return ') && bt.includes('.apply(this,arguments)}catch(')) { b[`__$$${a}$$1926__`] = true; this[`__$$${a}$$1937__`] = (this[`__$$${a}$$1937__`] || 0) + 1; if (this[`__$$${a}$$1937__`] > 1073741823) this[`__$$${a}$$1937__`] -= 536870911; // console.log(3928, a, this[`__$$${a}$$1937__`]) if (!this[`__$$${a}$$1938__`]) { this[`__$$${a}$$1938__`] = b; if (a === 'mousemove') { this.addEventListener4882('mouseenter', (evt) => { if (mousemoveCId) return; mousemoveCId = setIntervalX0(cif, 380); }); this.addEventListener4882('mouseleave', (evt) => { clearIntervalX0(mousemoveCId); mousemoveCId = 0; }); } this.addEventListener4882(a, (evt) => { const evt_ = evt; if (!this[`__$$${a}$$1937__`]) return; if (!this[`__$$${a}$$1938__`]) return; if (a === 'mousemove') { mouseoverFn && mouseoverFn(); if (mousemoveDT + 350 > (mousemoveDT = Date.now())) { (++mousemoveCount > 1e9) && (mousemoveCount = 9); } else { mousemoveCount = 0; } const f = mousemoveFn = () => { if (f !== mousemoveFn) return; mousemoveFn = null; this[`__$$${a}$$1938__`](evt_); }; if (mousemoveCount <= 1) mousemoveFn(); } else { if (a === 'mouseout' || a === 'mouseleave') { mousemoveFn = null; mousemoveDT = 0; mousemoveCount = 0; this[`__$$${a}$$1938__`](evt_); mouseoverFn && mouseoverFn(); } else { // mouseover, mouseenter mousemoveFn = null; mousemoveDT = 0; mousemoveCount = 0; mouseoverFn && mouseoverFn(); // just in case const f = mouseoverFn = () => { if (f !== mouseoverFn) return; mouseoverFn = null; this[`__$$${a}$$1938__`](evt_); } nextBrowserTick_(mouseoverFn); } } }, c); return; } else { return; } } } return this.addEventListener4882(a, b, c) } HTMLElement_.prototype.removeEventListener4882 = HTMLElement_.prototype.removeEventListener; HTMLElement_.prototype.removeEventListener = function (a, b, c) { if (this.id == 'movie_player' && `${a}`.startsWith('mouse') && c === undefined) { if (b[`__$$${a}$$1926__`]) { b[`__$$${a}$$1926__`] = false; if (this[`__$$${a}$$1937__`]) this[`__$$${a}$$1937__`] -= 1; // console.log(3929, a, this[`__$$${a}$$1937__`], b[`__$$${a}$$1926__`]) return; } } return this.removeEventListener4882(a, b, c) } })(); FIX_DOM_IF_REPEAT && (() => { // https://www.youtube.com/s/desktop/26a583e4/jsbin/live_chat_polymer.vflset/live_chat_polymer.js // DOM-IF is still a core class of Polymer, so no polymerController is available. // Be careful of the mixture of polymer functions and native Element functions // Be careful of the coding design is different with the modern Yt elements /* function Ks(a, b, c) { if (kj && !BOa(a)) throw Error("strictTemplatePolicy: template owner not trusted"); c = c || {}; if (a.__templatizeOwner) throw Error("A