youtube广告拦截

是拦截不是跳过也不是删除广告哦, 拦截所有youtube广告(播放页和首页)并且不留白哦,疑似是目前体验最好的

目前為 2023-11-24 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         youtube广告拦截
// @namespace    http://tampermonkey.net/
// @version      1.1.0
// @description  是拦截不是跳过也不是删除广告哦, 拦截所有youtube广告(播放页和首页)并且不留白哦,疑似是目前体验最好的
// @author       hua
// @match        https://www.youtube.com/*
// @grant        unsafeWindow
// @run-at       document-start
// @license      MIT
// ==/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) {
    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)
    })
}