您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
none
// ==UserScript== // @name yt blocker // @version 29 // @description none // @run-at document-start // @author rssaromeo // @license GPLv3 // @match *://youtube.com/* // @match *://*.youtube.com/* // @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEgAAABICAMAAABiM0N1AAAAAXNSR0IB2cksfwAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAHJQTFRFAAAAEIijAo2yAI60BYyuF4WaFIifAY6zBI2wB4usGIaZEYigIoiZCIyrE4igG4iYD4mjEomhFoedCoqpDIqnDomlBYyvE4efEYmiDYqlA42xBoytD4mkCYqqGYSUFYidC4qoC4upAo6yCoupDYqmCYur4zowOQAAACZ0Uk5TAO////9vr////1+/D/+/L+/Pf/////+f3///////H4////////+5G91rAAACgUlEQVR4nM2Y22KjIBCGidg1264liZqDadK03X3/V2wNKHMC7MpF/xthHD5mgERAqZhWhfYqH6K+Qf2qNNf625hCoFj9/gblMUi5q5jLkXLCKudgyiRm0FMK82cWJp1fLbV5VmvJbCIc0GCYaFqqlDJgADdBjncqAXYobm1xh72aFMflbysteFfdy2Yi1XGOm5HGBzQ1dq7TzEoxjeNTjQZb7VA3e1c7+ImgasAgQ9+xusNVNZIo5xmOMgihIS2PbCQIiHEUdTvhxCcS/kPomfFI2zHy2PkWmA6aNatIJpKFJyekyy02xh5Y3DI9T4aOT6VhIUrsNTFp1pf79Z4SIIVDegl6IJO6cHiL/GimIZDhgTu/BlYWCQzHMl0zBWT/T3KAhtxOuUB9FtBrpsz0RV4xsjHmW+UCaffcSy/5viMGer0/6HdFNMZBq/vjJL38H9Dqx4Fuy0Em12DbZy+9pGtiDijbglwAehyj11n0tRD3WUBm+lwulE/8h4BuA+iWAQQnteg2Xm63WQLTpnMnpjdge0Mgu/GRPsV4xdjQ94Lfi624fabhDkfUqIKNrM64Q837v8yL0prasepCgrtvw1sJpoqanGEX7b5mQboNW8eawXaWXTMfMGxub472hzWzHSn6Sg2G9+6TAyRruE71s+zAzjWaknoyJCQzwxrghH2k5FDT4eqWunuNxyN9QCGcxVod5oADbYnIUkDTGZEf1xDJnSFteQ3KdsT8zYDMQXcHxsevcLH1TrsABzkNPyA/L7b0jg704viMMlpQI96WsHknCt/3YH0kOEo9zcGkwrFK39ck72rmoehmKqo2RKlilzSy/nJKEV45CT38myJp456fezktHjN5aeMAAAAASUVORK5CYII= // @grant unsafeWindow // @require https://update.greasyfork.org/scripts/491829/1356221/tampermonkey%20storage%20proxy.js // @grant GM_getValue // @grant GM_setValue // @grant unsafeWindow // @namespace https://greasyfork.org/users/1184528 // ==/UserScript== // % of video watched to hide, 0 to not hide any const hideWatchedVidProg = 1 const rclickBlocksUrl = true const rclickBlocksTitle = true const updateInterval = 1000 unsafeWindow.newJsData ??= [] // Intercept XMLHttpRequest const originalOpen = XMLHttpRequest.prototype.open unsafeWindow.XMLHttpRequest.prototype.open = function (method, url) { // log(url) if (url.includes("youtube.com/youtubei/v1/player")) { const originalSend = this.send this.send = function (body) { this.addEventListener("load", function () { try { const responseData = JSON.parse(this.responseText) unsafeWindow.newJsData.push(responseData) console.log( "Intercepted YouTube player data:", responseData ) } catch (e) { console.error("Failed to parse response data:", e) } }) originalSend.call(this, body) } } originalOpen.call(this, method, url) } // Intercept fetch requests const originalFetch = window.fetch unsafeWindow.fetch = newfetch var timerId = setInterval(() => { if (unsafeWindow.fetch != newfetch) { clearInterval(timerId) log("Intercepted fetch request:") unsafeWindow.fetch = newfetch } }) function newfetch(...args) { const url = args[0] // log(url) if ( (url?.url ?? url)?.includes?.("youtube.com/youtubei/v1/player") ) { return originalFetch.apply(this, args).then((response) => { const clone = response.clone() // Clone the response to read it clone .json() .then((data) => { unsafeWindow.newJsData.push(data) console.log( "Intercepted YouTube player data from fetch:", data ) }) .catch((e) => { console.error("Failed to parse fetch response data:", e) }) return response // Return the original response }) } return originalFetch.apply(this, args) } ;(async () => { const a = loadlib("allfuncs") const sp = new storageproxy("globaloptions") var ls = sp.get() // var lastvol // stores the last volume to restore to when muting from sub button border var vidlock = true const LOC = {} updateLoc() // var LOADED = false function setVidSpeed() { unsafeWindow.novidspeedcontroller = true for (var vid of a.qsa("video")) { vid.playbackRate = vidlock || !unsafeWindow.ytInitialData ? 0 : Number(!globalname ? 0 : localStorage.playrate ?? 0) } } ls.blockedCreators ??= [] ls.blockedTitles ??= [] ls.blockedTitlesReg ??= [] ls.blockedCreatorsReg ??= [] ls.blockedUrls ??= [] if (LOC.watch) { var fastint = setInterval(setVidSpeed, 0) setVidSpeed() update() a.waituntil(() => globalname).then(()=> clearInterval(fastint)) } setInterval(update, updateInterval) // LOADED = true var globalname function newBlockBtn(title, creator) { var elem = a.newelem("button", { innerHTML: ls.blockedCreators.includes(this.title) ? "unblock" : "block", creator, title, id: "blockbtn", onclick(e) { e.stopImmediatePropagation() e.stopPropagation() e.preventDefault() log(this.creator) if (ls.blockedCreators.includes(this.creator)) { ls.blockedCreators.splice( ls.blockedCreators.indexOf(this.creator), 1 ) } else { ls.blockedCreators.push(this.creator) } update() log(ls.blockedCreators, this.creator) }, oncontextmenu(e) { e.stopImmediatePropagation() e.stopPropagation() e.preventDefault() if (rclickBlocksUrl) { log(this.url) if (ls.blockedUrls.includes(this.url)) { ls.blockedUrls.splice(ls.blockedUrls.indexOf(this.url), 1) } else { ls.blockedUrls.push(this.url) } update() log(ls.blockedUrls, this.url) } if (rclickBlocksTitle) { log(this.title) if (ls.blockedTitles.includes(this.title)) { ls.blockedTitles.splice( ls.blockedTitles.indexOf(this.title), 1 ) } else { ls.blockedTitles.push(this.title) } update() log(ls.blockedTitles, this.title) } }, }) ;((elem, creator) => { var val = creator Object.defineProperty(elem, "creator", { get() { return val }, set(newval) { val = newval.replace(/ • \d+\w? views$/, "") }, enumerable: true, configurable: true, }) })(elem, creator) return elem } update() function update() { try { function getCreatorId(viddiv, url) { var id = a .qs(url, viddiv) .href.split("?v=") .at(-1) .split("&")[0] var video = findAllKeys( unsafeWindow.ytInitialData, "lockupMetadataViewModel" ).find((e) => findValue(e, id)) var res = findKey(video, "browseEndpoint")?.browseId if (res) return res log( 1111111, unsafeWindow.newJsData.find((e) => findValue(e, id)) ) if (LOC.watch || LOC.home){ location.reload() } error(video, viddiv) return '' } updateLoc() unsafeWindow.ls = ls = sp.get() // log(LOC) if (LOC.search) { addVid( "div#dismissible.style-scope.ytd-video-renderer:has(a#video-title)", "a#video-title", "#video-title", getCreatorId, "#channel-info", "#text > a" ) } else if (LOC.root) { globalname = null addVid( "ytd-rich-item-renderer:has(a>.cbCustomTitle):has(a.yt-core-attributed-string__link):has(yt-lockup-metadata-view-model)", "a:has(> .cbCustomTitle:first-child)", "a:has(> .cbCustomTitle:first-child)>*:not(.cbCustomTitle)", getCreatorId, "yt-lockup-metadata-view-model", "a.yt-core-attributed-string__link" ) addVid( "ytd-rich-item-renderer:has(#content > yt-lockup-view-model > div > div > yt-lockup-metadata-view-model > div.yt-lockup-metadata-view-model-wiz__text-container > div > yt-content-metadata-view-model > div:nth-child(1) > span > span > a)", "#content > yt-lockup-view-model > div > a", "#content > yt-lockup-view-model > div > div > yt-lockup-metadata-view-model > div.yt-lockup-metadata-view-model-wiz__text-container > h3 > a > span.yt-core-attributed-string.yt-core-attributed-string--white-space-pre-wrap:not(.cbCustomTitle)", getCreatorId, "#content > yt-lockup-view-model > div > div > yt-lockup-metadata-view-model", "#content > yt-lockup-view-model > div > div > yt-lockup-metadata-view-model > div.yt-lockup-metadata-view-model-wiz__text-container > div > yt-content-metadata-view-model > div:nth-child(1) > span > span > a" ) } else if (LOC.feed) { addVid( "ytd-rich-item-renderer:has(a>.cbCustomTitle):has(a.yt-core-attributed-string__link):has(yt-lockup-metadata-view-model)", "a:has(> .cbCustomTitle:first-child)", "a:has(> .cbCustomTitle:first-child)>*:not(.cbCustomTitle)", getCreatorId, "yt-lockup-metadata-view-model", "a.yt-core-attributed-string__link" ) addVid( "#dismissible:has(#video-title-link)", "#video-title-link", "#video-title-link>yt-formatted-string:not(.cbCustomTitle)", getCreatorId, "#byline-container", "#container>#text-container>yt-formatted-string#text>a" ) // ['contents']['twoColumnBrowseResultsRenderer']['tabs']['0']['tabRenderer']['content']['richGridRenderer']['contents']['5']['richItemRenderer']['content']['lockupViewModel'] } else if (LOC.userhome || LOC.uservids) { const CREATOR = getCreatorNameFromUrl(location.href) const btn = a.qs( "#page-header > yt-page-header-renderer > yt-page-header-view-model > div > div.page-header-view-model-wiz__page-header-headline > div > yt-dynamic-text-view-model > h1 > #blockbtn" ) if ( a.qs( "#page-header > yt-page-header-renderer > yt-page-header-view-model > div > div.page-header-view-model-wiz__page-header-headline > div > yt-dynamic-text-view-model > h1" ) && !btn ) { a.qs( "#page-header > yt-page-header-renderer > yt-page-header-view-model > div > div.page-header-view-model-wiz__page-header-headline > div > yt-dynamic-text-view-model > h1" ).appendChild(newBlockBtn(null, CREATOR)) } else if (btn) { btn.innerHTML = isBlocked(btn.creator, btn.title, btn.url) ? "unblock - " + JSON.stringify( isBlocked(btn.creator, btn.title, btn.url) ) : "block" } if (LOC.userhome) { addVid( "#dismissible:has(#video-title-link)", "#video-title-link", "#video-title-link", () => CREATOR, "#byline-container" ) addVid( "#dismissible:has(#video-title-link)", "#video-title-link", "#video-title-link", (viddiv, url) => { var id = a .qs(url, viddiv) .href.split("?v=") .at(-1) .split("&")[0] var video = findAllKeys( unsafeWindow.ytInitialData, "lockupMetadataViewModel" ).find((e) => findValue(e, id)) return ( findKey(video, "browseEndpoint")?.browseId ?? error(video, viddiv) ) }, "#byline-container", "#container>#text-container>yt-formatted-string#text" ) } if (LOC.uservids) { addVid( "ytd-rich-item-renderer:has(#dismissible #video-title-link)", "#video-title-link", "#video-title-link > #video-title:not(.cbCustomTitle)", () => CREATOR, "#meta > h3" ) } } else if (LOC.watch) { if (!a.qs("#playrate") && a.qs(".ytp-right-controls")) { a.qs(".ytp-right-controls").appendChild( a.newelem( "div", { display: "inline flex", flexDirection: "row", id: "playrate", class: "ytp-button ytp-settings-button ytp-hd-quality-badge", width: "fit-content", }, [ a.newelem("button", { innerHTML: ".1", onclick() { localStorage.playrate = 0.1 update() }, }), a.newelem("button", { innerHTML: ".25", onclick() { localStorage.playrate = 0.25 update() }, }), a.newelem("button", { innerHTML: "1", backgroundColor: "#449", onclick() { localStorage.playrate = 1 update() }, }), a.newelem("button", { innerHTML: "1.4", onclick() { localStorage.playrate = 1.4 update() }, }), a.newelem("button", { innerHTML: "1.8", onclick() { localStorage.playrate = 1.8 update() }, }), a.newelem("button", { innerHTML: "2", backgroundColor: "#449", onclick() { localStorage.playrate = 2 update() }, }), a.newelem("button", { innerHTML: "2.5", onclick() { localStorage.playrate = 2.5 update() }, }), ] ) ) } globalname = unsafeWindow.ytInitialData?.["contents"]?.[ "twoColumnWatchNextResults" ]?.["results"]?.["results"]?.["contents"]?.["1"]?.[ "videoSecondaryInfoRenderer" ]?.["subscribeButton"]?.["subscribeButtonRenderer"]?.[ "channelId" ] || unsafeWindow.ytInitialData?.["header"]?.[ "pageHeaderRenderer" ]?.["content"]?.["pageHeaderViewModel"]?.["actions"]?.[ "flexibleActionsViewModel" ]?.["actionsRows"]?.["0"]?.["actions"]?.["0"]?.[ "subscribeButtonViewModel" ]?.["subscribeButtonContent"]?.["onTapCommand"]?.[ "innertubeCommand" ]?.["subscribeEndpoint"]?.["channelIds"] // a.qs( // "#upload-info>ytd-channel-name#channel-name>#container>#text-container>yt-formatted-string#text > a" // )?.textContent if ( a.qs("ytd-video-owner-renderer") && a.qs("#title > h1 > span.cbCustomTitle") && globalname ) { addVid( "ytd-watch-metadata", () => location.href.split("?v=").at(-1).split("&")[0], "#title > h1 > yt-formatted-string", () => { return globalname }, "ytd-video-owner-renderer", "#container>#text-container>yt-formatted-string#text>a" ) a.qs("ytd-watch-metadata").style.display = "" const btn = a.qs("ytd-video-owner-renderer>#blockbtn") vidlock = isBlocked(btn.creator, btn.title, btn.url) btn.innerHTML = isBlocked(btn.creator, btn.title, btn.url) ? "unblock - " + JSON.stringify( isBlocked(btn.creator, btn.title, btn.url) ) : "block" } // these don't have creator id anywhere only creator name so all will be hidden // addVid( // "ytd-compact-video-renderer:has(#dismissible #video-title-link)", // "#video-title-link", // "#container>#text-container>yt-formatted-string#text", // "#metadata" // ) // these also don't have creator data // addVid( // "#movie_player > div.html5-endscreen.ytp-player-content.videowall-endscreen.ytp-show-tiles > div > a:has(span.ytp-videowall-still-info)", // "span.ytp-videowall-still-info > span > span>div>.cbCustomTitle", // "span.ytp-videowall-still-info > span > span>.ytp-videowall-still-info-author", // "span.ytp-videowall-still-info > span > span>div" // ) // new youtube display classes? addVid( "yt-lockup-view-model:has(yt-lockup-metadata-view-model)", ".yt-lockup-metadata-view-model__title", ".yt-lockup-metadata-view-model__title", getCreatorId, ".yt-lockup-metadata-view-model__text-container" ) // log(video) // log() // a.qsa().forEach((e) => (e.style.visibility = "hidden")) // a.qsa(".ytp-videowall-still").forEach( // (e) => (e.style.visibility = "hidden") // ) // addVid( // "yt-lockup-view-model:has(yt-lockup-metadata-view-model)", // "div.yt-lockup-metadata-view-model-wiz__text-container > h3 > a", // "yt-content-metadata-view-model > div:nth-child(1) > span", // ".yt-lockup-view-model-wiz__metadata:has(yt-lockup-metadata-view-model)" // ) setVidSpeed() } } catch (e) { trace("update", e) } } function findAllKeys(obj, keyToFind) { const results = [] function recurse(currentObj) { for (const key in currentObj) { if (currentObj.hasOwnProperty(key)) { if (key.includes(keyToFind)) { results.push(currentObj[key]) // Add the path to results if it matches } if ( typeof currentObj[key] === "object" && currentObj[key] !== null ) { recurse(currentObj[key]) // Recur for nested objects } } } } recurse(obj, "") return results } function findAllValues(obj, keyToFind) { const results = [] function recurse(currentObj) { for (const key in currentObj) { if (currentObj.hasOwnProperty(key)) { try { if (currentObj[key].includes(keyToFind)) { results.push(currentObj[key]) // Add the path to results if it matches } } catch (e) {} if ( typeof currentObj[key] === "object" && currentObj[key] !== null ) { recurse(currentObj[key]) // Recur for nested objects } } } } recurse(obj, "") return results } function findKey(obj, keyToFind) { const results = [] function recurse(currentObj, exact = true) { if (results.length) { return results[0] } for (const key in currentObj) { if (currentObj.hasOwnProperty(key)) { if (exact ? key === keyToFind : key.includes(keyToFind)) { results.push(currentObj[key]) // Add the path to results if it matches return currentObj[key] } if ( typeof currentObj[key] === "object" && currentObj[key] !== null ) { recurse(currentObj[key]) // Recur for nested objects } } } } recurse(obj, "") return results[0] } function findValue(obj, keyToFind) { const results = [] function recurse(currentObj, exact = true) { if (results.length) { return results[0] } for (const key in currentObj) { if (currentObj.hasOwnProperty(key)) { try { if ( exact ? currentObj[key] === keyToFind : currentObj[key].includes(keyToFind) ) { results.push(currentObj[key]) // Add the path to results if it matches return currentObj[key] } } catch (e) {} if ( typeof currentObj[key] === "object" && currentObj[key] !== null ) { recurse(currentObj[key]) // Recur for nested objects } } } } recurse(obj, "") return results[0] } function addVid( mainDivID, url, titleID, creatorID, blockButtonParentID, // OLDurl, // OLDtitleID, OLDcreatorID // OLDblockButtonParentID ) { try { // log(mainDivID, a.qsa(mainDivID)) // remove invalid block buttons on root // if (isroot()) // a.qsa("#blockbtn") // .filter(function (btn) { // return btn.closest(blockButtonParentID) // }) // .forEach((e) => e.remove()) function trycall(thing) { if (typeof thing === "function") { return thing(viddiv, url, titleID, creatorID) ?? "" } return undefined } for (var viddiv of a.qsa(mainDivID)) { if (viddiv.textContent.includes("Free with ads")) { viddiv.style.display = "none" continue } var btn = a.qs(blockButtonParentID + ">#blockbtn", viddiv) if (!btn) btn = a .qs(blockButtonParentID, viddiv) .appendChild(newBlockBtn("", "")) btn.url = trycall(url) ?? a.qs(url, viddiv).href.split("?v=").at(-1).split("&")[0] btn.title = trycall(titleID) ?? a.qs(titleID, viddiv).textContent.toLowerCase() btn.creator = trycall(creatorID) ?? getCreatorNameFromUrl(a.qs(creatorID, viddiv).href) btn.innerHTML = ls.blockedCreators.includes(btn.creator) ? "unblock " + (btn.creator??"NO CREATOR SET") : "block "+(btn.creator??"NO CREATOR SET") var prog = a.qs( ".ytd-thumbnail-overlay-resume-playback-renderer.style-scope", viddiv ) || a.qs( ".ytThumbnailOverlayProgressBarHostWatchedProgressBarSegment", viddiv ) if (OLDcreatorID && btn.creator) { var OLDcreator = trycall(OLDcreatorID) ?? getCreatorNameFromUrl(a.qs(OLDcreatorID, viddiv).href) delete OLDcreatorID ls.creatorNameDict ??= {} ls.creatorNameDict[btn.creator] ??= [] if (!ls.creatorNameDict[btn.creator].includes(OLDcreator)) { ls.creatorNameDict[btn.creator].push(OLDcreator) } if (ls.blockedCreators.includes(OLDcreator)) { log( "UPDATING!!!", ls.blockedCreators[ ls.blockedCreators.indexOf(OLDcreator) ], btn.creator ) ls.blockedCreators[ ls.blockedCreators.indexOf(OLDcreator) ] = btn.creator } } viddiv.style.display = isBlocked(btn.creator, btn.title, btn.url) || (hideWatchedVidProg && prog && prog.style.width.replace("%", "") > hideWatchedVidProg) ? "none" : "" } } catch (e) { trace("addVid", e, titleID, creatorID, viddiv) } } unsafeWindow.isBlocked = isBlocked unsafeWindow.addVid = addVid function isBlocked(creator, title, url) { try { if (creator == undefined) return { type: "invalid creator", val: { creator, title, url }, } if (ls.blockedUrls.includes(url)) return { type: "blockedUrls", val: url } if (ls.blockedTitles.includes(title)) return { type: "blockedTitles", val: title } if (ls.blockedCreators.includes(creator)) return { type: "blockedCreators", val: creator } for (let reg of ls.blockedCreatorsReg) { if (new RegExp(reg, "i").test(creator)) return { type: "blockedCreatorsReg", val: reg } } for (let reg of ls.blockedTitlesReg) { if (new RegExp(reg, "i").test(title)) return { type: "blockedTitlesReg", val: reg } } return false } catch (e) { trace("isBlocked", e) } } function updateLoc() { Object.assign(LOC, { root: /^https?:\/\/(?:www\.)?youtube\.com\/?(?:\?|#|$)/.test( location.href ), watch: /^https?:\/\/(?:www\.)?youtube\.com\/watch\/?(?:\?|#|$)/.test( location.href ), search: /^https?:\/\/(?:www\.)?youtube\.com\/results\?search_query=.*(?:#|$)/.test( location.href ), feed: /^https?:\/\/(?:www\.)?youtube\.com\/feed\/subscriptions/.test( location.href ), userhome: /^https?:\/\/(?:www\.)?youtube\.com\/@[^\/]+\/?$/.test( location.href ), uservids: /^https?:\/\/(?:www\.)?youtube\.com\/@[^\/]+\/videos\/?$/.test( location.href ) || /^https?:\/\/(?:www\.)?youtube\.com\/(?:channel|user|c)\/[^\/]+\/videos\/?$/.test( location.href ), }) } unsafeWindow.getCreatorNameFromUrl = getCreatorNameFromUrl function getCreatorNameFromUrl(url) { return url.match(/(?:\/@|\/(?:channel|user|c)\/)([^\/]*)/i)?.[1] } })()