M3U8 Filter Ad Script

自用,拦截和过滤 m3u8(解析/采集资源) 的切片(插播)广告,同时在console打印过滤的行信息,不会误删。

  1. // ==UserScript==
  2. // @name M3U8 Filter Ad Script
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.3.2
  5. // @description 自用,拦截和过滤 m3u8(解析/采集资源) 的切片(插播)广告,同时在console打印过滤的行信息,不会误删。
  6. // @author ltxlong
  7. // @match *://*/*
  8. // @run-at document-start
  9. // @grant unsafeWindow
  10. // @grant GM_getResourceText
  11. // @grant GM_registerMenuCommand
  12. // @grant GM_unregisterMenuCommand
  13. // @grant GM_setValue
  14. // @grant GM_getValue
  15. // @require https://unpkg.com/sweetalert2@11/dist/sweetalert2.min.js
  16. // @resource Swal https://unpkg.com/sweetalert2@11/dist/sweetalert2.min.css
  17. // @resource SwalDark https://unpkg.com/@sweetalert2/theme-dark@5/dark.min.css
  18. // @license MIT
  19. // ==/UserScript==
  20.  
  21. (function() {
  22. 'use strict';
  23.  
  24. let ts_name_len = 0; // ts前缀长度
  25.  
  26. let ts_name_len_extend = 1; // 容错
  27.  
  28. let first_extinf_row = '';
  29.  
  30. let the_extinf_judge_row_n = 0;
  31.  
  32. let the_same_extinf_name_n = 0;
  33.  
  34. let the_extinf_benchmark_n = 5; // 基准
  35.  
  36. let prev_ts_name_index = -1; // 上个ts序列号
  37.  
  38. let first_ts_name_index = -1; // 首个ts序列号
  39.  
  40. let ts_type = 0; // 0:xxxx000数字递增.ts模式0 ;1:xxxxxxxxxx.ts模式1 ;2:***.ts模式2-暴力拆解
  41.  
  42. let the_ext_x_mode = 0; // 0:ext_x_discontinuity判断模式0 ;1:ext_x_discontinuity判断模式1
  43.  
  44. let the_current_host = unsafeWindow.location.hostname;
  45.  
  46. let script_whitelist_mode_flag = false; // 是否启用白名单模式,默认否,默认是匹配所有的网站
  47.  
  48. let the_current_host_in_whitelist_flag = false; // 当前域名是否在白名单,默认否
  49.  
  50. let show_toast_tip_flag = false; // 是否启用弹窗提示,默认否
  51.  
  52. let violent_filter_mode_flag = false; // 是否启用暴力拆解模式,默认否-自动判断模式
  53.  
  54. let filter_log_html = '';
  55.  
  56. let filter_done_flag = false;
  57.  
  58. function filter_log(...msg) {
  59.  
  60. const log_content = msg.join('</p><p>');
  61.  
  62. console.log('%c[m3u8_filter_ad]', 'font-weight: bold; color: white; background-color: #70b566b0; padding: 2px; border-radius: 2px;', ...msg);
  63.  
  64. filter_log_html += `
  65. <p>
  66. <span style="font-weight: bold; color: white; background-color: #70b566b0; padding: 2px; border-radius: 2px;">[m3u8_filter_ad]</span>
  67. ${log_content}
  68. </p>
  69. `;
  70.  
  71. }
  72.  
  73. let the_swalcss_color = "#ff679a";
  74.  
  75. let swalcss = `
  76. .swal2-styled{transition: all 0.2s ease;}
  77. .swal2-loader{display:none;align-items:center;justify-content:center;width:2.2em;height:2.2em;margin:0 1.875em;-webkit-animation:swal2-rotate-loading 1.5s linear 0s infinite normal;animation:swal2-rotate-loading 1.5s linear 0s infinite normal;border-width:.25em;border-style:solid;border-radius:100%;border-color:${the_swalcss_color} transparent }
  78. .swal2-styled.swal2-confirm{border:0;border-radius:.25em;background:initial;background-color:${the_swalcss_color};color:#fff;font-size:1em}
  79. .swal2-styled.swal2-confirm:hover,.swal2-styled.swal2-deny:hover{opacity:0.8;background-image:none!important}
  80. .swal2-styled.swal2-confirm:focus{box-shadow:0 0 0 3px ${the_swalcss_color}80}
  81. .swal2-styled.swal2-deny:focus{box-shadow:0 0 0 3px #dc374180}
  82. .swal2-timer-progress-bar-container{position:absolute;right:0;bottom:0;left:0;grid-column:auto;overflow:hidden;border-bottom-right-radius:5px;border-bottom-left-radius:5px}
  83. .swal2-timer-progress-bar{width:100%;height:.25em;background:${the_swalcss_color}33 }
  84. .swal2-progress-steps .swal2-progress-step{z-index:20;flex-shrink:0;width:2em;height:2em;border-radius:2em;background:${the_swalcss_color};color:#fff;line-height:2em;text-align:center}
  85. .swal2-progress-steps .swal2-progress-step.swal2-active-progress-step{background:${the_swalcss_color} }
  86. .swal2-progress-steps .swal2-progress-step-line{z-index:10;flex-shrink:0;width:2.5em;height:.4em;margin:0 -1px;background:${the_swalcss_color}}
  87. .swal2-popup {padding:1.25em 0 1.25em;flex-direction:column}
  88. .swal2-close {position:absolute;top:1px;right:1px;transition: all 0.2s ease;}
  89. div:where(.swal2-container) .swal2-html-container{padding: 1.3em 1.3em 0.3em;}
  90. div:where(.swal2-container) button:where(.swal2-close):hover {color:${the_swalcss_color}!important;font-size:60px!important}
  91. div:where(.swal2-icon) .swal2-icon-content {font-family: sans-serif;}
  92. .swal2-container {z-index: 1145141919810;}
  93. `;
  94.  
  95. // 动态添加样式
  96. function add_style(id, css) {
  97.  
  98. let try_add_style_n = 0;
  99.  
  100. let try_to_add_style = function() {
  101. let the_style_dom = unsafeWindow.document.getElementById(id);
  102. if (the_style_dom) the_style_dom.remove();
  103.  
  104. let the_style = unsafeWindow.document.createElement('style');
  105. the_style.rel = 'stylesheet';
  106. the_style.id = id;
  107. the_style.innerHTML = css;
  108.  
  109. let the_target_element = unsafeWindow.document.body;
  110. if (the_target_element) {
  111. the_target_element.insertBefore(the_style, the_target_element.firstChild);
  112. } else {
  113. try_add_style_n++;
  114. if (try_add_style_n < 50) {
  115. setTimeout(try_to_add_style, 100);
  116. }
  117. }
  118. };
  119.  
  120. try_to_add_style();
  121. }
  122.  
  123. // 先监听颜色方案变化
  124. unsafeWindow.matchMedia('(prefers-color-scheme: dark)').addListener(function (e) {
  125. if (e.matches) {
  126. // 切换到暗色主题
  127. add_style('swal-pub-style', GM_getResourceText('SwalDark'));
  128. } else {
  129. // 切换到浅色主题
  130. add_style('swal-pub-style', GM_getResourceText('Swal'));
  131. }
  132.  
  133. add_style('Panlinker-SweetAlert2-User', swalcss);
  134. });
  135.  
  136. // 再修改主题
  137. if (unsafeWindow.matchMedia && unsafeWindow.matchMedia('(prefers-color-scheme: dark)').matches) {
  138. // 切换到暗色主题
  139. add_style('swal-pub-style', GM_getResourceText('SwalDark'));
  140. } else {
  141. // 切换到浅色主题
  142. add_style('swal-pub-style', GM_getResourceText('Swal'));
  143. }
  144.  
  145. add_style('Panlinker-SweetAlert2-User', swalcss);
  146.  
  147. // Toast 提示配置
  148. let toast = Swal.mixin({
  149. toast: true,
  150. position: 'top-end',
  151. showConfirmButton: false,
  152. timer: 3000,
  153. timerProgressBar: true,
  154. showCloseButton: true,
  155. didOpen: function (toast) {
  156. toast.addEventListener('mouseenter', Swal.stopTimer);
  157. toast.addEventListener('mouseleave', Swal.resumeTimer);
  158. }
  159. });
  160.  
  161. // Toast 简易调用
  162. let message = {
  163. success: function (text) {
  164. toast.fire({ title: text, icon: 'success' });
  165. },
  166. error: function (text) {
  167. toast.fire({ title: text, icon: 'error' });
  168. },
  169. warning: function (text) {
  170. toast.fire({ title: text, icon: 'warning' });
  171. },
  172. info: function (text) {
  173. toast.fire({ title: text, icon: 'info' });
  174. },
  175. question: function (text) {
  176. toast.fire({ title: text, icon: 'question' });
  177. }
  178. };
  179.  
  180. function is_m3u8_file(url) {
  181. return /\.m3u8($|\?)/.test(url);
  182. }
  183.  
  184. function extract_number_before_ts(str) {
  185. // 匹配 .ts 前面的数字
  186. const match = str.match(/(\d+)\.ts/);
  187.  
  188. if (match) {
  189. // 使用 parseInt 去掉前导 0
  190. return parseInt(match[1], 10);
  191. }
  192.  
  193. return null; // 如果不匹配,返回 null
  194. }
  195.  
  196. function filter_lines(lines) {
  197. let result = [];
  198.  
  199. if (violent_filter_mode_flag) {
  200. filter_log('----------------------------暴力拆解模式--------------------------');
  201.  
  202. ts_type = 2; // ts命名模式
  203. } else {
  204. filter_log('----------------------------自动判断模式--------------------------');
  205.  
  206. let the_normal_int_ts_n = 0;
  207. let the_diff_int_ts_n = 0;
  208.  
  209. let last_ts_name_len = 0;
  210.  
  211. // 初始化参数
  212. for (let i = 0; i < lines.length; i++) {
  213.  
  214. const line = lines[i];
  215.  
  216. // 初始化first_extinf_row
  217. if (the_extinf_judge_row_n === 0 && line.startsWith('#EXTINF')) {
  218. first_extinf_row = line;
  219.  
  220. the_extinf_judge_row_n++;
  221. } else if (the_extinf_judge_row_n === 1 && line.startsWith('#EXTINF')) {
  222. if (line !== first_extinf_row) {
  223. first_extinf_row = '';
  224. }
  225.  
  226. the_extinf_judge_row_n++;
  227. }
  228.  
  229. // 判断ts模式
  230. let the_ts_name_len = line.indexOf('.ts'); // ts前缀长度
  231.  
  232. if (the_ts_name_len > 0) {
  233.  
  234. if (the_extinf_judge_row_n === 1) {
  235. ts_name_len = the_ts_name_len;
  236. }
  237.  
  238. last_ts_name_len = the_ts_name_len;
  239.  
  240. let ts_name_index = extract_number_before_ts(line);
  241. if (ts_name_index === null) {
  242. if (the_extinf_judge_row_n === 1) {
  243. ts_type = 1; // ts命名模式
  244. } else if (the_extinf_judge_row_n === 2 && (ts_type === 1 || the_ts_name_len === ts_name_len)) {
  245. ts_type = 1; // ts命名模式
  246.  
  247. filter_log('----------------------------识别ts模式1---------------------------');
  248.  
  249. break;
  250. } else {
  251. the_diff_int_ts_n++;
  252. }
  253. } else {
  254.  
  255. // 如果序号相隔等于1: 模式0
  256. // 如果序号相隔大于1,或其他:模式2(暴力拆解)
  257.  
  258. if (the_normal_int_ts_n === 0) {
  259. // 初始化ts序列号
  260. prev_ts_name_index = ts_name_index;
  261. first_ts_name_index = ts_name_index;
  262. prev_ts_name_index = first_ts_name_index - 1;
  263. }
  264.  
  265. if (the_ts_name_len !== ts_name_len) {
  266.  
  267. if (the_ts_name_len === last_ts_name_len + 1 && ts_name_index === prev_ts_name_index + 1) {
  268.  
  269. if (the_diff_int_ts_n) {
  270.  
  271. if (ts_name_index === prev_ts_name_index + 1) {
  272. ts_type = 0; // ts命名模式
  273. prev_ts_name_index = first_ts_name_index - 1;
  274.  
  275. filter_log('----------------------------识别ts模式0---------------------------')
  276.  
  277. break;
  278. } else {
  279. ts_type = 2; // ts命名模式
  280.  
  281. filter_log('----------------------------识别ts模式2---------------------------')
  282.  
  283. break;
  284. }
  285. }
  286.  
  287. the_normal_int_ts_n++;
  288. prev_ts_name_index = ts_name_index;
  289.  
  290. } else {
  291. the_diff_int_ts_n++;
  292. }
  293. } else {
  294.  
  295. if (the_diff_int_ts_n) {
  296.  
  297. if (ts_name_index === prev_ts_name_index + 1) {
  298. ts_type = 0; // ts命名模式
  299. prev_ts_name_index = first_ts_name_index - 1;
  300.  
  301. filter_log('----------------------------识别ts模式0---------------------------')
  302.  
  303. break;
  304. } else {
  305. ts_type = 2; // ts命名模式
  306.  
  307. filter_log('----------------------------识别ts模式2---------------------------')
  308.  
  309. break;
  310. }
  311. }
  312.  
  313. the_normal_int_ts_n++;
  314. prev_ts_name_index = ts_name_index;
  315. }
  316. }
  317. }
  318.  
  319. if (i === lines.length - 1) {
  320. // 后缀不是ts,而是jpeg等等,或者以上规则判断不了的,或者没有广告切片的:直接暴力拆解过滤
  321.  
  322. ts_type = 2; // ts命名模式
  323.  
  324. filter_log('----------------------------进入暴力拆解模式---------------------------')
  325. }
  326. }
  327. }
  328.  
  329. // 开始遍历过滤
  330. for (let i = 0; i < lines.length; i++) {
  331.  
  332. let ts_index_check = false;
  333.  
  334. const line = lines[i];
  335.  
  336. if (ts_type === 0) {
  337.  
  338. if (line.startsWith('#EXT-X-DISCONTINUITY') && lines[i + 1] && lines[i + 2]) {
  339.  
  340. // 检查当前行是否跟 #EXT-X-相关
  341. if (i > 0 && lines[i - 1].startsWith('#EXT-X-')) {
  342. result.push(line);
  343.  
  344. continue;
  345. } else {
  346. let the_ts_name_len = lines[i + 2].indexOf('.ts'); // ts前缀长度
  347.  
  348. if (the_ts_name_len > 0) {
  349.  
  350. // 根据ts名字长度过滤
  351. if (the_ts_name_len - ts_name_len > ts_name_len_extend) {
  352. // 广告过滤
  353. if (lines[i + 3] && lines[i + 3].startsWith('#EXT-X-DISCONTINUITY')) {
  354. // 打印即将过滤的行
  355. filter_log('过滤规则: #EXT-X-DISCONTINUITY-ts文件名长度-');
  356. filter_log('过滤的行:', "\n", line, "\n", lines[i + 1], "\n", lines[i + 2], "\n", lines[i + 3]);
  357. filter_log('------------------------------------------------------------------');
  358.  
  359. i += 3;
  360. } else {
  361. // 打印即将过滤的行
  362. filter_log('过滤规则: #EXT-X-DISCONTINUITY-ts文件名长度');
  363. filter_log('过滤的行:', "\n", line, "\n", lines[i + 1], "\n", lines[i + 2]);
  364. filter_log('------------------------------------------------------------------');
  365.  
  366. i += 2;
  367. }
  368.  
  369. continue;
  370. } else {
  371. ts_name_len = the_ts_name_len;
  372. }
  373.  
  374. // 根据ts序列号过滤
  375. let the_ts_name_index = extract_number_before_ts(lines[i + 2]);
  376.  
  377. if (the_ts_name_index !== prev_ts_name_index + 1) {
  378.  
  379. // 广告过滤
  380. if (lines[i + 3] && lines[i + 3].startsWith('#EXT-X-DISCONTINUITY')) {
  381. // 打印即将过滤的行
  382. filter_log('过滤规则: #EXT-X-DISCONTINUITY-ts序列号-');
  383. filter_log('过滤的行:', "\n", line, "\n", lines[i + 1], "\n", lines[i + 2], "\n", lines[i + 3]);
  384. filter_log('------------------------------------------------------------------');
  385.  
  386. i += 3;
  387. } else {
  388. // 打印即将过滤的行
  389. filter_log('过滤规则: #EXT-X-DISCONTINUITY-ts序列号');
  390. filter_log('过滤的行:', "\n", line, "\n", lines[i + 1], "\n", lines[i + 2]);
  391. filter_log('------------------------------------------------------------------');
  392.  
  393. i += 2;
  394. }
  395.  
  396. continue;
  397. }
  398. }
  399. }
  400. }
  401.  
  402. if (line.startsWith('#EXTINF') && lines[i + 1]) {
  403.  
  404. let the_ts_name_len = lines[i + 1].indexOf('.ts'); // ts前缀长度
  405.  
  406. if (the_ts_name_len > 0) {
  407.  
  408. // 根据ts名字长度过滤
  409. if (the_ts_name_len - ts_name_len > ts_name_len_extend) {
  410. // 广告过滤
  411. if (lines[i + 2] && lines[i + 2].startsWith('#EXT-X-DISCONTINUITY')) {
  412. // 打印即将过滤的行
  413. filter_log('过滤规则: #EXTINF-ts文件名长度-');
  414. filter_log('过滤的行:', "\n", line, "\n", lines[i + 1], "\n", lines[i + 2]);
  415. filter_log('------------------------------------------------------------------');
  416.  
  417. i += 2;
  418. } else {
  419. // 打印即将过滤的行
  420. filter_log('过滤规则: #EXTINF-ts文件名长度');
  421. filter_log('过滤的行:', "\n", line, "\n", lines[i + 1]);
  422. filter_log('------------------------------------------------------------------');
  423.  
  424. i += 1;
  425. }
  426.  
  427. continue;
  428. } else {
  429. ts_name_len = the_ts_name_len;
  430. }
  431.  
  432. // 根据ts序列号过滤
  433. let the_ts_name_index = extract_number_before_ts(lines[i + 1]);
  434.  
  435. if (the_ts_name_index === prev_ts_name_index + 1) {
  436.  
  437. prev_ts_name_index++;
  438.  
  439. } else {
  440. // 广告过滤
  441. if (lines[i + 2].startsWith('#EXT-X-DISCONTINUITY')) {
  442. // 打印即将过滤的行
  443. filter_log('过滤规则: #EXTINF-ts序列号-');
  444. filter_log('过滤的行:', "\n", line, "\n", lines[i + 1], "\n", lines[i + 2]);
  445. filter_log('------------------------------------------------------------------');
  446.  
  447. i += 2;
  448. } else {
  449. // 打印即将过滤的行
  450. filter_log('过滤规则: #EXTINF-ts序列号');
  451. filter_log('过滤的行:', "\n", line, "\n", lines[i + 1]);
  452. filter_log('------------------------------------------------------------------');
  453.  
  454. i += 1;
  455. }
  456.  
  457. continue;
  458. }
  459. }
  460. }
  461. } else if (ts_type === 1) {
  462.  
  463. if (line.startsWith('#EXTINF')) {
  464. if (line === first_extinf_row && the_same_extinf_name_n <= the_extinf_benchmark_n && the_ext_x_mode === 0) {
  465. the_same_extinf_name_n++;
  466. } else {
  467. the_ext_x_mode = 1;
  468. }
  469.  
  470. if (the_same_extinf_name_n > the_extinf_benchmark_n) {
  471. the_ext_x_mode = 1;
  472. }
  473. }
  474.  
  475. if (line.startsWith('#EXT-X-DISCONTINUITY')) {
  476. // 检查当前行是否跟 #EXT-X-PLAYLIST-TYPE相关
  477. if (i > 0 && lines[i - 1].startsWith('#EXT-X-PLAYLIST-TYPE')) {
  478. result.push(line);
  479.  
  480. continue;
  481. } else {
  482.  
  483. // 如果第 i+2 行是 .ts 文件,跳过当前行和接下来的两行
  484. if (lines[i + 1] && lines[i + 1].startsWith('#EXTINF') && lines[i + 2] && lines[i + 2].indexOf('.ts') > 0) {
  485.  
  486. let the_ext_x_discontinuity_condition_flag = false;
  487.  
  488. if (the_ext_x_mode === 1) {
  489. the_ext_x_discontinuity_condition_flag = lines[i + 1] !== first_extinf_row && the_same_extinf_name_n > the_extinf_benchmark_n;
  490. }
  491.  
  492. // 进一步检测第 i+3 行是否也是 #EXT-X-DISCONTINUITY
  493. if (lines[i + 3] && lines[i + 3].startsWith('#EXT-X-DISCONTINUITY') && the_ext_x_discontinuity_condition_flag) {
  494. // 打印即将过滤的行
  495. filter_log('过滤规则: #EXT-X-DISCONTINUITY-广告-#EXT-X-DISCONTINUITY过滤');
  496. filter_log('过滤的行:', "\n", line, "\n", lines[i + 1], "\n", lines[i + 2], "\n", lines[i + 3]);
  497. filter_log('------------------------------------------------------------------');
  498.  
  499. i += 3; // 跳过当前行和接下来的三行
  500. } else {
  501. // 打印即将过滤的行
  502. filter_log('过滤规则: #EXT-X-DISCONTINUITY-单个标识过滤');
  503. filter_log('过滤的行:', "\n", line);
  504. filter_log('------------------------------------------------------------------');
  505. }
  506.  
  507. continue;
  508. }
  509. }
  510. }
  511. } else {
  512.  
  513. // 暴力拆解
  514. if (line.startsWith('#EXT-X-DISCONTINUITY')) {
  515. // 检查当前行是否跟 #EXT-X-PLAYLIST-TYPE相关
  516. if (i > 0 && lines[i - 1].startsWith('#EXT-X-PLAYLIST-TYPE')) {
  517. result.push(line);
  518.  
  519. continue;
  520. } else {
  521.  
  522. // 打印即将过滤的行
  523. filter_log('过滤规则: #EXT-X-DISCONTINUITY-单个标识过滤');
  524. filter_log('过滤的行:', "\n", line);
  525. filter_log('------------------------------------------------------------------');
  526.  
  527. continue;
  528. }
  529. }
  530. }
  531.  
  532. // 保留不需要过滤的行
  533. result.push(line);
  534. }
  535.  
  536. return result;
  537. }
  538.  
  539. async function safely_process_m3u8(url, content) {
  540. try {
  541. const lines = content.split('\n');
  542. const new_ines = filter_lines(lines);
  543.  
  544. return new_ines.join('\n');
  545. } catch (e) {
  546. filter_log(`处理 m3u8 文件时出错: ${url}`, e);
  547.  
  548. return content;
  549. }
  550. }
  551.  
  552. // 脚本菜单变量
  553. let menu_item_violent = null;
  554. let menu_item_mode = null;
  555. let menu_item_host_join = null;
  556. let menu_item_toast = null;
  557. let menu_item_filter_tip = null;
  558.  
  559. function hookXHR() {
  560. const OriginalXHR = unsafeWindow.XMLHttpRequest;
  561. unsafeWindow.XMLHttpRequest = class extends OriginalXHR {
  562. constructor() {
  563. super();
  564.  
  565. this.addEventListener('readystatechange', async function () {
  566.  
  567. if (this.readyState === 4 && this.status === 200 && is_m3u8_file(this.responseURL)) {
  568.  
  569. filter_log('----------------------------hookXHR成功---------------------------');
  570.  
  571. const modifiedResponse = await safely_process_m3u8(this.responseURL, this.responseText);
  572. Object.defineProperty(this, 'responseText', { value: modifiedResponse });
  573. Object.defineProperty(this, 'response', { value: modifiedResponse });
  574.  
  575. if (show_toast_tip_flag) {
  576. message.success('已成功过滤切片广告');
  577. }
  578.  
  579. filter_done_flag = true;
  580. GM_unregisterMenuCommand(menu_item_filter_tip);
  581. check_menu_item_filter_tip();
  582. }
  583. }, false);
  584. }
  585. };
  586. }
  587.  
  588. function initHook() {
  589. hookXHR();
  590. }
  591.  
  592. // 初始化菜单判断变量
  593. violent_filter_mode_flag = GM_getValue('violent_filter_mode_flag', false);
  594. script_whitelist_mode_flag = GM_getValue('script_whitelist_mode_flag', false);
  595. the_current_host_in_whitelist_flag = GM_getValue(the_current_host, false);
  596. show_toast_tip_flag = GM_getValue('show_toast_tip_flag', false);
  597.  
  598. function check_menu_item_violent() {
  599. if (violent_filter_mode_flag) {
  600. menu_item_violent = GM_registerMenuCommand('暴力拆解模式(可点击切换到自动判断过滤模式)', function() {
  601.  
  602. GM_setValue('violent_filter_mode_flag', false);
  603.  
  604. violent_filter_mode_flag = false;
  605.  
  606. message.success('已设置:<br><br>自动判断过滤模式!');
  607.  
  608. unsafeWindow.location.reload();
  609. });
  610. } else {
  611. menu_item_violent = GM_registerMenuCommand('自动判断过滤模式(可点击切换到暴力拆解模式)', function() {
  612.  
  613. GM_setValue('violent_filter_mode_flag', true);
  614.  
  615. violent_filter_mode_flag = true;
  616.  
  617. message.success('已设置:<br><br>暴力拆解模式!');
  618.  
  619. unsafeWindow.location.reload();
  620. });
  621. }
  622. }
  623.  
  624. function check_menu_item_mode() {
  625. if (script_whitelist_mode_flag) {
  626. menu_item_mode = GM_registerMenuCommand('现在是白名单模式(可点击切换到全匹配模式)', function() {
  627.  
  628. GM_setValue('script_whitelist_mode_flag', false);
  629.  
  630. script_whitelist_mode_flag = false;
  631.  
  632. message.success('已设置:<br><br>全匹配模式,即匹配所有网站!');
  633.  
  634. unsafeWindow.location.reload();
  635. });
  636. } else {
  637. menu_item_mode = GM_registerMenuCommand('现在是全匹配模式(可点击切换到白名单模式)', function() {
  638.  
  639. GM_setValue('script_whitelist_mode_flag', true);
  640.  
  641. script_whitelist_mode_flag = true;
  642.  
  643. message.success('已设置:<br><br>白名单模式,即需要单个网站设置加入过滤名单!');
  644.  
  645. unsafeWindow.location.reload();
  646. });
  647. }
  648. }
  649.  
  650. function check_menu_item_host_join() {
  651. if (script_whitelist_mode_flag) {
  652.  
  653. if (the_current_host_in_whitelist_flag) {
  654.  
  655. initHook();
  656.  
  657. if (unsafeWindow.self === unsafeWindow.top) {
  658.  
  659. if (menu_item_host_join === null) {
  660. filter_log('----------------------------脚本加载完成---------------------------');
  661. filter_log('----------------------------还没 hookXHR---------------------------');
  662. }
  663.  
  664. menu_item_host_join = GM_registerMenuCommand('本网站已开启过滤(可点击关闭广告过滤)', function() {
  665.  
  666. GM_setValue(the_current_host, false);
  667.  
  668. the_current_host_in_whitelist_flag = false;
  669.  
  670. message.success('已设置:<br><br>关闭本网站的广告过滤!');
  671.  
  672. unsafeWindow.location.reload();
  673. });
  674.  
  675. }
  676. } else {
  677. if (unsafeWindow.self === unsafeWindow.top) {
  678.  
  679. if (menu_item_host_join === null) {
  680. filter_log('----------------------------还没开启过滤---------------------------');
  681. }
  682.  
  683. menu_item_host_join = GM_registerMenuCommand('本网站已关闭过滤(可点击开启广告过滤)', function() {
  684.  
  685. GM_setValue(the_current_host, true);
  686.  
  687. the_current_host_in_whitelist_flag = true;
  688.  
  689. message.success('已设置:<br><br>开启本网站的广告过滤!');
  690.  
  691. unsafeWindow.location.reload();
  692. });
  693.  
  694. }
  695. }
  696. } else {
  697. if (menu_item_host_join === null) {
  698. filter_log('----------------------------脚本加载完成---------------------------');
  699. filter_log('----------------------------还没 hookXHR---------------------------');
  700.  
  701. initHook();
  702. }
  703. }
  704. }
  705.  
  706. function check_menu_item_toast() {
  707. if (show_toast_tip_flag) {
  708. if (unsafeWindow.self === unsafeWindow.top) {
  709. menu_item_toast = GM_registerMenuCommand('已开启弹窗提示(可点击设置关闭)', function() {
  710.  
  711. GM_setValue('show_toast_tip_flag', false);
  712.  
  713. show_toast_tip_flag = false;
  714.  
  715. message.success('已设置:<br><br>关闭弹窗提示!');
  716.  
  717. add_menu_item_all();
  718. });
  719. }
  720. } else {
  721. if (unsafeWindow.self === unsafeWindow.top) {
  722. menu_item_toast = GM_registerMenuCommand('已关闭弹窗提示(可点击设置开启)', function() {
  723.  
  724. GM_setValue('show_toast_tip_flag', true);
  725.  
  726. show_toast_tip_flag = true;
  727.  
  728. message.success('已设置:<br><br>开启弹窗提示!');
  729.  
  730. add_menu_item_all();
  731. });
  732. }
  733. }
  734. }
  735.  
  736. function register_menu_filter_done_tip() {
  737. menu_item_filter_tip = GM_registerMenuCommand('提示:已成功过滤视频切片广告', function() {
  738. Swal.fire({
  739. title: '过滤日志',
  740. type: 'info',
  741. icon: 'info',
  742. html: filter_log_html,
  743. width: '50%',
  744. showClass: {
  745. popup: ''
  746. },
  747. hideClass: {
  748. popup: ''
  749. },
  750. confirmButtonText: 'OK',
  751. showCloseButton: true
  752. });
  753. });
  754. }
  755.  
  756. function register_menu_filter_undone_tip() {
  757. menu_item_filter_tip = GM_registerMenuCommand('提示:还没有过滤视频切片广告', function() {
  758. Swal.fire({
  759. type: 'info',
  760. icon: 'info',
  761. html: '过滤视频切片广告失败! <br><br> ctrl + F5 刷新试试 <br><br> 如果还不行,那说明:<br><br> 视频格式不是m3u8,无法过滤!',
  762. showClass: {
  763. popup: ''
  764. },
  765. hideClass: {
  766. popup: ''
  767. },
  768. confirmButtonText: 'OK',
  769. showCloseButton: true
  770. });
  771. });
  772. }
  773.  
  774. function check_menu_item_filter_tip() {
  775. if (filter_done_flag) {
  776. register_menu_filter_done_tip();
  777. } else {
  778. if ((script_whitelist_mode_flag && the_current_host_in_whitelist_flag) || !script_whitelist_mode_flag) {
  779. register_menu_filter_undone_tip();
  780. }
  781. }
  782. }
  783.  
  784. function remove_menu_item_all() {
  785. GM_unregisterMenuCommand(menu_item_violent);
  786. GM_unregisterMenuCommand(menu_item_mode);
  787. GM_unregisterMenuCommand(menu_item_host_join);
  788. GM_unregisterMenuCommand(menu_item_toast);
  789. }
  790.  
  791. function add_menu_item_all() {
  792. remove_menu_item_all();
  793.  
  794. check_menu_item_violent();
  795. check_menu_item_mode();
  796. check_menu_item_host_join();
  797. check_menu_item_toast();
  798. }
  799.  
  800. add_menu_item_all();
  801.  
  802. function listen_video_load_meta() {
  803. if ((script_whitelist_mode_flag && the_current_host_in_whitelist_flag) || !script_whitelist_mode_flag) {
  804.  
  805. let try_listen_video_meta_n = 0;
  806.  
  807. let try_to_add_listen_video_meta = function () {
  808. let the_video_dom = unsafeWindow.document.querySelector('video');
  809. if (the_video_dom) {
  810. the_video_dom.addEventListener('loadedmetadata', () => {
  811. setTimeout(function() {
  812. if (!filter_done_flag) {
  813.  
  814. if (show_toast_tip_flag) {
  815.  
  816. if (the_video_dom.src.indexOf('.mp4') > 0) {
  817. message.error('过滤失败!<br><br>播放视频格式是mp4 <br><br>不是m3u8,无法过滤!');
  818. } else {
  819. message.warning('过滤失败!ctrl+F5 刷新试试,<br><br> 若还不行,说明播放视频格式<br>不是m3u8,无法过滤!');
  820. }
  821.  
  822. }
  823.  
  824. GM_unregisterMenuCommand(menu_item_filter_tip);
  825. check_menu_item_filter_tip();
  826. }
  827. }, 1000);
  828. });
  829. } else {
  830. try_listen_video_meta_n++;
  831. if (try_listen_video_meta_n < 50) {
  832. setTimeout(try_to_add_listen_video_meta, 100);
  833. }
  834. }
  835. }
  836.  
  837. try_to_add_listen_video_meta();
  838. }
  839. }
  840.  
  841. listen_video_load_meta();
  842.  
  843. })();