// ==UserScript==
// @name M3u8
// @description 在线播放器 和 野鸡网站 通用过滤 采集资源 插播广告
// @version 20240403
// @author 江小白
// @include /^https?:\/\/(?:movie\.douban\.com\/subject|m\.douban\.com\/movie)\//
// @include /\.(?:(?:m3u8|png|css)(?:#|\?|\\|&|$)|php(?!\?\w+?=.+?\.(?:m(?:p4|kv)|flv|ts)(?:#|\?|\\|&|$)))|\/(?!.+?\.m(?:3u8|p4)(?:#|\?|\\|&|$)).+?(?<![a-z0-9])m(?:3u8|p4)(?![a-z0-9])/
// @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)(?:#|\?|\\|&|$)|php(?!\?\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",
),
],
},
];
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) {}
})();