Greasy Fork

Greasy Fork is available in English.

youtube广告拦截

拦截所有youtube广告,不留白,不闪屏,体验第一。已适配移动端,支持自定义拦截

目前为 2023-12-05 提交的版本。查看 最新版本

// ==UserScript==
// @name         youtube广告拦截
// @namespace    http://tampermonkey.net/
// @version      1.1.4
// @description  拦截所有youtube广告,不留白,不闪屏,体验第一。已适配移动端,支持自定义拦截
// @author       hua
// @match        https://www.youtube.com/*
// @match        https://m.youtube.com/*
// @connect      https://update.greasyfork.icu/
// @grant        unsafeWindow
// @grant        GM_xmlhttpRequest
// @grant        GM_setValue
// @grant        GM_getValue
// @run-at       document-start
// @license      MIT
// ==/UserScript==

const open_config_keyword = '2333'

let open_recommend_shorts = GM_getValue("open_recommend_shorts", false);

let open_recommend_movie = GM_getValue("open_recommend_movie", false);

let open_recommend_popular = GM_getValue("open_recommend_popular", false);

let open_recommend_liveroom = GM_getValue("open_recommend_liveroom", false);

const script_url = 'https://update.greasyfork.icu/scripts/480192/youtube%E5%B9%BF%E5%91%8A%E6%8B%A6%E6%88%AA.user.js'
let href = location.href
let home_page_ytInitialData_ad_rule
let watch_page_ytInitialData_ad_rule
let ytInitialPlayerResponse_ad_rule
let open_debugger = false
let isinint = false
let mobile_web
url_observer()
init()


function init() {
    log('初始化开始!', 0)
    config_init()
    let ytInitialPlayerResponse_value = unsafeWindow['ytInitialPlayerResponse']
    Object.defineProperty(unsafeWindow, 'ytInitialPlayerResponse', {
        get: function () {
            return ytInitialPlayerResponse_value
        },
        set: function (value) {
            let start_time = new Date().getTime()
            value && obj_process(value, ytInitialPlayerResponse_ad_rule, true)
            log('ytInitialPlayerResponse 时间:', new Date().getTime() - start_time, 1);
            ytInitialPlayerResponse_value = value
        }
    });
    let ytInitialData_value = unsafeWindow['ytInitialData']
    Object.defineProperty(unsafeWindow, 'ytInitialData', {
        get: function () {
            return ytInitialData_value
        },
        set: function (value) {
            let start_time = new Date().getTime()
            if (/watch/.test(href)) {
                value && obj_process(value, watch_page_ytInitialData_ad_rule, true)
                ytInitialData_value = value
            } else {
                value && obj_process(value, home_page_ytInitialData_ad_rule, true)
                ytInitialData_value = value
            }
            log('ytInitialData 时间:', new Date().getTime() - start_time, 1);
        }
    });
    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_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) {
                    // if (value.toString().indexOf('ytd-continuation-item-renderer')>-1){
                    //     if (href.indexOf('https://www.youtube.com/watch')>-1){
                    //         value = ''
                    //         log(value);
                    //         log('弹窗去掉------->ytd-continuation-item-renderer');
                    //     }
                    // }
                    if (value.toString().indexOf('yt-mealbar-promo-renderer') > -1) {
                        log('弹窗去掉------->yt-mealbar-promo-renderer', 1);
                        value = ''
                    }
                    innerhtml_setter.call(node, value)
                }
            })
        }
        return node
    }

    async function deal_resposn(name,response,rule){
        const responseClone = response.clone();
        let result = await responseClone.text()
        let start_time = new Date().getTime()
        result = text_process(result, rule, 'insert', true)
        log(name+' 时间:', new Date().getTime() - start_time, 1);
        return new Response(result, response)
    }
    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') > -1) {
                return await deal_resposn('next',response,watch_page_ytInitialData_ad_rule)
            }
            if (url.indexOf('youtubei/v1/player') > -1) {
                return await deal_resposn('player', response, ytInitialPlayerResponse_ad_rule)
            }
            if (url.indexOf('youtubei/v1/browse') > -1) {
                return await deal_resposn('browse', response, home_page_ytInitialData_ad_rule)
            }
            if (url.indexOf('https://m.youtube.com/youtubei/v1/guide') > -1) {
                return await deal_resposn('guide', response, home_page_ytInitialData_ad_rule)
            }
            return return_response
        }

        return originFetch(uri, options).then(fetch_request);
    }

    document.addEventListener('DOMContentLoaded', function () {
        !mobile_web && search_listener()
        checke_update()
    })

    isinint = true
    log('初始化结束!', 0)
}

function config_init() {
    let column_recommend_rule
    let item_label_fifter_rule
    let watch_page_item_label_fifter_rule
    let home_page_item_label_fifter_rule
    mobile_web = href.indexOf('https://m.youtube.com/') > -1
    ytInitialPlayerResponse_ad_rule = watch_page_ytInitialData_ad_rule = home_page_ytInitialData_ad_rule = null
    //打开直播频道
    let open_live_channel = false
    if (href.indexOf('channel/UC4R8DWoMoI7CAwX8_LjQHig') > -1) open_live_channel = true
    if (mobile_web) {
        column_recommend_rule = 'reelShelfRenderer.title.runs[0].text......=- ~='
        mobile_web_extra_column_recommend_rule = 'pivotBarItemRenderer.title.runs[0].text.....=- ~='
        // 直播规则
        let ad_label = 'metadataBadgeRenderer.label.....=- ~=赞助商广告'
        if (!open_recommend_movie) ad_label += '|免费(含广告)'
        item_label_fifter_rule = [ad_label]
        home_page_item_label_fifter_rule = watch_page_item_label_fifter_rule = item_label_fifter_rule
        if (!open_recommend_liveroom || open_live_channel) {
            item_label_fifter_rule.push('text.accessibility.accessibilityData.label........=- ~=直播')
            home_page_item_label_fifter_rule = item_label_fifter_rule
            watch_page_item_label_fifter_rule = item_label_fifter_rule
        }
    } else {
        column_recommend_rule = 'richShelfRenderer.title.runs[0].text......=- ~='
        let ad_label
        if (href.indexOf('watch') > -1) {
            ad_label = 'metadataBadgeRenderer.label.....=- ~=赞助商广告'
        } else {
            ad_label = 'metadataBadgeRenderer.label......=- ~=赞助商广告'
        }
        if (!open_recommend_movie) ad_label += '|免费(含广告)'
        item_label_fifter_rule = [ad_label]
        home_page_item_label_fifter_rule = watch_page_item_label_fifter_rule = item_label_fifter_rule
        if (!open_recommend_liveroom || open_live_channel) {
            item_label_fifter_rule.push('text.accessibility.accessibilityData.label........=- ~=直播')
            watch_page_item_label_fifter_rule = item_label_fifter_rule.concat(['metadataBadgeRenderer.label.....=- ~=直播'])
            home_page_item_label_fifter_rule = item_label_fifter_rule.concat(['metadataBadgeRenderer.label......=- ~=直播'])
        }
    }

    let column_recommend_list = []
    if (!open_recommend_shorts) column_recommend_list.push('Shorts')
    if (!open_recommend_movie) column_recommend_list.push('免费 Primetime 电影')
    if (!open_recommend_popular) column_recommend_list.push('时下流行')
    if (column_recommend_list.length > 0) {
        column_recommend_rule += column_recommend_list.join('|')
        if (mobile_web) mobile_web_extra_column_recommend_rule += column_recommend_list.join('|')
    }

    home_page_ytInitialData_ad_rule = [
        'title.runs[0].text......=- ~=YouTube Premium|你对这个视频有何看法?|此推荐内容怎么样?',
        'richGridRenderer.masthead=-',
        'videoOwnerRenderer=- /.purchaseButton.buttonRenderer.text.simpleText~=试用',
        'adSlotRenderer..=-',
    ]
    if (home_page_item_label_fifter_rule) home_page_ytInitialData_ad_rule = home_page_ytInitialData_ad_rule.concat(home_page_item_label_fifter_rule)

    if (!open_recommend_shorts || !open_recommend_movie || !open_recommend_popular) {
        home_page_ytInitialData_ad_rule.push(column_recommend_rule)
        if (mobile_web) {
            home_page_ytInitialData_ad_rule.push(mobile_web_extra_column_recommend_rule)
        }
    }

    watch_page_ytInitialData_ad_rule = [
        'tvfilmOfferModuleRenderer=- /.masthead$exist',
        'merchandiseShelfRenderer=-',
        'adSlotRenderer.=-'
    ]
    if (watch_page_item_label_fifter_rule) watch_page_ytInitialData_ad_rule = watch_page_ytInitialData_ad_rule.concat(watch_page_item_label_fifter_rule)

    ytInitialPlayerResponse_ad_rule = [
        "abs:playerAds=-",
        "abs:adPlacements=-",
        "abs:adBreakHeartbeatParams=-",
        "abs:adSlots=-",
    ]

    if (isinint) {
        setTimeout(search_listener, 500)
    }
}

function search_listener() {
    const search_selector = href.indexOf('https://m.youtube.com/') > -1 ? 'input.searchbox-input.title' : 'input[id="search"]'
    const search_input_node = document.querySelector(search_selector)
    if (search_input_node) {
        search_input_node.oninput = function (event) {
            if (open_config_keyword === this.value) {
                setTimeout(function () {
                    if (search_input_node.value === open_config_keyword) {
                        display_config_win()
                    }
                }, 500)
            }
        };
    }
}

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.destination.url.indexOf('about:blank') === 0) return
    href = event ? event.destination.url : location.href
    log('网页url改变 href -> ' + href, 0)
    config_init()
}

function log() {
    let arguments_arr = [...arguments]
    let flag = arguments_arr.pop()
    if (flag === 0 || open_debugger) console.log(...arguments_arr);
}

function display_config_win() {
    const css_str = '#set_list { z-index:9999999999; display: flex; position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); padding-top: 10px; padding-bottom: 10px; padding-left: 20px; padding-right: 20px; background-color: #fff; border: 1px solid #ccc; box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); border-radius: 10px; } #set_button { margin: 0 10px; display: inline-block; padding: 5px 10px; background-color: #3498db; color: #fff; border: none; border-radius: 5px; cursor: pointer; transition: background-color 0.3s ease; } #set_button:hover { background-color: #2980b9; }'
    const style = document.createElement("style");
    style.innerText = css_str;
    document.querySelector('body').appendChild(style)
    const config_info = {
        "open_recommend_movie": "电影推荐",
        "open_recommend_shorts": "Shorts推荐",
        "open_recommend_liveroom": "直播推荐",
        "open_recommend_popular": "时下流行",
    }
    const container = document.createElement("div");
    container.id = "set_list"
    for (let key in config_info) {
        let label = document.createElement("label")
        let input = document.createElement("input")
        input.id = key
        input.type = 'checkbox'
        input.checked = eval(key)
        label.appendChild(input)
        let span = document.createElement("span")
        span.textContent = config_info[key]
        span.style.userSelect = 'none'
        label.appendChild(span)
        container.appendChild(label)
    }
    let button = document.createElement("button")
    button.id = "set_button"
    button.textContent = '保存'
    button.onclick = function () {
        for (let key in config_info) {
            GM_setValue(key, document.querySelector('#' + key).checked);
        }
        document.querySelector('body').removeChild(container)
    }
    container.appendChild(button)
    document.querySelector('body').appendChild(container)
    let search_list_node = document.querySelector('body > div.gstl_50.sbdd_a')
    if (search_list_node) {
        search_list_node.style.display = 'none'
    }
}

function display_update_win() {
    function btn_click() {
        btn = this
        if (btn.id === 'go_btn') {
            location.href = script_url
        }
        document.querySelector('body').removeChild(container)
    }
    const css_str = "#update_tips_win { z-index:9999999999; display: flex; position: fixed; bottom: 20px; right: 20px; padding: 10px 20px; background-color: #fff; border: 1px solid #ccc; box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); border-radius: 10px; } .btn { margin: 0 10px; display: inline-block; padding: 5px 10px; background-color: #3498db; color: #fff; border: none; border-radius: 5px; cursor: pointer; transition: background-color 0.3s ease; } .btn:hover { background-color: #2980b9; }";
    const style = document.createElement("style");
    style.innerText = css_str;
    document.querySelector('body').appendChild(style)
    const container = document.createElement("div")
    container.id = "update_tips_win"
    const span = document.createElement("span")
    span.textContent = GM_info.script.name + '有更新了!!'
    container.appendChild(span)
    const go_btn = document.createElement("button")
    go_btn.textContent = 'GO'
    go_btn.id = 'go_btn'
    go_btn.className = 'btn'
    go_btn.onclick = btn_click
    container.appendChild(go_btn)
    const no_btn = document.createElement("button")
    no_btn.textContent = 'NO'
    no_btn.className = 'btn'
    no_btn.id = 'no_btn'
    no_btn.onclick = btn_click
    container.appendChild(no_btn)
    document.querySelector('body').appendChild(container)
}

function checke_update() {
    let last_check_time = GM_getValue('last_check_time', 0)
    if ((new Date().getTime() - last_check_time) < 1000 * 60 * 60 * 24) return
    GM_xmlhttpRequest({
        method: 'GET',
        url: script_url,
        onload: function (response) {
            const onlineScript = response.responseText;
            // 从线上脚本中提取版本号和元数据信息
            const onlineMeta = onlineScript.match(/@version\s+([^\s]+)/i);
            const onlineVersion = onlineMeta ? onlineMeta[1] : '';
            if (onlineVersion > GM_info.script.version) {
                display_update_win()
            }
        }
    });
    GM_setValue('last_check_time', new Date().getTime())
}

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 relative_path_info_list = []
    let relative_path_list = []
    let relative_short_path_list = []
    if (!json_obj) return
    function add_data_to_abs_path(path, relative_path, operator, value, condition, array_index, path_extral) {
        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.relative_path = relative_path
            tmp.operator = operator
            tmp.value = value
            tmp.condition = condition
            tmp.path_extral = path_extral
            abs_path_info_list.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
            tmp.path_extral = path_extral
            tmp.relative_path = relative_path
            abs_path_info_list.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 path_extral_match = path.match(/\/?\.+$/)
        let path_extral
        if (path_extral_match) {
            path_extral = {}
            let len
            if (path_extral_match[0].indexOf('/') === 0) {
                len = path_extral_match[0].length - 1
                path_extral['child'] = len
            } else {
                len = path_extral_match[0].length
                path_extral['parent'] = len
            }
            path = path.slice(0, path.length - len)
        }
        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, path, operator, value, conditions, array_index, path_extral)
        } 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,
                "path_extral": path_extral
            })
        }
    })
    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.path, real_path_info.operator, real_path_info.value, real_path_info.conditions, real_path_info.array_index, real_path_info.path_extral)
        }
    }
    abs_path_info_list.sort((a, b) => a > b ? 1 : -1)
    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
        let path_extral = path_info.path_extral
        if (path_extral) {
            let positions = []
            let regex = /\]/g
            while ((match = regex.exec(path)) !== null) {
                positions.push(match.index);
            }
            if (positions.length === 0) continue
            if ('parent' in path_extral) {
                if (positions.length - path_extral['parent'] - 1 < 0) continue
                split_index = positions[positions.length - path_extral['parent'] - 1] + 1
                path = path.slice(0, split_index)
            }
        }
        if (operator === '=-') {
            let math = path.match(/(.*)\[(\d+)\]$/)
            if (math) {
                let arr_express = math[1]
                let index = math[2]
                eval(arr_express + '.splice(' + index + ',1)')
                log(`依据:${path_info.relative_path}${!path_info.path_extral ? '' : JSON.stringify(path_info.path_extral)}${path_info.operator} ${!path_info.condition ? '' : JSON.stringify(path_info.condition)}`, 1);
                log('删除属性-->' + arr_express + '[' + index + ']', 1);
            } else {
                eval('delete ' + path)
                log(`依据:${path_info.relative_path}${!path_info.path_extral ? '' : JSON.stringify(path_info.path_extral)}${path_info.operator} ${!path_info.condition ? '' : JSON.stringify(path_info.condition)}`, 1);
                log('删除属性-->' + path, 1);
            }
        }

        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 (value.match(/\{.*\}/)) value = JSON.parse(value)
            if (typeof (value) === 'string' && value.match(/\[.*\]/)) value = JSON.parse(value)
            if (value === 'undefined') value = undefined
            if (value === 'null') value = null
            if (type_ === 'number' && !isNaN(value)) value = Number(value)
            eval(path + '=value')
            log(`依据:${path_info.relative_path}${!path_info.path_extral ? '' : JSON.stringify(path_info.path_extral)}${path_info.operator} ${!path_info.condition ? '' : JSON.stringify(path_info.condition)}`, 1);
            log('修改属性-->' + path, 1);
        }

    }

}

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 mod
            if (condition_path) {
                if (condition_path.indexOf('/') === 0) {
                    mod = 'child'
                } else if (condition_path.indexOf('.') === 0) {
                    mod = 'parent'
                } else {
                    mod = 'other'
                }
            } else {
                condition_path = express_info.path
            }
            let conditional_express = match[2]
            if (mod === 'child') {
                condition_path = express_info.path + condition_path.slice(1).replace(/\.[\d\w\-\_\$@]+/g, function (match) {
                    return '["' + match.slice(1) + '"]'
                })
            }
            if (mod === 'parent') {
                let reg = /^\.+/
                let matchs = condition_path.match(reg)
                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 - 1] + 1
                    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) + '"]'
                    })
                }
            }
            if (mod === 'other') {
                condition_path = ('json_obj.' + 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)
            result && log('条件成立-->', condition_value, 1);

            if (result) break
        }
        if (!result) return false
    }
    return true
}

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
        let deal = false
        for (let i = 0; i < dec_infos["short_keys"].length; i++) {
            if (dec_infos["short_keys"][i] === key) {
                let len = dec_infos["real_keys"][i].length
                if (tmp_path.slice(tmp_path.length - len) === dec_infos["real_keys"][i]) {
                    dec_list.push(tmp_path)
                    dec_index_list.push(i)
                    if (!deal && traverse_all && typeof (obj[key]) === 'object') {
                        obj_property_traverse(obj[key], tmp_path, dec_infos, dec_list, dec_index_list, traverse_all)
                    }
                    deal = true
                }
            }
        }
        let value = obj[key]
        if (deal || !value || typeof (value) !== 'object') return
        obj_property_traverse(value, tmp_path, dec_infos, dec_list, dec_index_list, traverse_all)
    })
}

function value_conditional(value, condition_express) {
    function excute_eval(express) {
        try {
            return eval(express)
        } catch (error) {
            return false
        }
    }

    let reg = /(\$text|\$value|\$exist|\$notexist)?((>=|<=|>|<|~=|=))?(.*)/
    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 === '$notexist') {
        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
}