IYF Ad Filter

Filter ads on iyf.tv

Per 08-09-2025. Zie de nieuwste versie.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey, Greasemonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Userscripts.

Voor het installeren van scripts heb je een extensie nodig, zoals {tampermonkey_link:Tampermonkey}.

Voor het installeren van scripts heb je een gebruikersscriptbeheerder nodig.

(Ik heb al een user script manager, laat me het downloaden!)

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

(Ik heb al een beheerder - laat me doorgaan met de installatie!)

// ==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()
})();