Greasy Fork

Greasy Fork is available in English.

youtube广告拦截

拦截(疑似)所有youtube广告并且不留白哦,疑似是目前体验最好的

当前为 2023-11-19 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         youtube广告拦截
// @namespace    http://tampermonkey.net/
// @version      1.0.6
// @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()
    }, 10)
});



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'],
                    }
                }], 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)
    })
}