M3U8 Filter Ad Script

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

当前为 2024-10-13 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name M3U8 Filter Ad Script
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.0.1
  5. // @description 自用,拦截和过滤 m3u8(解析/采集资源) 的广告切片,同时打印被过滤的行信息。
  6. // @author ltxlong
  7. // @match *://*/*
  8. // @grant unsafeWindow
  9. // @run-at document-start
  10. // @license MIT
  11. // ==/UserScript==
  12.  
  13. (function() {
  14. 'use strict';
  15.  
  16. initHook();
  17.  
  18. function initHook() {
  19. hookXHR();
  20. }
  21.  
  22. let ts_name_len = 0; // ts前缀长度
  23.  
  24. let ts_name_len_extend = 1; // 容错
  25.  
  26. let prev_ts_name_index = -1; // ts序列号
  27.  
  28. let ts_type = 0; // 0-xxxx000数字递增.ts模式 ;1-xxxxxxxxxx.ts模式
  29.  
  30. function isM3U8File(url) {
  31. return /\.m3u8($|\?)/.test(url);
  32. }
  33.  
  34. async function safelyProcessM3U8(url, content) {
  35. try {
  36. const lines = content.split('\n');
  37. const newLines = filterLines(lines);
  38.  
  39. return newLines.join('\n');
  40. } catch (e) {
  41. console.error(`处理 m3u8 文件时出错: ${url}`, e);
  42. return content;
  43. }
  44. }
  45.  
  46. function extractNumberBeforeTS(str) {
  47. // 匹配 .ts 前面的数字
  48. const match = str.match(/(\d+)\.ts/);
  49.  
  50. if (match) {
  51. // 使用 parseInt 去掉前导 0
  52. return parseInt(match[1], 10);
  53. }
  54.  
  55. return null; // 如果不匹配,返回 null
  56. }
  57.  
  58. function filterLines(lines) {
  59. let result = [];
  60.  
  61. // 先根据第一个ts名称来初始化参数
  62. for (let i = 0; i < lines.length; i++) {
  63.  
  64. const line = lines[i];
  65.  
  66. let the_ts_name_len = line.indexOf('.ts'); // ts前缀长度
  67.  
  68. if (the_ts_name_len > 0) {
  69.  
  70. ts_name_len = the_ts_name_len;
  71.  
  72. let ts_name_index = extractNumberBeforeTS(line);
  73. if (ts_name_index === null) {
  74. ts_type = 1; // ts命名模式
  75.  
  76. console.log('----------------------------识别ts模式1---------------------------');
  77.  
  78. } else {
  79. prev_ts_name_index = ts_name_index; // ts序列号
  80. prev_ts_name_index--;
  81. }
  82.  
  83. break;
  84. }
  85.  
  86. if (i === lines.length - 1) {
  87. console.log('----------------------------识别ts模式0---------------------------');
  88. }
  89.  
  90. }
  91.  
  92. // 开始遍历过滤
  93. for (let i = 0; i < lines.length; i++) {
  94.  
  95. let ts_index_check = false;
  96.  
  97. const line = lines[i];
  98.  
  99. if (ts_type === 0) {
  100.  
  101. if (line.startsWith('#EXT-X-DISCONTINUITY') && lines[i + 1] && lines[i + 2]) {
  102.  
  103. // 检查当前行是否跟 #EXT-X-PLAYLIST-TYPE相关
  104. if (i > 0 && lines[i - 1].startsWith('#EXT-X-PLAYLIST-TYPE')) {
  105. result.push(line);
  106.  
  107. continue;
  108. } else {
  109. let the_ts_name_len = lines[i + 2].indexOf('.ts'); // ts前缀长度
  110.  
  111. if (the_ts_name_len > 0) {
  112.  
  113. // 根据ts名字长度过滤
  114. if (the_ts_name_len - ts_name_len > ts_name_len_extend) {
  115. // 广告过滤
  116. if (lines[i + 3] && lines[i + 3].startsWith('#EXT-X-DISCONTINUITY')) {
  117. // 打印即将过滤的行
  118. console.log('------------------------------------------------------------------');
  119. console.log('过滤规则: #EXT-X-DISCONTINUITY-ts文件名长度-');
  120. console.log('过滤的行:', "\n", line, "\n", lines[i + 1], "\n", lines[i + 2], "\n", lines[i + 3]);
  121. console.log('------------------------------------------------------------------');
  122.  
  123. i += 3;
  124. } else {
  125. // 打印即将过滤的行
  126. console.log('------------------------------------------------------------------');
  127. console.log('过滤规则: #EXT-X-DISCONTINUITY-ts文件名长度');
  128. console.log('过滤的行:', "\n", line, "\n", lines[i + 1], "\n", lines[i + 2]);
  129. console.log('------------------------------------------------------------------');
  130.  
  131. i += 2;
  132. }
  133.  
  134. continue;
  135. } else {
  136. ts_name_len = the_ts_name_len;
  137. }
  138.  
  139. // 根据ts序列号过滤
  140. let the_ts_name_index = extractNumberBeforeTS(lines[i + 2]);
  141.  
  142. if (the_ts_name_index !== prev_ts_name_index + 1) {
  143.  
  144. // 广告过滤
  145. if (lines[i + 3] && lines[i + 3].startsWith('#EXT-X-DISCONTINUITY')) {
  146. // 打印即将过滤的行
  147. console.log('------------------------------------------------------------------');
  148. console.log('过滤规则: #EXT-X-DISCONTINUITY-ts序列号-');
  149. console.log('过滤的行:', "\n", line, "\n", lines[i + 1], "\n", lines[i + 2], "\n", lines[i + 3]);
  150. console.log('------------------------------------------------------------------');
  151.  
  152. i += 3;
  153. } else {
  154. // 打印即将过滤的行
  155. console.log('------------------------------------------------------------------');
  156. console.log('过滤规则: #EXT-X-DISCONTINUITY-ts序列号');
  157. console.log('过滤的行:', "\n", line, "\n", lines[i + 1], "\n", lines[i + 2]);
  158. console.log('------------------------------------------------------------------');
  159.  
  160. i += 2;
  161. }
  162.  
  163. continue;
  164. }
  165. }
  166. }
  167. }
  168.  
  169. if (line.startsWith('#EXTINF') && lines[i + 1]) {
  170.  
  171. let the_ts_name_len = lines[i + 1].indexOf('.ts'); // ts前缀长度
  172.  
  173. if (the_ts_name_len > 0) {
  174.  
  175. // 根据ts名字长度过滤
  176. if (the_ts_name_len - ts_name_len > ts_name_len_extend) {
  177. // 广告过滤
  178. if (lines[i + 2] && lines[i + 2].startsWith('#EXT-X-DISCONTINUITY')) {
  179. // 打印即将过滤的行
  180. console.log('------------------------------------------------------------------');
  181. console.log('过滤规则: #EXTINF-ts文件名长度-');
  182. console.log('过滤的行:', "\n", line, "\n", lines[i + 1], "\n", lines[i + 2]);
  183. console.log('------------------------------------------------------------------');
  184.  
  185. i += 2;
  186. } else {
  187. // 打印即将过滤的行
  188. console.log('------------------------------------------------------------------');
  189. console.log('过滤规则: #EXTINF-ts文件名长度');
  190. console.log('过滤的行:', "\n", line, "\n", lines[i + 1]);
  191. console.log('------------------------------------------------------------------');
  192.  
  193. i += 1;
  194. }
  195.  
  196. continue;
  197. } else {
  198. ts_name_len = the_ts_name_len;
  199. }
  200.  
  201. // 根据ts序列号过滤
  202. let the_ts_name_index = extractNumberBeforeTS(lines[i + 1]);
  203.  
  204. if (the_ts_name_index === prev_ts_name_index + 1) {
  205.  
  206. prev_ts_name_index++;
  207.  
  208. } else {
  209. // 广告过滤
  210. if (lines[i + 2].startsWith('#EXT-X-DISCONTINUITY')) {
  211. // 打印即将过滤的行
  212. console.log('------------------------------------------------------------------');
  213. console.log('过滤规则: #EXTINF-ts序列号-');
  214. console.log('过滤的行:', "\n", line, "\n", lines[i + 1], "\n", lines[i + 2]);
  215. console.log('------------------------------------------------------------------');
  216.  
  217. i += 2;
  218. } else {
  219. // 打印即将过滤的行
  220. console.log('------------------------------------------------------------------');
  221. console.log('过滤规则: #EXTINF-ts序列号');
  222. console.log('过滤的行:', "\n", line, "\n", lines[i + 1]);
  223. console.log('------------------------------------------------------------------');
  224.  
  225. i += 1;
  226. }
  227.  
  228. continue;
  229. }
  230. }
  231. }
  232. } else {
  233.  
  234. if (line.startsWith('#EXT-X-DISCONTINUITY')) {
  235. // 检查当前行是否跟 #EXT-X-PLAYLIST-TYPE相关
  236. if (i > 0 && lines[i - 1].startsWith('#EXT-X-PLAYLIST-TYPE')) {
  237. result.push(line);
  238.  
  239. continue;
  240. } else {
  241.  
  242. // 如果第 i+2 行是 .ts 文件,跳过当前行和接下来的两行
  243. if (lines[i + 2] && lines[i + 2].indexOf('.ts') > 0) {
  244. // 进一步检测第 i+3 行是否也是 #EXT-X-DISCONTINUITY
  245. if (lines[i + 3] && lines[i + 3].startsWith('#EXT-X-DISCONTINUITY')) {
  246. // 打印即将过滤的行
  247. console.log('------------------------------------------------------------------');
  248. console.log('过滤规则: #EXT-X-DISCONTINUITY-广告-#EXT-X-DISCONTINUITY过滤');
  249. console.log('过滤的行:', "\n", line, "\n", lines[i + 1], "\n", lines[i + 2], "\n", lines[i + 3]);
  250. console.log('------------------------------------------------------------------');
  251.  
  252. i += 3; // 跳过当前行和接下来的三行
  253. } else {
  254. // 打印即将过滤的行
  255. console.log('------------------------------------------------------------------');
  256. console.log('过滤规则: #EXT-X-DISCONTINUITY-单个标识过滤');
  257. console.log('过滤的行:', "\n", line);
  258. console.log('------------------------------------------------------------------');
  259. }
  260.  
  261. continue;
  262. }
  263. }
  264. }
  265. }
  266.  
  267. // 保留不需要过滤的行
  268. result.push(line);
  269. }
  270.  
  271. return result;
  272. }
  273.  
  274. function hookXHR() {
  275. const OriginalXHR = unsafeWindow.XMLHttpRequest;
  276. unsafeWindow.XMLHttpRequest = class extends OriginalXHR {
  277. constructor() {
  278. super();
  279.  
  280. this.addEventListener('readystatechange', async function () {
  281. if (this.readyState === 4 && this.status === 200 && isM3U8File(this.responseURL)) {
  282. const modifiedResponse = await safelyProcessM3U8(this.responseURL, this.responseText);
  283. Object.defineProperty(this, 'responseText', { value: modifiedResponse });
  284. Object.defineProperty(this, 'response', { value: modifiedResponse });
  285. }
  286. }, false);
  287. }
  288. };
  289. }
  290.  
  291. })();

QingJ © 2025

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