Greasy Fork

Greasy Fork is available in English.

M3u8

在线播放器 和 野鸡网站 通用过滤 采集资源 插播广告

目前为 2024-05-25 提交的版本。查看 最新版本

// ==UserScript==
// @name M3u8
// @description 在线播放器 和 野鸡网站 通用过滤 采集资源 插播广告
// @version 20240526
// @author 江小白
// @include /(?:lay|ideo).*?\.html/
// @include /^https?:\/\/(?:movie\.douban\.com\/subject|m\.douban\.com\/movie)\//
// @include /\.(?:m3u8|png|css)(?:#|\?|\\|&|$)/
// @run-at document-body
// @namespace http://greasyfork.icu/users/769699
// ==/UserScript==

(function() {
    try {
        if (typeof location['m3u8去插播广告'] === 'undefined') {
            try {
                Object.defineProperty(location, 'm3u8去插播广告', {
                    value: 'm3u8去插播广告',
                    writable: false,
                    enumerable: false,
                    configurable: false
                });
            } catch (e) {}
            let urlvip, m3u8wz, mp4wz, flvwz, tswz, playsharewz, ggbmd, shouldStopExecution, 打印, ggtspd, gggzpd, ggsjgg, ggzlhx, ggljbmd, hhzz, bhhzz, m3u8gglj;
            urlvip = location.href;
            m3u8gglj = '';
            hhzz = '[\\n\\r\\u0085\\u2028\\u2029]';
            bhhzz = '[^\\n\\r\\u0085\\u2028\\u2029]';
            m3u8wz = /\.(?:m3u8|png|css)(?:#|\?|\\|&|$)|(?<!thread|forum|read)\.php(?!(?:[a-z0-9\/]|\?\w+?=.+?\.(?:m(?:p4|kv)|flv|ts)(?:#|\?|\\|&|$)))|\/(?!.+?\.m(?:3u8|p4)(?:#|\?|\\|&|$)).+?(?<![a-z0-9])m(?:3u8|p4)(?![a-z0-9])/i;
            mp4wz = /\.m(?:p4|kv)(?:#|\?|\\|&|$)|^https?:\/\/(?:[^\/]+?\.)?pstatp.+?\/obj\/[^\.]+?$|type=video_mp4&(?!.+\.[a-z]{2,5}(?:#|\?|\\|&|$))|\.php\?\w+?=.+?\.mp4/i;
            flvwz = /\.flv(?:#|\?|\\|&|$)/i;
            tswz = /\.ts(?:#|\?|\\|&|$)/i;
            playsharewz = /^https?:\/\/[^\/]+?\/{1,}(?:play|share)\/{1,}[a-zA-Z0-9]+?(?:\/{1,})?$/i;
            ggzlhx = 'ts|jpg';
            ggljbmd = /&token=/i;
            ggbmd = /\.php(?:#|\?|\\|&|$)/i;
            ggsjgg = '4|20';
            打印 = '开';
            /*以下是核心代码不懂勿动*/
            try {
                if (!shouldStopExecution) {
                    const tyad1 = '#EXTINF\\s*?:\\s*?',
                          tyad2 = '#EXT-X-DISCONTINUITY',
                          tyad3 = tyad2 + hhzz,
                          tyada = bhhzz + '+?\\.(?:' + ggzlhx + ')' + hhzz + '+',
                          tyadb = tyad1 + '\\d+(?:\\.\\d+)?\\s*?,' + hhzz + '+?',
                          tyadc = tyad3 + '+',
                          tyadd = tyadc + '?' + tyadb,
                          tyade = tyada + '?[\\s\\S]*?' + tyadc,
                          tyadf = '(?<=#EXT-X-TARGETDURATION\\s*?:\\s*?',
                          tyadg = ')(?:\\.0{1,})?\\s*?,',
                          tyadh = '(?:#EXT-X-[^:]+?:\\s*?',
                          tyad100 = ggsjgg + tyadg,
                          tyad101 = hhzz + '+?' + tyadh + bhhzz,
                          tyad102 = tyad1 + '(?:' + tyad100 + hhzz,
                          tyad103 = tyad101 + '+?' + hhzz,
                          itemsHandle = [{
                          reUrl: m3u8wz,
                          reAds: [new RegExp(tyadf + '(?:' + tyad100 + '?' + bhhzz + '+?' + tyad103 + '+?){0,})' + tyad102 + '+?' + bhhzz + '+?-' + bhhzz + '+?\\d\\.(?:' + ggzlhx + ')' + hhzz + '+(?=' + tyad1 + ')','gmi'), new RegExp(tyadf + bhhzz + '+?' + tyad103 + '+){0,})(?:' + tyad3 + '+?)?' + tyad102 + '+?[a-z\\d]+?0{4,}\\.(?:' + ggzlhx + ')' + hhzz + '+[\\s\\S]+?' + hhzz + '+[a-z\\d]+?0{2,}\\d\\.(?:' + ggzlhx + ')' + hhzz + '+?(?<![\\s\\S]+?10\\.(?:' + ggzlhx + ')\\n*?[\\s\\S]*?' + hhzz + '+)(?=(?:' + tyad3 + '+|' + tyad1 + '\\d+(?:\\.\\d+)?\\s*?,' + hhzz + '+?[a-z\\d]+?10\\.(?:' + ggzlhx + ')' + hhzz + '+))','gi'), new RegExp('(?<=^\\s*?#EXTM3U' + hhzz + '+?(?:#EXT-X-' + bhhzz + '+?' + hhzz + '+?){0,}(#EXT-X-KEY\\s*?:\\s*?' + bhhzz + '+?key\\.key' + bhhzz + '+?)' + hhzz + '[\\S\\s]+?' + hhzz + ')' + tyad2 + hhzz + '+?#EXT-X-KEY\\s*?:\\s*?METHOD=NONE' + hhzz + '+?(?:#EXTINF:\\d+?(?:\\.\\d+?)?,' + hhzz + '+?' + bhhzz + '+?\\.ts' + hhzz + '+?){0,}' + tyad2 + hhzz + '+?\\1' + hhzz + '+','gmi')],
                    }];
                    const urlFromArg = arg=>typeof arg === 'string' ? arg : arg instanceof Request ? arg.url : String(arg);
                    const matchM3u = url=>itemsHandle.find(item=>item.reUrl.test(url) && !mp4wz.test(url) && !flvwz.test(url) && !tswz.test(url) && !playsharewz.test(url) && !ggbmd.test(url));
                    const deleteAbnormalTs = (text,jxbgza,jxbgzb,jxbgzc,jxbgzd)=>{
                        if (!jxbgzd) {
                            jxbgzd = /^\s*?(?:[a-z]+?\s*?-\s*?)?\d+?\s*?$/i;
                        } else if (jxbgzd == '空') {
                            jxbgzd = /^\s*?空\s*?$/;
                        }
                        try {
                            const rgtya = '#EXTINF:\\d+?(?:\\.\\d+?)?,' + hhzz + '+?',
                                  rgtyb = jxbgza + '\\.(?:' + ggzlhx + ')' + hhzz + '+',
                                  regex = '(?<=' + rgtya + ')(' + jxbgzb + ')(?=' + rgtyb + ')',
                                  regexx = new RegExp(regex,'gi'),
                                  matches = text.match(regexx),
                                  paths = {};
                            for (let i = 0; i < matches.length; i++) {
                                const path = matches[i];
                                if (!paths[path]) {
                                    paths[path] = [];
                                }
                                paths[path].push(path);
                            }
                            let maxCount = 0, maxPath = '';
                            for (const path in paths) {
                                if (paths[path].length > maxCount) {
                                    maxCount = paths[path].length;
                                    maxPath = path;
                                }
                            }
                            for (const path in paths) {
                                if (path !== maxPath) {
                                    paths[path].forEach(p=>{
                                        if (!jxbgzd.test(p)) {
                                            text = text.replace(new RegExp(rgtya + p + rgtyb,'gi'), (match)=>{
                                                if (!/^\s*[0关]?\s*?$/i.test(打印)) {
                                                    console.log("%c[江小白-广告资源-已经发现] ✂\n%c对比" + jxbgzc + "的广告正则:\n%c" + regexx + "\n%c已经删除的广告内容:\n%c" + match, "border-left:5px solid #A0B;color:#A0B;padding:3px", "color:blue;", "color:red;", "color:blue;", "color:black;");
                                                }
                                                try {
                                                    if (!ggtspd) {
                                                        ggtspd = true;
                                                    }
                                                } catch (e) {}
                                                return '';
                                            }
                                            );
                                        }
                                    }
                                    );
                                }
                            }
                        } catch (e) {}
                        return text;
                    };
                    const pruner = (text,item)=>{
                        try {
                            if (!/^\s*#EXTM3U/i.test(text)) {
                                return text;
                            } else {
                                try {
                                    if (!gggzpd) {
                                        gggzpd = true;
                                        if (!/^\s*[0关]?\s*?$/i.test(打印)) {
                                            console.table(itemsHandle);
                                        }
                                    }
                                } catch (e) {}
                                let modifiedText;
                                try {
                                    modifiedText = deleteAbnormalTs(text, '[^\\.]+?', '(?:' + bhhzz + '+\\\/|\\b)', '路径');
                                    try {
                                        modifiedText = deleteAbnormalTs(modifiedText, '\\d+?', '\\w+?(?:[^\\d]\\d{2})?', '名称');
                                    } catch (e) {}
                                    try {
                                        modifiedText = deleteAbnormalTs(modifiedText, '\\d+?', '\\w+(?=\\d{4})', '名称', '空');
                                    } catch (e) {}
                                    try {
                                        modifiedText = deleteAbnormalTs(modifiedText, '\\d+?', '[^0]\\d+[^0]0{2,}\\d0', '名称', /(?<=[^0]0{3,})\d+$/);
                                    } catch (e) {}
                                } catch (e) {
                                    modifiedText = text;
                                }
                                for (const reAd of item.reAds) {
                                    const matches = modifiedText.match(reAd);
                                    if (matches) {
                                        matches.forEach(match=>{
                                            if (!/^\s*[0关]?\s*?$/i.test(打印)) {
                                                console.log("%c[江小白-资源广告-已经发现] ✂\n%c已经生效的广告正则:\n%c" + reAd + "\n%c已经删除的广告内容:\n%c" + match, "border-left:5px solid #A0B;color:#A0B;padding:3px", "color:blue;", "color:red;", "color:blue;", "color:black;");
                                            }
                                        }
                                        );
                                        try {
                                            if (!ggtspd) {
                                                ggtspd = true;
                                            }
                                        } catch (e) {}
                                    }
                                    modifiedText = modifiedText.replace(reAd, "");
                                }
                                if (modifiedText.length < text.length) {
                                    return modifiedText;
                                }
                                return text;
                            }
                        } catch (e) {
                            return text;
                        }
                    };
                    const realFetch = self.fetch;
                    self.fetch = new Proxy(self.fetch,{
                        apply(target, thisArg, args) {
                            const item = matchM3u(urlFromArg(args[0]));
                            if (!item) {
                                return Reflect.apply(target, thisArg, args);
                            }
                            return realFetch(...args).then(realResponse=>realResponse.text().then(text=>{
                                const modifiedText = pruner(text, item);
                                return new Response(modifiedText,{
                                    status: realResponse.status,
                                    statusText: realResponse.statusText,
                                    headers: realResponse.headers
                                });
                            }
                            ));
                        }
                    });
                    self.XMLHttpRequest.prototype.open = new Proxy(self.XMLHttpRequest.prototype.open,{
                        apply: async(target,thisArg,args)=>{
                            try {
                                if (!shouldStopExecution) {
                                    urlFromArgBy = urlFromArg(args[1]);
                                    const item = matchM3u(urlFromArgBy);
                                    if (ggljbmd.test(urlFromArgBy)) {
                                        shouldStopExecution = true;
                                    } else {
                                        m3u8gglj = urlFromArgBy;
                                    }
                                    if (item) {
                                        thisArg.addEventListener('readystatechange', async function() {
                                            if (thisArg.readyState !== 4) {
                                                return;
                                            }
                                            const type = thisArg.responseType;
                                            if (type !== '' && type !== 'text') {
                                                return;
                                            }
                                            const textin = thisArg.responseText;
                                            const textout = pruner(textin, item);
                                            if (textout !== textin) {
                                                Reflect.defineProperty(thisArg, 'response', {
                                                    value: textout
                                                });
                                                Reflect.defineProperty(thisArg, 'responseText', {
                                                    value: textout
                                                });
                                            }
                                        });
                                    }
                                    return Reflect.apply(target, thisArg, args);
                                } else {
                                    return Reflect.apply(target, thisArg, args);
                                }
                            } catch (e) {
                                return Reflect.apply(target, thisArg, args);
                            }
                        }
                    });
                }
            } catch (e) {}
        }
    } catch (e) {}
})();