Greasy Fork镜像 支持简体中文。

M3U8 Filter Ad Script

自用,拦截和过滤 m3u8(解析/采集资源) 的广告切片,同时打印被过滤的行信息。

目前為 2024-10-13 提交的版本,檢視 最新版本

// ==UserScript==
// @name         M3U8 Filter Ad Script
// @namespace    http://tampermonkey.net/
// @version      1.0.1
// @description  自用,拦截和过滤 m3u8(解析/采集资源) 的广告切片,同时打印被过滤的行信息。
// @author       ltxlong
// @match        *://*/*
// @grant        unsafeWindow
// @run-at       document-start
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    initHook();

    function initHook() {
        hookXHR();
    }

    let ts_name_len = 0; // ts前缀长度

    let ts_name_len_extend = 1; // 容错

    let prev_ts_name_index = -1; // ts序列号

    let ts_type = 0; // 0-xxxx000数字递增.ts模式 ;1-xxxxxxxxxx.ts模式

    function isM3U8File(url) {
        return /\.m3u8($|\?)/.test(url);
    }

    async function safelyProcessM3U8(url, content) {
        try {
            const lines = content.split('\n');
            const newLines = filterLines(lines);

            return newLines.join('\n');
        } catch (e) {
            console.error(`处理 m3u8 文件时出错: ${url}`, e);
            return content;
        }
    }

    function extractNumberBeforeTS(str) {
        // 匹配 .ts 前面的数字
        const match = str.match(/(\d+)\.ts/);

        if (match) {
            // 使用 parseInt 去掉前导 0
            return parseInt(match[1], 10);
        }

        return null; // 如果不匹配,返回 null
    }

    function filterLines(lines) {
        let result = [];

        // 先根据第一个ts名称来初始化参数
        for (let i = 0; i < lines.length; i++) {

            const line = lines[i];

            let the_ts_name_len = line.indexOf('.ts'); // ts前缀长度

            if (the_ts_name_len > 0) {

                ts_name_len = the_ts_name_len;

                let ts_name_index = extractNumberBeforeTS(line);
                if (ts_name_index === null) {
                    ts_type = 1; // ts命名模式

                    console.log('----------------------------识别ts模式1---------------------------');

                } else {
                    prev_ts_name_index = ts_name_index; // ts序列号
                    prev_ts_name_index--;
                }

                break;
            }

            if (i === lines.length - 1) {
                console.log('----------------------------识别ts模式0---------------------------');
            }

        }

        // 开始遍历过滤
        for (let i = 0; i < lines.length; i++) {

            let ts_index_check = false;

            const line = lines[i];

            if (ts_type === 0) {

                if (line.startsWith('#EXT-X-DISCONTINUITY') && lines[i + 1] && lines[i + 2]) {

                    // 检查当前行是否跟 #EXT-X-PLAYLIST-TYPE相关
                    if (i > 0 && lines[i - 1].startsWith('#EXT-X-PLAYLIST-TYPE')) {
                        result.push(line);

                        continue;
                    } else {
                        let the_ts_name_len = lines[i + 2].indexOf('.ts'); // ts前缀长度

                        if (the_ts_name_len > 0) {

                            // 根据ts名字长度过滤
                            if (the_ts_name_len - ts_name_len > ts_name_len_extend) {
                                // 广告过滤
                                if (lines[i + 3] && lines[i + 3].startsWith('#EXT-X-DISCONTINUITY')) {
                                    // 打印即将过滤的行
                                    console.log('------------------------------------------------------------------');
                                    console.log('过滤规则: #EXT-X-DISCONTINUITY-ts文件名长度-');
                                    console.log('过滤的行:', "\n", line, "\n", lines[i + 1], "\n", lines[i + 2], "\n", lines[i + 3]);
                                    console.log('------------------------------------------------------------------');

                                    i += 3;
                                } else {
                                    // 打印即将过滤的行
                                    console.log('------------------------------------------------------------------');
                                    console.log('过滤规则: #EXT-X-DISCONTINUITY-ts文件名长度');
                                    console.log('过滤的行:', "\n", line, "\n", lines[i + 1], "\n", lines[i + 2]);
                                    console.log('------------------------------------------------------------------');

                                    i += 2;
                                }

                                continue;
                            } else {
                                ts_name_len = the_ts_name_len;
                            }

                            // 根据ts序列号过滤
                            let the_ts_name_index = extractNumberBeforeTS(lines[i + 2]);

                            if (the_ts_name_index !== prev_ts_name_index + 1) {

                                // 广告过滤
                                if (lines[i + 3] && lines[i + 3].startsWith('#EXT-X-DISCONTINUITY')) {
                                    // 打印即将过滤的行
                                    console.log('------------------------------------------------------------------');
                                    console.log('过滤规则: #EXT-X-DISCONTINUITY-ts序列号-');
                                    console.log('过滤的行:', "\n", line, "\n", lines[i + 1], "\n", lines[i + 2], "\n", lines[i + 3]);
                                    console.log('------------------------------------------------------------------');

                                    i += 3;
                                } else {
                                    // 打印即将过滤的行
                                    console.log('------------------------------------------------------------------');
                                    console.log('过滤规则: #EXT-X-DISCONTINUITY-ts序列号');
                                    console.log('过滤的行:', "\n", line, "\n", lines[i + 1], "\n", lines[i + 2]);
                                    console.log('------------------------------------------------------------------');

                                    i += 2;
                                }

                                continue;
                            }
                        }
                    }
                }

                if (line.startsWith('#EXTINF') && lines[i + 1]) {

                    let the_ts_name_len = lines[i + 1].indexOf('.ts'); // ts前缀长度

                    if (the_ts_name_len > 0) {

                        // 根据ts名字长度过滤
                        if (the_ts_name_len - ts_name_len > ts_name_len_extend) {
                            // 广告过滤
                            if (lines[i + 2] && lines[i + 2].startsWith('#EXT-X-DISCONTINUITY')) {
                                // 打印即将过滤的行
                                console.log('------------------------------------------------------------------');
                                console.log('过滤规则: #EXTINF-ts文件名长度-');
                                console.log('过滤的行:', "\n", line, "\n", lines[i + 1], "\n", lines[i + 2]);
                                console.log('------------------------------------------------------------------');

                                i += 2;
                            } else {
                                // 打印即将过滤的行
                                console.log('------------------------------------------------------------------');
                                console.log('过滤规则: #EXTINF-ts文件名长度');
                                console.log('过滤的行:', "\n", line, "\n", lines[i + 1]);
                                console.log('------------------------------------------------------------------');

                                i += 1;
                            }

                            continue;
                        } else {
                            ts_name_len = the_ts_name_len;
                        }

                        // 根据ts序列号过滤
                        let the_ts_name_index = extractNumberBeforeTS(lines[i + 1]);

                        if (the_ts_name_index === prev_ts_name_index + 1) {

                            prev_ts_name_index++;

                        } else {
                            // 广告过滤
                            if (lines[i + 2].startsWith('#EXT-X-DISCONTINUITY')) {
                                // 打印即将过滤的行
                                console.log('------------------------------------------------------------------');
                                console.log('过滤规则: #EXTINF-ts序列号-');
                                console.log('过滤的行:', "\n", line, "\n", lines[i + 1], "\n", lines[i + 2]);
                                console.log('------------------------------------------------------------------');

                                i += 2;
                            } else {
                                // 打印即将过滤的行
                                console.log('------------------------------------------------------------------');
                                console.log('过滤规则: #EXTINF-ts序列号');
                                console.log('过滤的行:', "\n", line, "\n", lines[i + 1]);
                                console.log('------------------------------------------------------------------');

                                i += 1;
                            }

                            continue;
                        }
                    }
                }
            } else {

                if (line.startsWith('#EXT-X-DISCONTINUITY')) {
                    // 检查当前行是否跟 #EXT-X-PLAYLIST-TYPE相关
                    if (i > 0 && lines[i - 1].startsWith('#EXT-X-PLAYLIST-TYPE')) {
                        result.push(line);

                        continue;
                    } else {

                        // 如果第 i+2 行是 .ts 文件,跳过当前行和接下来的两行
                        if (lines[i + 2] && lines[i + 2].indexOf('.ts') > 0) {
                            // 进一步检测第 i+3 行是否也是 #EXT-X-DISCONTINUITY
                            if (lines[i + 3] && lines[i + 3].startsWith('#EXT-X-DISCONTINUITY')) {
                                // 打印即将过滤的行
                                console.log('------------------------------------------------------------------');
                                console.log('过滤规则: #EXT-X-DISCONTINUITY-广告-#EXT-X-DISCONTINUITY过滤');
                                console.log('过滤的行:', "\n", line, "\n", lines[i + 1], "\n", lines[i + 2], "\n", lines[i + 3]);
                                console.log('------------------------------------------------------------------');

                                i += 3; // 跳过当前行和接下来的三行
                            } else {
                                // 打印即将过滤的行
                                console.log('------------------------------------------------------------------');
                                console.log('过滤规则: #EXT-X-DISCONTINUITY-单个标识过滤');
                                console.log('过滤的行:', "\n", line);
                                console.log('------------------------------------------------------------------');
                            }

                            continue;
                        }
                    }
                }
            }

            // 保留不需要过滤的行
            result.push(line);
        }

        return result;
    }

    function hookXHR() {
        const OriginalXHR = unsafeWindow.XMLHttpRequest;
        unsafeWindow.XMLHttpRequest = class extends OriginalXHR {
            constructor() {
                super();

                this.addEventListener('readystatechange', async function () {
                    if (this.readyState === 4 && this.status === 200 && isM3U8File(this.responseURL)) {
                        const modifiedResponse = await safelyProcessM3U8(this.responseURL, this.responseText);
                        Object.defineProperty(this, 'responseText', { value: modifiedResponse });
                        Object.defineProperty(this, 'response', { value: modifiedResponse });
                    }
                }, false);
            }
        };
    }

})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址