Greasy Fork

Greasy Fork is available in English.

M3u8

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

目前为 2024-04-03 提交的版本。查看 最新版本

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