// ==UserScript== // @name youtube广告拦截 // @namespace http://tampermonkey.net/ // @version 1.1.1 // @description 是拦截不是跳过也不是删除广告哦, 拦截所有youtube广告(播放页和首页)并且不留白哦,疑似是目前体验最好的 // @author hua // @match https://www.youtube.com/* // @grant unsafeWindow // @run-at document-start // @license MIT // @downloadURL none // ==/UserScript== let href = location.href url_observer() init() function init() { console.log('初始化开始!') let ytInitialPlayerResponse_value = unsafeWindow['ytInitialPlayerResponse'] Object.defineProperty(unsafeWindow, 'ytInitialPlayerResponse', { get: function () { return ytInitialPlayerResponse_value }, set: function (value) { obj_process(value, ["abs:playerAds=-", "abs:adPlacements=-", "abs:adBreakHeartbeatParams=-", 'abs:adSlots=-']) ytInitialPlayerResponse_value = value } }); let ytInitialData_value = unsafeWindow['ytInitialData'] Object.defineProperty(unsafeWindow, 'ytInitialData', { get: function () { return ytInitialData_value }, set: function (value) { if (/watch/.test(href)) { obj_process(value, ['adSlotRenderer=-', 'relatedChipCommand.contents[*]=- /.compactMovieRenderer.badges[0].metadataBadgeRenderer.label~=含广告', 'secondaryResults.results[*]=- /.compactMovieRenderer.badges[0].metadataBadgeRenderer.label~=含广告'], true) ytInitialData_value = value } else { obj_process(value, [{ "value": "richSectionRenderer=-", "conditions": { "value": ['/.content.richShelfRenderer.title.runs[0].text$text~=Shorts|电影|时下流行', '/.content.inlineSurveyRenderer.title.runs[0].text$text=你对这个视频有何看法?'] } }, 'richGridRenderer.masthead=-', 'richItemRenderer=- /.content.videoRenderer.badges[0].metadataBadgeRenderer.label~=含广告', { "value": "richGridRenderer.contents[*]=-", "conditions": { "value": ['/.richItemRenderer.content.adSlotRenderer$exist', "/.richSectionRenderer.content.statementBannerRenderer.title.runs[0].text~=Premium"], } },], true) ytInitialData_value = value } } }); Object.defineProperty(navigator, 'userAgent', { get: function () { return 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36' } }) let origin_creatElement = document.createElement document.createElement.toString = origin_creatElement.toString document.createElement = function () { let node = origin_creatElement.apply(this, arguments) if (arguments[0] === 'template') { let innerHTML_ = node.innerHTML let innerhtml_getter = Object.getOwnPropertyDescriptor(Element.prototype, "innerHTML").get; let innerhtml_setter = Object.getOwnPropertyDescriptor(Element.prototype, "innerHTML").set; Object.defineProperty(node, 'innerHTML', { get: function () { return innerhtml_getter.call(node) }, set: function (value) { // console.log(value); if (value.toString().indexOf('ytd-continuation-item-renderer')>-1){ value = '' } if (value.toString().indexOf('yt-mealbar-promo-renderer') > -1) { value = '' } innerhtml_setter.call(node, value) } }) } return node } const originFetch = fetch; unsafeWindow.fetch = (uri, options) => { async function fetch_request(response) { let url = response.url return_response = response if (url.indexOf('youtubei/v1/next') > 0) { const responseClone = response.clone(); let result = await responseClone.text(); result = text_process(result, ['adSlotRenderer=-', 'appendContinuationItemsAction.continuationItems[*]=- /.compactMovieRenderer.badges[0].metadataBadgeRenderer.label~=含广告', 'itemSectionRenderer.contents[*]=- /.compactMovieRenderer.badges[0].metadataBadgeRenderer.label~=含广告'] , 'insert', true) return_response = new Response(result, response); } if (url.indexOf('youtubei/v1/player') > 0) { const responseClone = response.clone(); let result = await responseClone.text(); result = text_process(result, ["abs:playerAds=-", "abs:adPlacements=-", "abs:adBreakHeartbeatParams=-", 'abs:dSlots=-'] , 'insert', true) return_response = new Response(result, response); } if (url.indexOf('youtubei/v1/browse') > 0) { const responseClone = response.clone(); let result = await responseClone.text(); result = text_process(result, [{ "value": "richSectionRenderer=-", "conditions": { "value": ['/.content.richShelfRenderer.title.runs[0].text$text~=Shorts|电影|时下流行', '/.content.inlineSurveyRenderer.title.runs[0].text$text=你对这个视频有何看法?'] } }, 'richGridRenderer.masthead=-', 'richItemRenderer=- /.content.videoRenderer.badges[0].metadataBadgeRenderer.label~=含广告', { "value": "richGridRenderer.contents[*]=-", "conditions": { "value": ['/.richItemRenderer.content.adSlotRenderer$exist', "/.richSectionRenderer.content.statementBannerRenderer.title.runs[0].text~=Premium"], } }, 'secondaryResults.results[*]=- /.compactMovieRenderer.badges[0].metadataBadgeRenderer.label~=含广告'], 'insert', true) return_response = new Response(result, response); } return return_response } return originFetch(uri, options).then(fetch_request); } console.log('初始化完成!') } function url_observer() { if (unsafeWindow.navigation) { navigation.addEventListener('navigate', (event) => { url_change(event) }); return } const _historyWrap = function (type) { const orig = unsafeWindow.history[type]; const e = new Event(type); return function () { const rv = orig.apply(this, arguments); e.arguments = arguments; unsafeWindow.dispatchEvent(e); return rv; }; }; unsafeWindow.history.pushState = _historyWrap('pushState'); unsafeWindow.history.replaceState = _historyWrap('replaceState'); unsafeWindow.addEventListener('replaceState', function (event) { url_change() }) unsafeWindow.addEventListener('pushState', function (event) { url_change() }); unsafeWindow.addEventListener('popstate', function (event) { url_change() }) unsafeWindow.addEventListener('hashchange', function (event) { url_change() }) } function url_change(event = null) { if (event && !event.target.canGoBack) return href = event ? event.destination.url : location.href console.log('网页url改变 href -> ' + href) } function text_process(data, values, mode, traverse_all) { mode = mode || 'cover' if (mode === 'reg') { for (let value of values) { let patten_express = value.split(SPLIT_TAG)[0] let replace_value = value.split(SPLIT_TAG)[1] let patten = new RegExp(patten_express, "g") data = data.replace(patten, replace_value) } } if (mode === 'cover') { data = values[0] } if (mode === 'insert') { traverse_all = traverse_all || false let json_data = JSON.parse(data) obj_process(json_data, values, traverse_all) data = JSON.stringify(json_data) } return data } function obj_process(json_obj, express_list, traverse_all = false) { let abs_path_info_list = [] let abs_path_info_list_by_attr = [] let abs_path_info_list_by_arr = [] let relative_path_info_list = [] let relative_path_list = [] let relative_short_path_list = [] function add_data_to_abs_path(path, operator, value, condition, array_index) { let tmp path = path.replace(/\.[\d\w\-\_\$@]+/g, function (match) { return '["' + match.slice(1) + '"]' }) if (array_index !== "*") { tmp = {} path = path + (array_index ? '[' + array_index + ']' : '') tmp.path = path tmp.operator = operator tmp.value = value tmp.condition = condition if (array_index) { abs_path_info_list_by_arr.push(tmp) } else { abs_path_info_list_by_attr.push(tmp) } return } let array_length try { array_length = eval(path + '.length') if (!array_length) return } catch (error) { return } for (let tmp_index = array_length - 1; tmp_index >= 0; tmp_index--) { tmp = {} tmp.path = path + "[" + tmp_index + "]" tmp.operator = operator tmp.value = value tmp.condition = condition abs_path_info_list_by_arr.push(tmp) } } express_list.forEach(express => { let reg let express_type = typeof (express) let matchs let conditions let value reg = /^(abs:)?([a-zA-Z_0-9\.\*\[\]]*)((=\-|~=|=))(.*)?/ if (express_type === 'string') { matchs = express.match(reg) } else { matchs = express.value.match(reg) conditions = express.conditions } let abs = matchs[1] let path = matchs[2] let operator = matchs[3] if (express_type === 'string') { let tmp_value = matchs[5] || '' let split_index = tmp_value.indexOf(' ') if (split_index > -1) { value = tmp_value.substring(0, split_index) conditions = tmp_value.substring(split_index + 1) conditions = { 'value': [conditions] } } else { value = tmp_value } } matchs = path.match(/\[(\*?\d*)\]$/) let array_index if (matchs) { path = path.replace(/\[(\*?\d*)\]$/, '') array_index = matchs[1] } if (abs) { add_data_to_abs_path('json_obj.' + path, operator, value, conditions, array_index) } else { relative_path_list.push(path) let tmp_short_path = path.split('.').pop() relative_short_path_list.push(tmp_short_path) relative_path_info_list.push({ "path": path, "operator": operator, "value": value, "conditions": conditions, "array_index": array_index }) } }) if (relative_path_list.length > 0) { let dec_list = [] let dec_index_list = [] obj_property_traverse(json_obj, '', { "short_keys": relative_short_path_list, "real_keys": relative_path_list }, dec_list, dec_index_list, traverse_all) for (let i = 0; i < dec_index_list.length; i++) { let real_index = dec_index_list[i] let real_path_info = relative_path_info_list[real_index] let tmp_path = 'json_obj' + dec_list[i] add_data_to_abs_path(tmp_path, real_path_info.operator, real_path_info.value, real_path_info.conditions, real_path_info.array_index) } } abs_path_info_list = abs_path_info_list_by_attr.concat(abs_path_info_list_by_arr) for (let path_info of abs_path_info_list) { if (!obj_conditional(path_info, json_obj)) continue let operator = path_info.operator let path = path_info.path let value = path_info.value if (operator === '=-') { let math = path.match(/(.*)\[(\d+)\]$/) if (math) { let arr_express = math[1] let index = math[2] eval(arr_express + '.splice(' + index + ',1)') console.log('删除属性-->' + arr_express + '[' + index + ']', 0); } else { eval('delete ' + path) console.log('删除属性-->' + path, 0); } } if (operator === '~=') { let search_value = value.split(SPLIT_TAG)[0] let replace_value = value.split(SPLIT_TAG)[1] eval(path + '=' + path + '.replace(new RegExp(search_value, "g"), replace_value)') } if (operator === '=') { let type_ = eval('typeof (' + path + ')') if (type_ === 'number') value = Number(value) eval(path + '=value') } } } function obj_conditional(express_info, json_obj) { //json_obj 在eval里直接调用 if (!express_info['condition']) return true let condition_infos = express_info['condition'] // 与 for (let condition_list of Object.values(condition_infos)) { let result = false for (let condition of condition_list) { let reg = /^([a-zA-Z_0-9\/\.\[\]]*)?(.*)/ let match = condition.match(reg) let condition_path = match[1] let conditional_express = match[2] if (condition_path.indexOf('/') === 0) { condition_path = express_info.path + condition_path.slice(1).replace(/\.[\d\w\-\_\$@]+/g, function (match) { return '["' + match.slice(1) + '"]' }) } if (condition_path.indexOf('.') === 0) { let reg = /^\.+/ let matchs = condition_path.match(reg) // let tmp_paths = express_info.path.split('.') let positions = [] let regex = /"\]/g while ((match = regex.exec(express_info.path)) !== null) { positions.push(match.index); } if (positions.length > 0) { let split_index = positions[positions.length - matchs[0].length] + 2 let short_condition_path = condition_path.replace(reg, '') if (!/^\[/.test(short_condition_path)) { short_condition_path = '.' + short_condition_path } condition_path = express_info.path.slice(0, split_index) + short_condition_path.replace(/\.[\d\w\-\_\$@]+/g, function (match) { return '["' + match.slice(1) + '"]' }) } } let condition_value try { condition_value = eval(condition_path) } catch (error) { continue } result = value_conditional(condition_value, conditional_express) if (result) break } if (!result) return false } return true } function value_conditional(value, condition_express) { function excute_eval(express) { try { return eval(express) } catch (error) { return false } } let reg = /(\$text|\$value|\$exist)?((>=|<=|>|<|~=|=))?(.*)/ let match = condition_express.match(reg) let condition_type = match[1] || '$text' let condition_operator = match[2] let condition_test_value = match[4] if (condition_type === '$value') { if (!['>=', '<=', '>', '<', '='].includes(condition_operator)) return false if (condition_operator === '=') condition_operator = '===' return excute_eval(value + condition_operator + condition_test_value) } if (condition_type === '$exist') { return excute_eval('value !== undefined && value !== null') } if (condition_type === '$text') { if (typeof (value) === 'object') value = JSON.stringify(value) if (['>=', '<=', '>', '<'].includes(condition_operator)) { return excute_eval(value.length + condition_operator + condition_test_value.length) } if (['=', '~='].includes(condition_operator)) { return condition_operator === '=' ? value === condition_test_value : new RegExp(condition_test_value).test(value) } } return false } function obj_property_traverse(obj, cur_path, dec_infos, dec_list, dec_index_list, traverse_all = false) { if (Array.isArray(obj)) { obj.forEach((tmp_obj, index) => { let tmp_path = cur_path + '[' + index + ']' if (!tmp_obj || typeof (tmp_obj) !== 'object') return obj_property_traverse(tmp_obj, tmp_path, dec_infos, dec_list, dec_index_list, traverse_all) }) return } Object.keys(obj).forEach((key) => { let tmp_path = cur_path + '.' + key for (let i = 0; i < dec_infos["short_keys"].length; i++) { if (dec_infos["short_keys"][i] === key) { if (tmp_path.indexOf('.' + dec_infos["real_keys"][i]) > -1) { dec_list.push(tmp_path) dec_index_list.push(i) if (traverse_all && typeof (obj[key]) === 'object') { obj_property_traverse(obj[key], tmp_path, dec_infos, dec_list, dec_index_list, traverse_all) } return } break } } let value = obj[key] if (!value || typeof (value) !== 'object') return obj_property_traverse(value, tmp_path, dec_infos, dec_list, dec_index_list, traverse_all) }) }