YoutubeAdblock: fix lag/high memory usage after clicking videos (#4317)
This commit is contained in:
parent
70d41b0584
commit
84adcc7002
1 changed files with 195 additions and 191 deletions
|
|
@ -18,199 +18,203 @@
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with AdGuard's Block YouTube Ads. If not, see <http://www.gnu.org/licenses/>.
|
* along with AdGuard's Block YouTube Ads. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
(() => {
|
||||||
|
if (window.adguardInjected) return;
|
||||||
|
window.adguardInjected = true;
|
||||||
|
|
||||||
const hiddenCSS = [
|
const hiddenCSS = [
|
||||||
"#__ffYoutube1",
|
"#__ffYoutube1",
|
||||||
"#__ffYoutube2",
|
"#__ffYoutube2",
|
||||||
"#__ffYoutube3",
|
"#__ffYoutube3",
|
||||||
"#__ffYoutube4",
|
"#__ffYoutube4",
|
||||||
"#feed-pyv-container",
|
"#feed-pyv-container",
|
||||||
"#feedmodule-PRO",
|
"#feedmodule-PRO",
|
||||||
"#homepage-chrome-side-promo",
|
"#homepage-chrome-side-promo",
|
||||||
"#merch-shelf",
|
"#merch-shelf",
|
||||||
"#offer-module",
|
"#offer-module",
|
||||||
'#pla-shelf > ytd-pla-shelf-renderer[class="style-scope ytd-watch"]',
|
'#pla-shelf > ytd-pla-shelf-renderer[class="style-scope ytd-watch"]',
|
||||||
"#pla-shelf",
|
"#pla-shelf",
|
||||||
"#premium-yva",
|
"#premium-yva",
|
||||||
"#promo-info",
|
"#promo-info",
|
||||||
"#promo-list",
|
"#promo-list",
|
||||||
"#promotion-shelf",
|
"#promotion-shelf",
|
||||||
"#related > ytd-watch-next-secondary-results-renderer > #items > ytd-compact-promoted-video-renderer.ytd-watch-next-secondary-results-renderer",
|
"#related > ytd-watch-next-secondary-results-renderer > #items > ytd-compact-promoted-video-renderer.ytd-watch-next-secondary-results-renderer",
|
||||||
"#search-pva",
|
"#search-pva",
|
||||||
"#shelf-pyv-container",
|
"#shelf-pyv-container",
|
||||||
"#video-masthead",
|
"#video-masthead",
|
||||||
"#watch-branded-actions",
|
"#watch-branded-actions",
|
||||||
"#watch-buy-urls",
|
"#watch-buy-urls",
|
||||||
"#watch-channel-brand-div",
|
"#watch-channel-brand-div",
|
||||||
"#watch7-branded-banner",
|
"#watch7-branded-banner",
|
||||||
"#YtKevlarVisibilityIdentifier",
|
"#YtKevlarVisibilityIdentifier",
|
||||||
"#YtSparklesVisibilityIdentifier",
|
"#YtSparklesVisibilityIdentifier",
|
||||||
".carousel-offer-url-container",
|
".carousel-offer-url-container",
|
||||||
".companion-ad-container",
|
".companion-ad-container",
|
||||||
".GoogleActiveViewElement",
|
".GoogleActiveViewElement",
|
||||||
'.list-view[style="margin: 7px 0pt;"]',
|
'.list-view[style="margin: 7px 0pt;"]',
|
||||||
".promoted-sparkles-text-search-root-container",
|
".promoted-sparkles-text-search-root-container",
|
||||||
".promoted-videos",
|
".promoted-videos",
|
||||||
".searchView.list-view",
|
".searchView.list-view",
|
||||||
".sparkles-light-cta",
|
".sparkles-light-cta",
|
||||||
".watch-extra-info-column",
|
".watch-extra-info-column",
|
||||||
".watch-extra-info-right",
|
".watch-extra-info-right",
|
||||||
".ytd-carousel-ad-renderer",
|
".ytd-carousel-ad-renderer",
|
||||||
".ytd-compact-promoted-video-renderer",
|
".ytd-compact-promoted-video-renderer",
|
||||||
".ytd-companion-slot-renderer",
|
".ytd-companion-slot-renderer",
|
||||||
".ytd-merch-shelf-renderer",
|
".ytd-merch-shelf-renderer",
|
||||||
".ytd-player-legacy-desktop-watch-ads-renderer",
|
".ytd-player-legacy-desktop-watch-ads-renderer",
|
||||||
".ytd-promoted-sparkles-text-search-renderer",
|
".ytd-promoted-sparkles-text-search-renderer",
|
||||||
".ytd-promoted-video-renderer",
|
".ytd-promoted-video-renderer",
|
||||||
".ytd-search-pyv-renderer",
|
".ytd-search-pyv-renderer",
|
||||||
".ytd-video-masthead-ad-v3-renderer",
|
".ytd-video-masthead-ad-v3-renderer",
|
||||||
".ytp-ad-action-interstitial-background-container",
|
".ytp-ad-action-interstitial-background-container",
|
||||||
".ytp-ad-action-interstitial-slot",
|
".ytp-ad-action-interstitial-slot",
|
||||||
".ytp-ad-image-overlay",
|
".ytp-ad-image-overlay",
|
||||||
".ytp-ad-overlay-container",
|
".ytp-ad-overlay-container",
|
||||||
".ytp-ad-progress",
|
".ytp-ad-progress",
|
||||||
".ytp-ad-progress-list",
|
".ytp-ad-progress-list",
|
||||||
'[class*="ytd-display-ad-"]',
|
'[class*="ytd-display-ad-"]',
|
||||||
'[layout*="display-ad-"]',
|
'[layout*="display-ad-"]',
|
||||||
'a[href^="http://www.youtube.com/cthru?"]',
|
'a[href^="http://www.youtube.com/cthru?"]',
|
||||||
'a[href^="https://www.youtube.com/cthru?"]',
|
'a[href^="https://www.youtube.com/cthru?"]',
|
||||||
"ytd-action-companion-ad-renderer",
|
"ytd-action-companion-ad-renderer",
|
||||||
"ytd-banner-promo-renderer",
|
"ytd-banner-promo-renderer",
|
||||||
"ytd-compact-promoted-video-renderer",
|
"ytd-compact-promoted-video-renderer",
|
||||||
"ytd-companion-slot-renderer",
|
"ytd-companion-slot-renderer",
|
||||||
"ytd-display-ad-renderer",
|
"ytd-display-ad-renderer",
|
||||||
"ytd-promoted-sparkles-text-search-renderer",
|
"ytd-promoted-sparkles-text-search-renderer",
|
||||||
"ytd-promoted-sparkles-web-renderer",
|
"ytd-promoted-sparkles-web-renderer",
|
||||||
"ytd-search-pyv-renderer",
|
"ytd-search-pyv-renderer",
|
||||||
"ytd-single-option-survey-renderer",
|
"ytd-single-option-survey-renderer",
|
||||||
"ytd-video-masthead-ad-advertiser-info-renderer",
|
"ytd-video-masthead-ad-advertiser-info-renderer",
|
||||||
"ytd-video-masthead-ad-v3-renderer",
|
"ytd-video-masthead-ad-v3-renderer",
|
||||||
"YTM-PROMOTED-VIDEO-RENDERER",
|
"YTM-PROMOTED-VIDEO-RENDERER",
|
||||||
];
|
];
|
||||||
/**
|
/**
|
||||||
* Adds CSS to the page
|
* Adds CSS to the page
|
||||||
*/
|
*/
|
||||||
const hideElements = () => {
|
const hideElements = () => {
|
||||||
const selectors = hiddenCSS;
|
const selectors = hiddenCSS;
|
||||||
if (!selectors) {
|
if (!selectors) {
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
const rule = selectors.join(", ") + " { display: none!important; }";
|
|
||||||
const style = document.createElement("style");
|
|
||||||
style.textContent = rule;
|
|
||||||
document.head.appendChild(style);
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* Calls the "callback" function on every DOM change, but not for the tracked events
|
|
||||||
* @param {Function} callback callback function
|
|
||||||
*/
|
|
||||||
const observeDomChanges = callback => {
|
|
||||||
const domMutationObserver = new MutationObserver(mutations => {
|
|
||||||
callback(mutations);
|
|
||||||
});
|
|
||||||
domMutationObserver.observe(document.documentElement, {
|
|
||||||
childList: true,
|
|
||||||
subtree: true,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* This function is supposed to be called on every DOM change
|
|
||||||
*/
|
|
||||||
const hideDynamicAds = () => {
|
|
||||||
const elements = document.querySelectorAll("#contents > ytd-rich-item-renderer ytd-display-ad-renderer");
|
|
||||||
if (elements.length === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
elements.forEach(el => {
|
|
||||||
if (el.parentNode && el.parentNode.parentNode) {
|
|
||||||
const parent = el.parentNode.parentNode;
|
|
||||||
if (parent.localName === "ytd-rich-item-renderer") {
|
|
||||||
parent.style.display = "none";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
const rule = selectors.join(", ") + " { display: none!important; }";
|
||||||
};
|
const style = document.createElement("style");
|
||||||
/**
|
style.textContent = rule;
|
||||||
* This function checks if the video ads are currently running
|
document.head.appendChild(style);
|
||||||
* and auto-clicks the skip button.
|
|
||||||
*/
|
|
||||||
const autoSkipAds = () => {
|
|
||||||
// If there's a video that plays the ad at this moment, scroll this ad
|
|
||||||
if (document.querySelector(".ad-showing")) {
|
|
||||||
const video = document.querySelector("video");
|
|
||||||
if (video && video.duration) {
|
|
||||||
video.currentTime = video.duration;
|
|
||||||
// Skip button should appear after that,
|
|
||||||
// now simply click it automatically
|
|
||||||
setTimeout(() => {
|
|
||||||
const skipBtn = document.querySelector("button.ytp-ad-skip-button");
|
|
||||||
if (skipBtn) {
|
|
||||||
skipBtn.click();
|
|
||||||
}
|
|
||||||
}, 100);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* This function overrides a property on the specified object.
|
|
||||||
*
|
|
||||||
* @param {object} obj object to look for properties in
|
|
||||||
* @param {string} propertyName property to override
|
|
||||||
* @param {*} overrideValue value to set
|
|
||||||
*/
|
|
||||||
const overrideObject = (obj, propertyName, overrideValue) => {
|
|
||||||
if (!obj) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
let overriden = false;
|
|
||||||
for (const key in obj) {
|
|
||||||
if (obj.hasOwnProperty(key) && key === propertyName) {
|
|
||||||
obj[key] = overrideValue;
|
|
||||||
overriden = true;
|
|
||||||
} else if (obj.hasOwnProperty(key) && typeof obj[key] === "object") {
|
|
||||||
if (overrideObject(obj[key], propertyName, overrideValue)) {
|
|
||||||
overriden = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return overriden;
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* Overrides JSON.parse and Response.json functions.
|
|
||||||
* Examines these functions arguments, looks for properties with the specified name there
|
|
||||||
* and if it exists, changes it's value to what was specified.
|
|
||||||
*
|
|
||||||
* @param {string} propertyName name of the property
|
|
||||||
* @param {*} overrideValue new value for the property
|
|
||||||
*/
|
|
||||||
const jsonOverride = (propertyName, overrideValue) => {
|
|
||||||
const nativeJSONParse = JSON.parse;
|
|
||||||
JSON.parse = (...args) => {
|
|
||||||
const obj = nativeJSONParse.apply(this, args);
|
|
||||||
// Override it's props and return back to the caller
|
|
||||||
overrideObject(obj, propertyName, overrideValue);
|
|
||||||
return obj;
|
|
||||||
};
|
};
|
||||||
// Override Response.prototype.json
|
/**
|
||||||
Response.prototype.json = new Proxy(Response.prototype.json, {
|
* Calls the "callback" function on every DOM change, but not for the tracked events
|
||||||
async apply(...args) {
|
* @param {Function} callback callback function
|
||||||
// Call the target function, get the original Promise
|
*/
|
||||||
const result = await Reflect.apply(...args);
|
const observeDomChanges = callback => {
|
||||||
// Create a new one and override the JSON inside
|
const domMutationObserver = new MutationObserver(mutations => {
|
||||||
overrideObject(result, propertyName, overrideValue);
|
callback(mutations);
|
||||||
return result;
|
});
|
||||||
},
|
domMutationObserver.observe(document.documentElement, {
|
||||||
});
|
childList: true,
|
||||||
};
|
subtree: true,
|
||||||
// Removes ads metadata from YouTube XHR requests
|
});
|
||||||
jsonOverride("adPlacements", []);
|
};
|
||||||
jsonOverride("playerAds", []);
|
/**
|
||||||
// Applies CSS that hides YouTube ad elements
|
* This function is supposed to be called on every DOM change
|
||||||
hideElements();
|
*/
|
||||||
// Some changes should be re-evaluated on every page change
|
const hideDynamicAds = () => {
|
||||||
hideDynamicAds();
|
const elements = document.querySelectorAll("#contents > ytd-rich-item-renderer ytd-display-ad-renderer");
|
||||||
autoSkipAds();
|
if (elements.length === 0) {
|
||||||
observeDomChanges(() => {
|
return;
|
||||||
|
}
|
||||||
|
elements.forEach(el => {
|
||||||
|
if (el.parentNode && el.parentNode.parentNode) {
|
||||||
|
const parent = el.parentNode.parentNode;
|
||||||
|
if (parent.localName === "ytd-rich-item-renderer") {
|
||||||
|
parent.style.display = "none";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* This function checks if the video ads are currently running
|
||||||
|
* and auto-clicks the skip button.
|
||||||
|
*/
|
||||||
|
const autoSkipAds = () => {
|
||||||
|
// If there's a video that plays the ad at this moment, scroll this ad
|
||||||
|
if (document.querySelector(".ad-showing")) {
|
||||||
|
const video = document.querySelector("video");
|
||||||
|
if (video && video.duration) {
|
||||||
|
video.currentTime = video.duration;
|
||||||
|
// Skip button should appear after that,
|
||||||
|
// now simply click it automatically
|
||||||
|
setTimeout(() => {
|
||||||
|
const skipBtn = document.querySelector("button.ytp-ad-skip-button");
|
||||||
|
if (skipBtn) {
|
||||||
|
skipBtn.click();
|
||||||
|
}
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* This function overrides a property on the specified object.
|
||||||
|
*
|
||||||
|
* @param {object} obj object to look for properties in
|
||||||
|
* @param {string} propertyName property to override
|
||||||
|
* @param {*} overrideValue value to set
|
||||||
|
*/
|
||||||
|
const overrideObject = (obj, propertyName, overrideValue) => {
|
||||||
|
if (!obj) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let overriden = false;
|
||||||
|
for (const key in obj) {
|
||||||
|
if (obj.hasOwnProperty(key) && key === propertyName) {
|
||||||
|
obj[key] = overrideValue;
|
||||||
|
overriden = true;
|
||||||
|
} else if (obj.hasOwnProperty(key) && typeof obj[key] === "object") {
|
||||||
|
if (overrideObject(obj[key], propertyName, overrideValue)) {
|
||||||
|
overriden = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return overriden;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Overrides JSON.parse and Response.json functions.
|
||||||
|
* Examines these functions arguments, looks for properties with the specified name there
|
||||||
|
* and if it exists, changes it's value to what was specified.
|
||||||
|
*
|
||||||
|
* @param {string} propertyName name of the property
|
||||||
|
* @param {*} overrideValue new value for the property
|
||||||
|
*/
|
||||||
|
const jsonOverride = (propertyName, overrideValue) => {
|
||||||
|
const nativeJSONParse = JSON.parse;
|
||||||
|
JSON.parse = (...args) => {
|
||||||
|
const obj = nativeJSONParse.apply(this, args);
|
||||||
|
// Override it's props and return back to the caller
|
||||||
|
overrideObject(obj, propertyName, overrideValue);
|
||||||
|
return obj;
|
||||||
|
};
|
||||||
|
// Override Response.prototype.json
|
||||||
|
Response.prototype.json = new Proxy(Response.prototype.json, {
|
||||||
|
async apply(...args) {
|
||||||
|
// Call the target function, get the original Promise
|
||||||
|
const result = await Reflect.apply(...args);
|
||||||
|
// Create a new one and override the JSON inside
|
||||||
|
overrideObject(result, propertyName, overrideValue);
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
// Removes ads metadata from YouTube XHR requests
|
||||||
|
jsonOverride("adPlacements", []);
|
||||||
|
jsonOverride("playerAds", []);
|
||||||
|
// Applies CSS that hides YouTube ad elements
|
||||||
|
hideElements();
|
||||||
|
// Some changes should be re-evaluated on every page change
|
||||||
hideDynamicAds();
|
hideDynamicAds();
|
||||||
autoSkipAds();
|
autoSkipAds();
|
||||||
});
|
observeDomChanges(() => {
|
||||||
|
hideDynamicAds();
|
||||||
|
autoSkipAds();
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue