IYF Ad Filter

Filter ads on iyf.tv

24.10.2025 itibariyledir. En son verisyonu görün.

Bu betiği kurabilmeniz için Tampermonkey, Greasemonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği kurabilmeniz için Tampermonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği kurabilmeniz için Tampermonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği kurabilmeniz için Tampermonkey ya da Userscripts gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

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

Bu komut dosyasını yüklemek için bir kullanıcı komut dosyası yöneticisi uzantısı yüklemeniz gerekecek.

(Zaten bir kullanıcı komut dosyası yöneticim var, kurmama izin verin!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(Zateb bir user-style yöneticim var, yükleyeyim!)

// ==UserScript==
// @name         IYF Ad Filter
// @namespace    https://github.com/dzist
// @version      0.3.16
// @description  Filter ads on iyf.tv
// @description:zh-CN  过滤广告 on iyf.tv
// @author       Dylan Zhang
// @include      *://*.iyf.tv/*
// @include      *://*.yifan.tv/*
// @include      *://*.yfsp.tv/*
// @include      *://*.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.seekStep = 5
            this.volumeStep = 0.1
            this.bindEvents()
        }

        bindEvents() {
            window.addEventListener('keydown', this.handleKeyDown)
            window.addEventListener('beforeunload', this.handleBeforeUnload)
        }
        handleKeyDown = (event) => {
            if (this.isTyping) return
            if (!this.vgAPI) return

            switch(event.key) {
                case '1':
                case '2':
                case '3':
                case '4':
                case '5':
                    this.setPlaybackRate(parseInt(event.key))
                    break
                case 'w':
                    this.setVolume(Math.min(this.currentVolume + this.volumeStep, 1))
                    break
                case 's':
                    this.setVolume(Math.max(this.currentVolume - this.volumeStep, 0))
                    break
                case 'a': // rewind
                    this.seek(this.currentTime - this.seekStep)
                    break
                case 'd': // fast forward
                    this.seek(this.currentTime + this.seekStep)
                    break
                case 'f': // fullscreen
                    this.toggleFullscreen()
                    break
            }
        }
        handleBeforeUnload = () => {
            window.removeEventListener('keydown', this.handleKeyDown)
            window.removeEventListener('beforeunload', this.handleBeforeUnload)
            this.vgAPI = null
        }

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

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

        get currentVolume() {
            return this.vgAPI.volume
        }
        setVolume(volume) {
            this.vgAPI.volume = volume
        }

        setPlaybackRate(rate) {
            this.vgAPI.playbackRate = rate
        }

        get 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 vg-pause-f,
        .video-player .overlay-logo,
        .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()
})();