IYF Ad Filter

Filter ads on iyf.tv

Verzia zo dňa 08.09.2025. Pozri najnovšiu verziu.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, Greasemonkey alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, % alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, % alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey alebo Userscripts.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie, ako napríklad Tampermonkey.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie správcu používateľských skriptov.

(Už mám správcu používateľských skriptov, nechajte ma ho nainštalovať!)

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

(Už mám správcu používateľských štýlov, nechajte ma ho nainštalovať!)

// ==UserScript==
// @name         IYF Ad Filter
// @namespace    http://tampermonkey.net/
// @version      0.3.10
// @description  Filter ads on iyf.tv
// @description:zh-CN  过滤广告 on iyf.tv
// @author       Dylan Zhang
// @include      https://*.iyf.tv/*
// @include      https://*.yifan.tv/*
// @include      https://*.yfsp.tv/*
// @include      https://*.aiyifan.tv/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=iyf.tv
// @license      MIT
// @grant        GM_addStyle
// ==/UserScript==

(function() {
    'use strict';

    /* utilities */
    const utils = {
        ensureCondition(condition, maxAttempts = 600 /* 10s */, failureMessage) {
            return new Promise((resolve, reject) => {
                let attempts = 0
                const detect = () => {
                    const result = condition()
                    if (result) {
                        resolve(result)
                    } else if (attempts < maxAttempts) {
                        attempts++
                        requestAnimationFrame(detect)
                    } else {
                        reject(new Error(failureMessage))
                    }
                }
                requestAnimationFrame(detect)
            })
        },
        ensureElement(selector, maxAttempts = 600) {
            return utils.ensureCondition(
                () => document.querySelector(selector),
                maxAttempts,
                `Could not detect ${selector} after ${maxAttempts} attempts`
            )
        },

        getDeps(el, prop) {
            for(const key in el) {
                if (key.startsWith('__ngContext__')) {
                    const context = el[key]
                    for (const item of context) {
                        if (item && typeof item === 'object' && item[prop]) {
                            return [item[prop], item]
                        }
                    }
                }
            }

            return []
        },

        delegateArray(arr) {
            const delegateProto = Object.create(Array.prototype)
            delegateProto.push = function(){ return 0 }
            Object.setPrototypeOf(arr, delegateProto)
            return arr
        }
    }

    /* router */
    class Router {
        constructor() {
            this.routes = new Map()
            this.initialize()
        }
        initialize() {
            const originalPushState = history.pushState
            const originalReplaceState = history.replaceState
            const pushstateEvent = new Event('pushstate')
            const replacestateEvent = new Event('replacestate')

            // override pushState
            history.pushState = function() {
                const result = originalPushState.apply(this, arguments)
                window.dispatchEvent(pushstateEvent)
                return result
            };

            // override replaceState
            history.replaceState = function() {
                const result = originalReplaceState.apply(this, arguments)
                window.dispatchEvent(replacestateEvent)
                return result
            }

            ;['pushstate', 'replaceState', 'popstate'].forEach(eventName => {
                window.addEventListener(eventName, () => this.handle())
            })
        }
        use(path, handler) {
            const handlers = this.routes.has(path)
                ? [...this.routes.get(path), handler]
                : [handler]

            this.routes.set(path, handlers)
            return this
        }
        once(path, handler) {
            let done = false
            function fn() {
                if (done) return
                done = true
                handler.apply(this, arguments)

            }
            this.use(path, fn)
            return this
        }
        handle(pathname) {
            if (!pathname) pathname = this.getPathname()

            for(const [path, handlers] of this.routes) {
                if (
                    typeof path === 'string' && path === pathname ||
                    path instanceof RegExp && path.test(pathname)
                ) {
                    handlers.forEach(fn => fn())
                    return
                }
            }
        }
        getPathname() {
            const path = location.pathname.split('/')[1]
            return path ? `/${path}` : '/'
        }
    }

    /* shortcuts */
    class VideoShortcuts {
        constructor(vgAPI) {
            this.vgAPI = vgAPI
            this.step = 10
            this.bindEvents()
        }
        bindEvents() {
            window.addEventListener('keydown', this.handleKeyDown.bind(this))
        }
        handleKeyDown(event) {
            if (this.isTyping()) return
            if (!this.vgAPI) return

            switch(event.key) {
                case 'a': // rewind
                    this.seek(this.getCurrentTime() - this.step)
                    break
                case 'd': // fast forward
                    this.seek(this.getCurrentTime() + this.step)
                    break
                case 'f': // fullscreen
                    this.toggleFullscreen()
                    break
            }
        }

        toggleFullscreen() {
            this.isFullscreen()
                ? this.exitFullscreen()
                : this.requestFullscreen()
        }
        isFullscreen() {
            return this.vgAPI.fsAPI.isFullscreen
        }
        requestFullscreen() {
            this.vgAPI.fsAPI.request()
        }
        exitFullscreen() {
            this.vgAPI.fsAPI.exit()
        }

        seek(time) {
            this.vgAPI.currentTime = time
        }
        getCurrentTime() {
            return this.vgAPI.currentTime
        }

        isTyping() {
            const activeElement = document.activeElement
            return activeElement instanceof HTMLInputElement ||
                activeElement instanceof HTMLTextAreaElement ||
                activeElement.isContentEditable === true
        }
    }

    /* page */
    const commonStyle = `
        a:has(img[alt="广告"]),
        a[href="https://www.wyav.tv/"],
        i.vip-label {
            display: none!important;
        }

        .navbar .multi-top-buttons,
        .navbar app-dn-user-menu-item.top-item,
        .navbar .login-inner-box,
        .navbar .menu-item:has(a[href="https://www.wyav.tv/"]) {
            display: none!important;
        }
        .navbar .menu-pop.two-col {
            width: 160px!important;
            left: 0!important;
        }
        .navbar .my-card.none-user,
        .navbar .none-user-content {
            height: auto!important;
        }

        .login-frame-container .gg-dl {
            display: none!important;
        }
        .login-frame-container .login-frame-box.heighter,
        .login-frame-container .inner {
            width: auto!important;
            margin-left: 0px!important;
        }

        #sticky-block .inner {
            display: none!important;
        }
    `
    const indexStyle = `
        .sliders .sec4,
        .sliders .sec4 + .separater,

        app-index app-recommended-news:nth-of-type(2),
        app-index app-classified-top-videos:nth-of-type(1) > app-home-collection,
        app-index div:has(> app-discovery-in-home),
        app-index .new-list {
            display: none!important;
        }
    `
    const playStyle = `
        .video-player > div:last-child,

        .video-player .overlay-logo,
        .video-player vg-pause-f,
        .video-player .publicbox,
        .video-player .quanlity-items .use-coin-box {
            display: none!important;
        }

        .video-player .player-title {
            margin-left: 0!important;
        }

        .video-player + div.ps > div.bl {
            display: none!important;
        }

        .main div.playPageTop {
            min-height: 594px!important;
        }
    `
    const listStyle = `
        #filterDiv,
        .filters {
            width: 100%;
        }

        .filters +  div.ss-ctn {
            display: none!important;
        }
    `

    const router = new Router()
    router.once('/', () => {
        GM_addStyle(indexStyle)
    })

    const playRE = /^\/(play|watch)/
    let videoShortcuts = null
    router.once(playRE, () => {
        GM_addStyle(playStyle)
        // shortcuts
        videoShortcuts = new VideoShortcuts()
    })
    .use(playRE, () => {
        const { ensureElement, getDeps, delegateArray } = utils

        Promise.all([
            ensureElement('aa-videoplayer'),
            ensureElement('vg-player'),
            ensureElement('.action-pannel i')
        ]).then(([
            aaVideoPlayerEl,
            vgPlayerEl,
            danmuEl
        ]) => {
            // close danmu
            if (danmuEl.classList.contains('icondanmukai')) {
                danmuEl.click()
            }

            // remove pause ads for 20s
            const [pgmp] = getDeps(aaVideoPlayerEl, 'pgmp')
            if (pgmp) {
                pgmp.dataList = delegateArray([])
            } else {
                console.warn('pgmp not found')
            }

            // shortcuts
            const [vgAPI] = getDeps(vgPlayerEl, 'API')
            if (vgAPI) {
                videoShortcuts.vgAPI = vgAPI
            } else {
                console.warn('vgAPI not found')
            }
        })
    })

    router.once(/^\/(list|search)/, () => {
        GM_addStyle(listStyle)
    })

    function init() {
        GM_addStyle(commonStyle)
        router.handle()
    }

    init()
})();