// ==UserScript==
// @name youtube广告拦截
// @namespace http://tampermonkey.net/
// @version 1.0.8
// @description 是拦截不是跳过也不是删除广告哦, 拦截所有youtube广告(播放页和首页)并且不留白哦,疑似是目前体验最好的
// @author hua
// @match https://www.youtube.com/*
// @grant unsafeWindow
// @run-at document-start
// @license MIT
// ==/UserScript==
//移除首页出现的短视频推荐栏 设置为true则移除 (无效)
// let remove_short_video = true
// // 移除首页出现的电影推荐栏 免费 Primetime 电影 设置为true则移除 (无效)
// let remove_movie_recommend_tab = true
// let remove_movie_recommend = true
let href
init()
navigation.addEventListener('navigate', () => {
setTimeout(() => {
url_change()
}, 5)
});
function init() {
href = location.href
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~=含广告'], 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=你对这个视频有何看法?']
}
}, 'masthead.adSlotRenderer=-',
{
"value": "richGridRenderer.contents[*]=-",
"conditions": {
"value": ['/.richItemRenderer.content.videoRenderer.badges[0].metadataBadgeRenderer.label~=含广告', '/.richItemRenderer.content.adSlotRenderer$exist',"/.richSectionRenderer.content.statementBannerRenderer.title.runs[0].text~=歡迎試用|欢迎试用"],
}
}], true)
ytInitialData_value = value
}
}
});
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, ["richSectionRenderer=- /.content.richShelfRenderer.title.runs[0].text$text~=Shorts|电影|时下流行", {
'value': 'richItemRenderer=-',
'conditions': {
'value': ['/.content.videoRenderer.badges[0].metadataBadgeRenderer.label$text~=含广告', '/.content.adSlotRenderer$exist'],
}
}]
, 'insert', true)
return_response = new Response(result, response);
}
return return_response
}
return originFetch(uri, options).then(fetch_request);
}
console.log('初始化完成!')
}
function url_change() {
if (location.href !== href) {
console.log('网页url改变 href -> ' + location.href)
href = location.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
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)
}
if (condition_path.indexOf('.') === 0) {
let reg = /^\.+/
let matchs = condition_path.match(reg)
let tmp_paths = express_info.path.split('.')
tmp_paths.length = tmp_paths.length - matchs[0].length
condition_path = tmp_paths.join('.') + '.' + condition_path.replace(reg, '')
}
let condition_value
try {
condition_value = eval(condition_path)
} catch (error) {
continue
}
// if (typeof (condition_value) === 'object') condition_value = JSON.stringify(condition_value)
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)
})
}