Greasy Fork is available in English.
Instantly skips YouTube ads (skippable & non-skippable) and blocks ad elements. Features: customizable settings, statistics tracking, keyboard shortcuts. © 2025 Gurveer. All rights reserved.
当前为
// ==UserScript==
// @name YouTube Ad Skipper @ Gurveer
// @namespace https://github.com/gurr-i/browser-scripts
// @version 9.0.0
// @author Gurveer (@Gurveer)
// @description Instantly skips YouTube ads (skippable & non-skippable) and blocks ad elements. Features: customizable settings, statistics tracking, keyboard shortcuts. © 2025 Gurveer. All rights reserved.
// @license All Rights Reserved
// @copyright 2025, Gurveer (@Gurveer)
// @icon https://www.google.com/s2/favicons?domain=youtube.com
// @match https://www.youtube.com/*
// @match https://m.youtube.com/*
// @match https://music.youtube.com/*
// @exclude https://studio.youtube.com/*
// @grant none
// @run-at document-start
// @noframes
// ==/UserScript==
(function () {
'use strict';
/**
* YouTube Ad Skipper
*
* @author Gurveer (@Gurveer)
* @version 1.0.0
* @license All Rights Reserved
* @copyright 2025 Gurveer. All rights reserved.
*
* This script automatically skips YouTube ads and blocks ad elements.
* Features:
* - Instant ad skipping (skippable and non-skippable)
* - Interface ad blocking
* - Customizable settings
* - Statistics tracking
* - Keyboard shortcuts (Ctrl+Shift+A for settings, Ctrl+Shift+S for stats)
*/
(function() {
function loadSettings() {
try {
const saved = localStorage.getItem("ytAdSkipperSettings");
return saved ? JSON.parse(saved) : {};
} catch (e) {
return {};
}
}
let videoPlayer;
let originalVolume = 1;
let originalMuted = false;
let adStats = {
blocked: 0,
skipped: 0,
sessionStart: Date.now()
};
let lastAdSkipTime = 0;
let adSkipCooldown = 2e3;
const settings = {
autoUnmute: true,
speedUpAds: false,
// Disabled by default for better compatibility
showStats: true,
showNotifications: true,
debugMode: false,
// Disabled in production
...loadSettings()
};
const adSelectors = [
"#masthead-ad",
"ytd-rich-item-renderer.style-scope.ytd-rich-grid-row #content:has(.ytd-display-ad-renderer)",
".video-ads.ytp-ad-module",
"tp-yt-paper-dialog:has(yt-mealbar-promo-renderer)",
'ytd-engagement-panel-section-list-renderer[target-id="engagement-panel-ads"]',
"#related #player-ads",
"#related ytd-ad-slot-renderer",
"ytd-ad-slot-renderer",
"ytd-in-feed-ad-layout-renderer",
"yt-mealbar-promo-renderer",
'ytd-popup-container:has(a[href="/premium"])',
"ad-slot-renderer",
"ytm-companion-ad-renderer",
".ytp-ad-overlay-container",
".ytp-ad-text-overlay"
];
window.debugMode = settings.debugMode;
function formatDate(date) {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, "0");
const day = String(date.getDate()).padStart(2, "0");
const hours = String(date.getHours()).padStart(2, "0");
const minutes = String(date.getMinutes()).padStart(2, "0");
const seconds = String(date.getSeconds()).padStart(2, "0");
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
}
function debugLog(message) {
if (!window.debugMode) return;
console.log(`[Ad Skipper] ${formatDate(/* @__PURE__ */ new Date())} - ${message}`);
}
function saveSettings() {
try {
localStorage.setItem("ytAdSkipperSettings", JSON.stringify(settings));
} catch (e) {
debugLog("Failed to save settings: " + e.message);
}
}
function showNotification(message, duration = 3e3) {
if (!settings.showNotifications) return;
if (!document.getElementById("michroma-font")) {
const fontLink = document.createElement("link");
fontLink.id = "michroma-font";
fontLink.rel = "stylesheet";
fontLink.href = "https://fonts.googleapis.com/css2?family=Michroma&display=swap";
document.head.appendChild(fontLink);
}
const notification = document.createElement("div");
notification.style.cssText = `
position: fixed;
top: 80px;
right: 20px;
background: rgba(0, 0, 0, 0.75);
color: #fff;
padding: 12px 20px;
border-radius: 8px;
font-family: 'Michroma', sans-serif;
font-size: 13px;
z-index: 999998;
box-shadow: 0 4px 12px rgba(0,0,0,0.5);
backdrop-filter: blur(10px);
animation: slideIn 0.3s ease-out;
`;
notification.textContent = message;
const style = document.createElement("style");
style.textContent = `
@keyframes slideIn {
from { transform: translateX(400px); opacity: 0; }
to { transform: translateX(0); opacity: 1; }
}
`;
document.head.appendChild(style);
document.body.appendChild(notification);
setTimeout(() => {
notification.style.animation = "slideIn 0.3s ease-out reverse";
setTimeout(() => notification.remove(), 300);
}, duration);
}
function saveVideoState() {
if (videoPlayer && !videoPlayer.classList.contains("ad-showing")) {
originalVolume = videoPlayer.volume;
originalMuted = videoPlayer.muted;
debugLog(`Saved video state: volume=${originalVolume}, muted=${originalMuted}`);
}
}
function restoreVideoState() {
if (videoPlayer && settings.autoUnmute) {
videoPlayer.volume = originalVolume;
videoPlayer.muted = originalMuted;
debugLog("Restored video state");
}
}
function updateStats() {
if (!settings.showStats) return;
if (!document.getElementById("michroma-font")) {
const fontLink = document.createElement("link");
fontLink.id = "michroma-font";
fontLink.rel = "stylesheet";
fontLink.href = "https://fonts.googleapis.com/css2?family=Michroma&display=swap";
document.head.appendChild(fontLink);
}
let statsDiv = document.getElementById("yt-ad-skipper-stats");
if (!statsDiv) {
statsDiv = document.createElement("div");
statsDiv.id = "yt-ad-skipper-stats";
statsDiv.style.cssText = `
position: fixed;
bottom: 20px;
right: 20px;
background: rgba(0, 0, 0, 0.41);
color: #fff;
padding: 12px 18px;
border-radius: 8px;
font-family: 'Michroma', sans-serif;
font-size: 11px;
z-index: 999997;
min-width: 160px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.39);
backdrop-filter: blur(10px);
`;
document.body.appendChild(statsDiv);
}
const sessionTime = Math.floor((Date.now() - adStats.sessionStart) / 6e4);
statsDiv.textContent = "";
const title = document.createElement("div");
title.style.cssText = "font-weight: bold; margin-bottom: 8px; font-size: 10px; letter-spacing: 0.5px;";
title.textContent = "🛡️ Ad Skipper @Gurveer";
const blocked = document.createElement("div");
blocked.style.cssText = "margin: 4px 0; letter-spacing: 0.3px; font-size: 8px;";
blocked.textContent = `Blocked: ${adStats.blocked}`;
const skipped = document.createElement("div");
skipped.style.cssText = "margin: 4px 0; letter-spacing: 0.3px; font-size: 8px;";
skipped.textContent = `Skipped: ${adStats.skipped}`;
const session = document.createElement("div");
session.style.cssText = "margin: 4px 0; letter-spacing: 0.3px; font-size: 8px;";
session.textContent = `Session: ${sessionTime}m`;
statsDiv.appendChild(title);
statsDiv.appendChild(blocked);
statsDiv.appendChild(skipped);
statsDiv.appendChild(session);
}
function setExecutionFlag(flagId) {
const style = document.createElement("style");
style.id = flagId;
(document.head || document.body).appendChild(style);
}
function getExecutionFlag(flagId) {
return document.getElementById(flagId);
}
function hasExecutionFlag(flagId) {
if (getExecutionFlag(flagId)) {
return true;
}
setExecutionFlag(flagId);
return false;
}
function createAdBlockStyle(flagId) {
if (hasExecutionFlag(flagId)) {
debugLog("Ad-blocking style already applied");
return;
}
const style = document.createElement("style");
(document.head || document.body).appendChild(style);
style.appendChild(document.createTextNode(generateAdBlockCss(adSelectors)));
const blockedAds = document.querySelectorAll(adSelectors.join(","));
adStats.blocked = blockedAds.length;
updateStats();
debugLog("Ad-blocking style applied successfully");
}
function generateAdBlockCss(selectors) {
return selectors.map((selector) => `${selector}{display:none!important}`).join(" ");
}
function simulateTouch() {
try {
const touch = new Touch({
identifier: Date.now(),
target: this,
clientX: 12,
clientY: 34,
radiusX: 56,
radiusY: 78,
rotationAngle: 0,
force: 1
});
const touchStart = new TouchEvent("touchstart", {
bubbles: true,
cancelable: true,
view: window,
touches: [touch],
targetTouches: [touch],
changedTouches: [touch]
});
this.dispatchEvent(touchStart);
const touchEnd = new TouchEvent("touchend", {
bubbles: true,
cancelable: true,
view: window,
touches: [],
targetTouches: [],
changedTouches: [touch]
});
this.dispatchEvent(touchEnd);
} catch (e) {
debugLog("Touch simulation failed: " + e.message);
}
}
function fetchVideoElement() {
videoPlayer = document.querySelector(".ad-showing video") || document.querySelector(".html5-main-video") || document.querySelector("video.video-stream") || document.querySelector("video");
if (videoPlayer) {
debugLog("Video element found: " + videoPlayer.className);
} else {
debugLog("No video element found");
}
}
function resumePlayback() {
if ((videoPlayer == null ? void 0 : videoPlayer.paused) && videoPlayer.currentTime < 1) {
videoPlayer.play();
debugLog("Resumed video playback");
}
}
function clearOverlay() {
const premiumPopups = [...document.querySelectorAll("ytd-popup-container")];
const targetPopups = premiumPopups.filter((popup) => popup.querySelector('a[href="/premium"]'));
if (targetPopups.length > 0) {
targetPopups.forEach((popup) => popup.remove());
debugLog("Removed ad blocker popup");
}
const backdrops = document.querySelectorAll("tp-yt-iron-overlay-backdrop");
const targetBackdrop = Array.from(backdrops).find((backdrop) => backdrop.style.zIndex === "2201");
if (targetBackdrop) {
targetBackdrop.className = "";
targetBackdrop.removeAttribute("opened");
debugLog("Closed overlay backdrop");
}
}
function bypassAd() {
if (!videoPlayer) {
debugLog("bypassAd: No video player");
return;
}
const skipBtn = document.querySelector(".ytp-ad-skip-button") || document.querySelector(".ytp-skip-ad-button") || document.querySelector(".ytp-ad-skip-button-modern");
const shortAd = document.querySelector(".video-ads.ytp-ad-module .ytp-ad-player-overlay") || document.querySelector(".ytp-ad-button-icon");
const adShowing = videoPlayer.classList.contains("ad-showing");
const adInterrupting = videoPlayer.classList.contains("ad-interrupting");
const adPlaying = document.querySelector(".ad-showing");
const isAdPlaying = skipBtn || shortAd || adShowing || adInterrupting || adPlaying;
if (isAdPlaying) {
const now = Date.now();
if (now - lastAdSkipTime < adSkipCooldown) {
return;
}
debugLog(`AD DETECTED! Skip: ${!!skipBtn}, Short: ${!!shortAd}, Showing: ${adShowing}, Interrupting: ${adInterrupting}, AdPlaying: ${!!adPlaying}`);
if (!videoPlayer.dataset.adStateSaved) {
saveVideoState();
videoPlayer.dataset.adStateSaved = "true";
debugLog("Saved video state");
}
if (!window.location.href.includes("https://m.youtube.com/")) {
if (!videoPlayer.muted) {
videoPlayer.muted = true;
debugLog("Muted ad");
}
}
if (settings.speedUpAds && videoPlayer.playbackRate < 16) {
videoPlayer.playbackRate = 16;
debugLog("Sped up ad to 16x");
}
if (skipBtn) {
debugLog("Clicking skip button...");
skipBtn.click();
simulateTouch.call(skipBtn);
adStats.skipped++;
updateStats();
showNotification("⚡ Ad skipped!", 2e3);
debugLog("Clicked skip ad button");
lastAdSkipTime = now;
if (videoPlayer.duration && videoPlayer.duration > 0) {
videoPlayer.currentTime = videoPlayer.duration - 0.1;
debugLog("Force-ended skippable ad");
}
} else if (videoPlayer.duration && videoPlayer.duration > 0 && !isNaN(videoPlayer.duration)) {
debugLog(`Forcing ad end: current=${videoPlayer.currentTime}, duration=${videoPlayer.duration}`);
videoPlayer.currentTime = videoPlayer.duration - 0.1;
adStats.skipped++;
updateStats();
showNotification("⚡ Ad skipped!", 2e3);
debugLog("Force-ended non-skippable ad");
lastAdSkipTime = now;
}
} else {
if (videoPlayer.dataset.adStateSaved) {
if (videoPlayer.playbackRate > 1) {
videoPlayer.playbackRate = 1;
debugLog("Restored playback rate");
}
restoreVideoState();
delete videoPlayer.dataset.adStateSaved;
debugLog("Restored video state - ad ended");
}
}
}
function checkAndSkipAds() {
try {
fetchVideoElement();
clearOverlay();
bypassAd();
resumePlayback();
} catch (error) {
debugLog("Error in ad blocker: " + error.message);
}
}
function blockPlayerAds(flagId) {
if (hasExecutionFlag(flagId)) {
debugLog("Player ad blocker already running");
return;
}
const target = document.body;
const config = { childList: true, subtree: true };
const observer = new MutationObserver(checkAndSkipAds);
observer.observe(target, config);
debugLog("Player ad blocker started successfully");
setInterval(checkAndSkipAds, 2e3);
}
function createSettingsPanel() {
if (!document.getElementById("michroma-font")) {
const fontLink = document.createElement("link");
fontLink.id = "michroma-font";
fontLink.rel = "stylesheet";
fontLink.href = "https://fonts.googleapis.com/css2?family=Michroma&display=swap";
document.head.appendChild(fontLink);
}
const backdrop = document.createElement("div");
backdrop.id = "yt-ad-skipper-backdrop";
backdrop.style.cssText = `
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.7);
z-index: 999998;
display: none;
backdrop-filter: blur(5px);
`;
document.body.appendChild(backdrop);
const panel = document.createElement("div");
panel.id = "yt-ad-skipper-settings";
panel.style.cssText = `
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: rgba(0, 0, 0, 0.75);
color: #fff;
padding: 24px;
border-radius: 12px;
font-family: 'Michroma', sans-serif;
z-index: 999999;
box-shadow: 0 8px 32px rgba(0,0,0,0.6);
backdrop-filter: blur(10px);
display: none;
min-width: 320px;
max-width: 420px;
`;
const title = document.createElement("h3");
title.style.cssText = "margin: 0 0 20px 0; font-size: 16px; letter-spacing: 1px; text-align: center;";
title.textContent = "⚙️ Ad Skipper Settings";
panel.appendChild(title);
const options = [
{ id: "autoUnmute", label: "Auto-unmute after ads", checked: settings.autoUnmute },
{ id: "speedUpAds", label: "Speed up ads (16x)", checked: settings.speedUpAds },
{ id: "showStats", label: "Show statistics", checked: settings.showStats },
{ id: "showNotifications", label: "Show notifications", checked: settings.showNotifications },
{ id: "debugMode", label: "Debug mode", checked: settings.debugMode }
];
options.forEach((opt) => {
const label = document.createElement("label");
label.style.cssText = "display: block; margin: 12px 0; cursor: pointer; font-size: 11px; letter-spacing: 0.3px;";
const checkbox = document.createElement("input");
checkbox.type = "checkbox";
checkbox.id = opt.id;
checkbox.checked = opt.checked;
checkbox.style.cssText = "margin-right: 8px; cursor: pointer;";
label.appendChild(checkbox);
label.appendChild(document.createTextNode(" " + opt.label));
panel.appendChild(label);
});
const saveBtn = document.createElement("button");
saveBtn.id = "saveSettings";
saveBtn.textContent = "Save Settings";
saveBtn.style.cssText = `
margin-top: 20px;
padding: 10px 18px;
background: rgba(255, 0, 0, 0.8);
color: #fff;
border: none;
border-radius: 6px;
cursor: pointer;
font-family: 'Michroma', sans-serif;
font-size: 11px;
letter-spacing: 0.5px;
width: 100%;
transition: background 0.3s ease;
`;
saveBtn.onmouseover = () => saveBtn.style.background = "rgba(255, 0, 0, 1)";
saveBtn.onmouseout = () => saveBtn.style.background = "rgba(255, 0, 0, 0.8)";
panel.appendChild(saveBtn);
const closeBtn = document.createElement("button");
closeBtn.id = "closeSettings";
closeBtn.textContent = "Close";
closeBtn.style.cssText = `
margin-top: 10px;
padding: 10px 18px;
background: rgba(96, 96, 96, 0.8);
color: #fff;
border: none;
border-radius: 6px;
cursor: pointer;
font-family: 'Michroma', sans-serif;
font-size: 11px;
letter-spacing: 0.5px;
width: 100%;
transition: background 0.3s ease;
`;
closeBtn.onmouseover = () => closeBtn.style.background = "rgba(96, 96, 96, 1)";
closeBtn.onmouseout = () => closeBtn.style.background = "rgba(96, 96, 96, 0.8)";
panel.appendChild(closeBtn);
document.body.appendChild(panel);
const closePanel = () => {
panel.style.display = "none";
backdrop.style.display = "none";
};
saveBtn.addEventListener("click", () => {
settings.autoUnmute = document.getElementById("autoUnmute").checked;
settings.speedUpAds = document.getElementById("speedUpAds").checked;
settings.showStats = document.getElementById("showStats").checked;
settings.showNotifications = document.getElementById("showNotifications").checked;
settings.debugMode = document.getElementById("debugMode").checked;
window.debugMode = settings.debugMode;
saveSettings();
showNotification("✅ Settings saved!");
closePanel();
if (!settings.showStats) {
const statsDiv = document.getElementById("yt-ad-skipper-stats");
if (statsDiv) statsDiv.remove();
} else {
updateStats();
}
});
closeBtn.addEventListener("click", closePanel);
backdrop.addEventListener("click", closePanel);
}
function addKeyboardShortcuts() {
document.addEventListener("keydown", (e) => {
if (e.ctrlKey && e.shiftKey && e.key === "A") {
e.preventDefault();
const panel = document.getElementById("yt-ad-skipper-settings");
const backdrop = document.getElementById("yt-ad-skipper-backdrop");
if (panel && backdrop) {
const isVisible = panel.style.display !== "none";
panel.style.display = isVisible ? "none" : "block";
backdrop.style.display = isVisible ? "none" : "block";
}
}
if (e.ctrlKey && e.shiftKey && e.key === "S") {
e.preventDefault();
settings.showStats = !settings.showStats;
saveSettings();
if (settings.showStats) {
updateStats();
showNotification("📊 Stats enabled");
} else {
const statsDiv = document.getElementById("yt-ad-skipper-stats");
if (statsDiv) statsDiv.remove();
showNotification("📊 Stats disabled");
}
}
});
}
function initialize() {
debugLog("========================================");
debugLog("Initializing YouTube Ad Skipper v1.0.0");
debugLog("Author: Gurveer (@Gurveer)");
debugLog("========================================");
createAdBlockStyle("adBlockStyle");
blockPlayerAds("playerAdBlock");
createSettingsPanel();
addKeyboardShortcuts();
updateStats();
setTimeout(checkAndSkipAds, 1e3);
setInterval(() => {
if (settings.showStats) {
const blockedAds = document.querySelectorAll(adSelectors.join(","));
adStats.blocked = blockedAds.length;
updateStats();
}
}, 5e3);
debugLog("YouTube Ad Skipper initialized successfully");
debugLog("Press Ctrl+Shift+A to open settings");
showNotification("🛡️ Ad Skipper Active", 3e3);
}
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", initialize);
debugLog("YouTube ad skipper scheduled");
} else {
initialize();
}
function resumeAndClear() {
const video = document.querySelector("video.html5-main-video");
if (video == null ? void 0 : video.paused) {
video.play();
debugLog("Resumed paused video");
}
}
function clearPopup(node) {
var _a, _b;
try {
const popup = (_a = node.querySelector) == null ? void 0 : _a.call(node, ".ytd-popup-container .ytd-enforcement-message-view-model");
if (popup && popup.parentNode) {
popup.parentNode.remove();
debugLog("Removed popup element");
const backdrops = document.getElementsByTagName("tp-yt-iron-overlay-backdrop");
for (let i = backdrops.length - 1; i >= 0; i--) {
backdrops[i].remove();
}
resumeAndClear();
}
if (((_b = node.tagName) == null ? void 0 : _b.toLowerCase()) === "tp-yt-iron-overlay-backdrop") {
node.remove();
debugLog("Removed backdrop element");
resumeAndClear();
}
} catch (error) {
debugLog("Error clearing popup: " + error.message);
}
}
const popupObserver = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (mutation.type === "childList") {
Array.from(mutation.addedNodes).filter((node) => node.nodeType === 1).forEach((node) => clearPopup(node));
}
});
});
if (document.body) {
popupObserver.observe(document.body, {
childList: true,
subtree: true
});
} else {
document.addEventListener("DOMContentLoaded", () => {
popupObserver.observe(document.body, {
childList: true,
subtree: true
});
});
}
})();
})();