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