哔哩哔哩屏蔽增强器

对B站视频或评论进行屏蔽,支持关键词模糊正则等,支持时长播放弹幕过滤等,如视频、评论、动态、直播间的评论等,详情可看下面支持的屏蔽类型

目前为 2025-04-06 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name 哔哩哔哩屏蔽增强器
  3. // @namespace http://tampermonkey.net/
  4. // @license Apache-2.0
  5. // @version 2.9.1
  6. // @author byhgz
  7. // @description 对B站视频或评论进行屏蔽,支持关键词模糊正则等,支持时长播放弹幕过滤等,如视频、评论、动态、直播间的评论等,详情可看下面支持的屏蔽类型
  8. // @icon https://static.hdslb.com/images/favicon.ico
  9. // @noframes
  10. // @run-at document-start
  11. // @grant GM_setValue
  12. // @grant GM_getValue
  13. // @grant GM_deleteValue
  14. // @grant GM_addStyle
  15. // @grant GM_unregisterMenuCommand
  16. // @grant GM_registerMenuCommand
  17. // @grant GM_openInTab
  18. // @exclude *://message.bilibili.com/pages/nav/header_sync
  19. // @exclude *://message.bilibili.com/pages/nav/index_new_pc_sync
  20. // @exclude *://live.bilibili.com/blackboard/dropdown-menu.html
  21. // @exclude *://live.bilibili.com/p/html/live-web-mng/*
  22. // @exclude *://www.bilibili.com/correspond/*
  23. // @match *://search.bilibili.com/*
  24. // @match *://t.bilibili.com/*
  25. // @match *://space.bilibili.com/*
  26. // @match *://live.bilibili.com/*
  27. // @match *://www.bilibili.com/*
  28. // @require https://cdn.jsdelivr.net/npm/vue@2
  29. // @require https://unpkg.com/element-ui/lib/index.js
  30. // @require https://cdn.jsdelivr.net/npm/dexie@4.0.10/dist/dexie.min.js
  31. // @require https://update.gf.qytechs.cn/scripts/517928/gz_ui_css-v1.js
  32. // @source https://gitee.com/hangexi/BiBiBSPUserVideoMonkeyScript
  33. // homepage https://scriptcat.org/zh-CN/script-show-page/1029
  34. // ==/UserScript==
  35. "use strict";
  36. (function (Vue, Dexie) {
  37. 'use strict';
  38. var gmUtil = {
  39. setData(key, content) {
  40. GM_setValue(key, content);
  41. },
  42. getData(key, defaultValue) {
  43. return GM_getValue(key, defaultValue);
  44. },
  45. delData(key) {
  46. if (!this.isData(key)) {
  47. return false;
  48. }
  49. GM_deleteValue(key);
  50. return true;
  51. },
  52. isData(key) {
  53. return this.getData(key) !== undefined;
  54. },
  55. addStyle(style) {
  56. GM_addStyle(style);
  57. },
  58. addGMMenu(text, func, shortcutKey = null) {
  59. return GM_registerMenuCommand(text, func, shortcutKey);
  60. },
  61. openInTab(url, options = {active: true, insert: true, setParent: true}) {
  62. GM_openInTab(url, options);
  63. }
  64. };
  65. class EventEmitter {
  66. #regularEvents = {
  67. events: {},
  68. futures: {}
  69. }
  70. #callbackEvents = {
  71. events: {},
  72. callbackInterval: 1500
  73. }
  74. on(eventName, callback) {
  75. const events = this.#regularEvents.events;
  76. if (events[eventName]) {
  77. events[eventName].push(callback);
  78. return
  79. }
  80. events[eventName] = [];
  81. events[eventName].push(callback);
  82. const futureEvents = this.#regularEvents.futures;
  83. if (futureEvents[eventName]) {
  84. for (const futureEvent of futureEvents[eventName]) {
  85. callback(...futureEvent);
  86. }
  87. delete futureEvents[eventName];
  88. }
  89. }
  90. once(eventName, callback) {
  91. const onceCallback = (...args) => {
  92. callback(...args);
  93. this.#removeCallback(eventName, onceCallback);
  94. };
  95. this.on(eventName, onceCallback);
  96. }
  97. handler(eventName, callback) {
  98. const handlerEvents = this.#callbackEvents.events;
  99. if (handlerEvents[eventName]) {
  100. throw new Error('该事件名已经存在,请更换事件名')
  101. }
  102. handlerEvents[eventName] = callback;
  103. }
  104. invoke(eventName, ...data) {
  105. return new Promise(resolve => {
  106. const handlerEvents = this.#callbackEvents.events;
  107. if (handlerEvents[eventName]) {
  108. resolve(handlerEvents[eventName](...data));
  109. return
  110. }
  111. const i1 = setInterval(() => {
  112. if (handlerEvents[eventName]) {
  113. clearInterval(i1);
  114. resolve(handlerEvents[eventName](...data));
  115. }
  116. }, this.#callbackEvents.callbackInterval);
  117. })
  118. }
  119. send(eventName, ...data) {
  120. const ordinaryEvents = this.#regularEvents;
  121. const events = ordinaryEvents.events;
  122. const event = events[eventName];
  123. if (event) {
  124. for (const callback of event) {
  125. callback(...data);
  126. }
  127. return;
  128. }
  129. const futures = ordinaryEvents.futures;
  130. if (futures[eventName]) {
  131. futures[eventName].push(data);
  132. return;
  133. }
  134. futures[eventName] = [];
  135. futures[eventName].push(data);
  136. }
  137. #removeCallback(eventName, callback) {
  138. const events = this.#regularEvents.events;
  139. if (events[eventName]) {
  140. events[eventName] = events[eventName].filter(cb => cb !== callback);
  141. }
  142. const handlerEvents = this.#callbackEvents.events;
  143. if (handlerEvents[eventName]) {
  144. handlerEvents[eventName] = handlerEvents[eventName].filter(cb => cb !== callback);
  145. }
  146. }
  147. off(eventName) {
  148. const events = this.#regularEvents.events;
  149. if (events[eventName]) {
  150. delete events[eventName];
  151. return true
  152. }
  153. const handlerEvents = this.#callbackEvents.events;
  154. if (handlerEvents[eventName]) {
  155. delete handlerEvents[eventName];
  156. return true
  157. }
  158. return false
  159. }
  160. setInvokeInterval(interval) {
  161. this.#callbackEvents.callbackInterval = interval;
  162. }
  163. getEvents() {
  164. return {
  165. regularEvents: this.#regularEvents,
  166. callbackEvents: this.#callbackEvents
  167. }
  168. }
  169. }
  170. const eventEmitter = new EventEmitter();
  171. const setBorderColor = (color) => {
  172. gmUtil.setData("borderColor", color);
  173. };
  174. const defBorderColor = "rgb(0, 243, 255)";
  175. const getBorderColor = () => {
  176. return gmUtil.getData("borderColor", defBorderColor)
  177. };
  178. const setOutputInformationFontColor = (color) => {
  179. gmUtil.setData("output_information_font_color", color);
  180. };
  181. const defOutputInformationFontColor = "rgb(119,128,248)";
  182. const getOutputInformationFontColor = () => {
  183. return gmUtil.getData("output_information_font_color", defOutputInformationFontColor)
  184. };
  185. const setHighlightInformationColor = (color) => {
  186. gmUtil.setData("highlight_information_color", color);
  187. };
  188. const defHighlightInformationColor = "rgb(234, 93, 93)";
  189. const getHighlightInformationColor = () => {
  190. return gmUtil.getData("highlight_information_color", defHighlightInformationColor);
  191. };
  192. const setDefaultColorInfo = () => {
  193. setBorderColor(defBorderColor);
  194. setOutputInformationFontColor(defOutputInformationFontColor);
  195. setHighlightInformationColor(defHighlightInformationColor);
  196. };
  197. const getBOnlyTheHomepageIsBlocked = () => {
  198. return gmUtil.getData("bOnlyTheHomepageIsBlocked", false);
  199. };
  200. const getAdaptationBAppCommerce = () => {
  201. return gmUtil.getData("adaptation-b-app-recommend", false) === true;
  202. };
  203. const isShowRightTopMainButSwitch = () => {
  204. return gmUtil.getData("showRightTopMainButSwitch", true) === true;
  205. };
  206. const isFirstFullDisplay = () => {
  207. return gmUtil.getData('isFirstFullDisplay', true) === true
  208. };
  209. const isHalfHiddenIntervalAfterInitialDisplay = () => {
  210. return gmUtil.getData('is_half_hidden_interval_after_initial_display', true) === true
  211. };
  212. const isCompatible_BEWLY_BEWLY = () => {
  213. return gmUtil.getData("compatible_BEWLY_BEWLY", false) === true;
  214. };
  215. const isDiscardOldCommentAreas = () => {
  216. return gmUtil.getData("discardOldCommentAreas", false) === true;
  217. };
  218. const isDelPlayerPageRightVideoList = () => {
  219. return gmUtil.getData("isDelPlayerPageRightVideoList", false) === true
  220. };
  221. const bFuzzyAndRegularMatchingWordsToLowercase$1 = () => {
  222. return gmUtil.getData("bFuzzyAndRegularMatchingWordsToLowercase", false)
  223. };
  224. const isRequestFrequencyVal = () => {
  225. return gmUtil.getData("requestFrequencyVal", 0.2)
  226. };
  227. const isDisableNetRequestsBvVideoInfo = () => {
  228. return gmUtil.getData('isDisableNetRequestsBvVideoInfo', false)
  229. };
  230. const isBlockFollowed = () => {
  231. return gmUtil.getData('blockFollowed', false)
  232. };
  233. const isUpOwnerExclusive = () => {
  234. return gmUtil.getData('is_up_owner_exclusive', false)
  235. };
  236. const isGenderRadioVal = () => {
  237. return gmUtil.getData('genderRadioVal', '不处理');
  238. };
  239. const isVipTypeRadioVal = () => {
  240. return gmUtil.getData('vipTypeRadioVal', '不处理');
  241. };
  242. const isSeniorMember = () => {
  243. return gmUtil.getData('is_senior_member', false)
  244. };
  245. const isCopyrightRadio = () => {
  246. return gmUtil.getData('copyrightRadioVal', '不处理');
  247. };
  248. const isDelBottomComment = () => {
  249. return gmUtil.getData('isDelBottomComment', false)
  250. };
  251. const isBlockVerticalVideo = () => {
  252. return gmUtil.getData('blockVerticalVideo', false)
  253. };
  254. const isCheckTeamMember = () => {
  255. return gmUtil.getData('checkTeamMember', false)
  256. };
  257. const getVideoLikeRate = () => {
  258. return gmUtil.getData('video_like_rate', 0.05)
  259. };
  260. const isVideoLikeRateBlockingStatus = () => {
  261. return gmUtil.getData('video_like_rate_blocking_status', false)
  262. };
  263. const isCoinLikesRatioRateBlockingStatus = () => {
  264. return gmUtil.getData('coin_likes_ratio_rate_blocking_status', false)
  265. };
  266. const getCoinLikesRatioRate = () => {
  267. return gmUtil.getData('coin_likes_ratio_rate', 0.05)
  268. };
  269. const isCoinLikesRatioRateDisabled = () => {
  270. return gmUtil.getData('coin_likes_ratio_rate_blocking_status', false)
  271. };
  272. const isInteractiveRateBlockingStatus = () => {
  273. return gmUtil.getData('interactive_rate_blocking_status', false)
  274. };
  275. const getInteractiveRate = () => {
  276. return gmUtil.getData('interactive_rate', 0.05)
  277. };
  278. const isTripleRateBlockingStatus = () => {
  279. return gmUtil.getData('triple_rate_blocking_status', false)
  280. };
  281. const getTripleRate = () => {
  282. return gmUtil.getData('triple_rate', 0.05)
  283. };
  284. const getUidRangeMasking = () => {
  285. return gmUtil.getData('uid_range_masking', [0, 100])
  286. };
  287. const isUidRangeMaskingStatus = () => {
  288. return gmUtil.getData('uid_range_masking_status', false)
  289. };
  290. const isTimeRangeMaskingStatus = () => {
  291. return gmUtil.getData('time_range_masking_status', false)
  292. };
  293. const getTimeRangeMaskingArr = () => {
  294. return gmUtil.getData('time_range_masking', [])
  295. };
  296. const isDelPlayerEndingPanel = () => {
  297. return gmUtil.getData('is_del_player_ending_panel', false)
  298. };
  299. const isOpenDev = () => {
  300. return gmUtil.getData('open-dev', false)
  301. };
  302. const setOpenDev = (bool) => {
  303. gmUtil.setData('open-dev', bool);
  304. };
  305. const getCommentWordLimitVal = () => {
  306. return gmUtil.getData('comment_word_limit', -1)
  307. };
  308. const getSubstituteWordsArr = () => {
  309. return gmUtil.getData('substitute_words', [])
  310. };
  311. const isClearCommentEmoticons = () => {
  312. return gmUtil.getData('is_clear_comment_emoticons', false)
  313. };
  314. const isReplaceCommentSearchTerms = () => {
  315. return gmUtil.getData('is_replace_comment_search_terms', false)
  316. };
  317. const enableReplacementProcessing = () => {
  318. return gmUtil.getData('enable_replacement_processing', false)
  319. };
  320. var localMKData = {
  321. getTripleRate,
  322. isTripleRateBlockingStatus,
  323. setBorderColor,
  324. getBorderColor,
  325. setOutputInformationFontColor,
  326. getOutputInformationFontColor,
  327. setHighlightInformationColor,
  328. getHighlightInformationColor,
  329. getBOnlyTheHomepageIsBlocked,
  330. getAdaptationBAppCommerce,
  331. setDefaultColorInfo,
  332. isCompatible_BEWLY_BEWLY,
  333. isDiscardOldCommentAreas,
  334. isShowRightTopMainButSwitch,
  335. isFirstFullDisplay,
  336. isHalfHiddenIntervalAfterInitialDisplay,
  337. isDelPlayerPageRightVideoList,
  338. bFuzzyAndRegularMatchingWordsToLowercase: bFuzzyAndRegularMatchingWordsToLowercase$1,
  339. isRequestFrequencyVal,
  340. isDisableNetRequestsBvVideoInfo,
  341. isBlockFollowed,
  342. isUpOwnerExclusive,
  343. isGenderRadioVal,
  344. isVipTypeRadioVal,
  345. isSeniorMember,
  346. isCopyrightRadio,
  347. isDelBottomComment,
  348. isBlockVerticalVideo,
  349. isCheckTeamMember,
  350. getVideoLikeRate,
  351. isVideoLikeRateBlockingStatus,
  352. isCoinLikesRatioRateBlockingStatus,
  353. getCoinLikesRatioRate,
  354. isCoinLikesRatioRateDisabled,
  355. isInteractiveRateBlockingStatus,
  356. getInteractiveRate,
  357. getUidRangeMasking,
  358. isUidRangeMaskingStatus,
  359. isTimeRangeMaskingStatus,
  360. isDelPlayerEndingPanel,
  361. getTimeRangeMaskingArr,
  362. getCommentWordLimitVal
  363. };
  364. const group_url = 'http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=tFU0xLt1uO5u5CXI2ktQRLh_XGAHBl7C&authKey=KAf4rICQYjfYUi66WelJAGhYtbJLILVWumOm%2BO9nM5fNaaVuF9Iiw3dJoPsVRUak&noverify=0&group_code=876295632';
  365. const scriptCat_js_url = 'https://scriptcat.org/zh-CN/script-show-page/1029';
  366. const b_url = 'https://space.bilibili.com/473239155';
  367. const common_question_url = 'https://docs.qq.com/doc/DSlJNR1NVcGR3eEto';
  368. const update_log_url = 'https://docs.qq.com/doc/DSnhjSVZmRkpCd0Nj';
  369. const adaptationBAppCommerce = localMKData.getAdaptationBAppCommerce();
  370. const compatibleBEWLYBEWLY = localMKData.isCompatible_BEWLY_BEWLY();
  371. const bOnlyTheHomepageIsBlocked = localMKData.getBOnlyTheHomepageIsBlocked();
  372. const returnTempVal = {state: false};
  373. var globalValue = {
  374. group_url,
  375. scriptCat_js_url,
  376. b_url,
  377. common_question_url,
  378. update_log_url,
  379. adaptationBAppCommerce,
  380. compatibleBEWLYBEWLY,
  381. bOnlyTheHomepageIsBlocked
  382. };
  383. gmUtil.addGMMenu('主面板', () => {
  384. eventEmitter.send('主面板开关');
  385. }, 'Q');
  386. gmUtil.addGMMenu('脚本猫脚本更新页', () => {
  387. gmUtil.openInTab(globalValue.scriptCat_js_url);
  388. }, 'E');
  389. gmUtil.addGMMenu('gf脚本更新页', () => {
  390. gmUtil.openInTab('https://gf.qytechs.cn/zh-CN/scripts/461382');
  391. }, 'W');
  392. gmUtil.addGMMenu('加入or反馈', () => {
  393. gmUtil.openInTab(globalValue.group_url);
  394. }, "T");
  395. gmUtil.addGMMenu('常见问题', () => {
  396. gmUtil.openInTab(globalValue.common_question_url);
  397. }, 'Y');
  398. gmUtil.addGMMenu('更新日志', () => {
  399. gmUtil.openInTab(globalValue.update_log_url);
  400. }, 'U');
  401. const start = () => {
  402. let loop = false;
  403. let msg;
  404. if (!Vue) {
  405. loop = true;
  406. msg = 'Vue is not defined,Vue未定义,请检查是否引入了Vue';
  407. }
  408. if (!Dexie) {
  409. loop = true;
  410. msg = 'Dexie is not defined,Dexie未定义,请检查是否引入了Dexie';
  411. }
  412. if (loop) {
  413. if (confirm('外部库验证失败:' + msg + `\n请联系作者核查问题\n可通过点击确定按钮跳转。
  414. \n脚本主页信息中,有相关解决文档
  415. \n或通过脚本信息底下联系方式联系作者解决`)) {
  416. gmUtil.openInTab(globalValue.scriptCat_js_url);
  417. gmUtil.openInTab(globalValue.group_url);
  418. }
  419. throw new Error(`外部库验证失败:${msg}`)
  420. }
  421. };
  422. start();
  423. const panel_settings_vue = {
  424. template: `
  425. <div>
  426. <el-card shadow="never">
  427. <template #header>
  428. <span>颜色设置</span>
  429. </template>
  430. <div class="el-horizontal-center">
  431. 选择器
  432. <el-color-picker v-model="input_color"/>
  433. </div>
  434. <el-button @click="setBorderColorBut">设置边框色</el-button>
  435. <el-button @click="setDefFontColorForOutputInformationBut">设置输出信息默认字体色</el-button>
  436. <el-button @click="setTheFontColorForOutputInformationBut">设置输出信息高亮字体色</el-button>
  437. <el-tooltip content="刷新页面生效">
  438. <el-button @click="setDefInfoBut">恢复默认</el-button>
  439. </el-tooltip>
  440. </el-card>
  441. <el-card shadow="never">
  442. <template #header>
  443. <span>页面右侧悬浮按钮设置</span>
  444. </template>
  445. <el-switch v-model="showRightTopMainButSwitch" active-text="显示按钮"></el-switch>
  446. <el-tooltip content="页面每次加载完之后是否完整展示按钮,否则半隐藏">
  447. <el-switch v-model="isFirstFullDisplay" active-text="初次完整显示"></el-switch>
  448. </el-tooltip>
  449. <el-tooltip content="页面每次加载完之后如完整展示按钮时,间隔10秒后半隐藏处理">
  450. <el-switch v-model="isHalfHiddenIntervalAfterInitialDisplay"
  451. active-text="初次显示后间隔半隐藏"/>
  452. </el-tooltip>
  453. </el-card>
  454. <el-card shadow="never">
  455. <template #header>
  456. <span>说明</span>
  457. </template>
  458. <div>按键盘tab键上的~键为展开关闭主面板</div>
  459. </el-card>
  460. <el-card shadow="never">
  461. <template #header>
  462. <span>devTools</span>
  463. </template>
  464. <el-input v-model.trim="devToolsInputVal" @keyup.enter.native="changeDevToolsInput"></el-input>
  465. </el-card>
  466. </div>`,
  467. data() {
  468. return {
  469. input_color: null,
  470. showRightTopMainButSwitch: localMKData.isShowRightTopMainButSwitch(),
  471. isFirstFullDisplay: localMKData.isFirstFullDisplay(),
  472. isHalfHiddenIntervalAfterInitialDisplay: localMKData.isHalfHiddenIntervalAfterInitialDisplay(),
  473. devToolsInputVal: ''
  474. }
  475. },
  476. methods: {
  477. setBorderColorBut() {
  478. this.$confirm('是否设置面板边框颜色', '提示').then(() => {
  479. localMKData.setBorderColor(this.input_color);
  480. this.$alert("已设置面板边框颜色,刷新生效");
  481. });
  482. },
  483. setDefFontColorForOutputInformationBut() {
  484. this.$confirm("是否设置输出信息默认字体颜色", "提示").then(() => {
  485. localMKData.setOutputInformationFontColor(this.input_color);
  486. this.$alert("已设置输出信息默认字体颜色,刷新生效");
  487. });
  488. },
  489. setTheFontColorForOutputInformationBut() {
  490. this.$confirm('是要设置输出信息高亮字体颜色吗?').then(() => {
  491. localMKData.setHighlightInformationColor(this.input_color);
  492. this.$alert("已设置输出信息高亮字体颜色,刷新生效");
  493. });
  494. },
  495. setDefInfoBut() {
  496. localMKData.setDefaultColorInfo();
  497. this.$alert("已恢复默认颜色,刷新生效");
  498. },
  499. changeDevToolsInput() {
  500. const toolsInputVal = this.devToolsInputVal;
  501. if (toolsInputVal === 'show-dev') {
  502. setOpenDev(true);
  503. eventEmitter.send('debugger-dev-show', true);
  504. this.devToolsInputVal = '';
  505. return
  506. }
  507. if (toolsInputVal === 'stop-dev') {
  508. setOpenDev(false);
  509. eventEmitter.send('debugger-dev-show', false);
  510. this.devToolsInputVal = '';
  511. return;
  512. }
  513. if (isOpenDev()) {
  514. eventEmitter.send(toolsInputVal);
  515. }
  516. }
  517. },
  518. watch: {
  519. showRightTopMainButSwitch(newVal) {
  520. gmUtil.setData("showRightTopMainButSwitch", newVal === true);
  521. eventEmitter.send('显隐主面板开关', newVal);
  522. },
  523. isFirstFullDisplay(newVal) {
  524. gmUtil.setData('isFirstFullDisplay', newVal === true);
  525. },
  526. isHalfHiddenIntervalAfterInitialDisplay(newBool) {
  527. gmUtil.setData('is_half_hidden_interval_after_initial_display', newBool === true);
  528. }
  529. }
  530. };
  531. const getSelectOptions = () => {
  532. const options = [
  533. {
  534. value: '模糊匹配',
  535. label: '模糊匹配',
  536. children: []
  537. },
  538. {
  539. value: '正则匹配',
  540. label: '正则匹配',
  541. children: []
  542. },
  543. {
  544. value: '多重匹配',
  545. label: '多重匹配',
  546. children: []
  547. },
  548. {
  549. value: '精确匹配',
  550. label: '精确匹配',
  551. children: []
  552. }
  553. ];
  554. for (let {name, key} of ruleKeyListData) {
  555. let children;
  556. if (name.includes('(模糊匹配)')) {
  557. children = options[0].children;
  558. }
  559. if (name.includes('(正则匹配)')) {
  560. children = options[1].children;
  561. }
  562. if (name.includes('(组合精确匹配)')) {
  563. children = options[2].children;
  564. }
  565. if (name.includes('(精确匹配)')) {
  566. children = options[3].children;
  567. }
  568. children.push({
  569. value: key,
  570. label: name
  571. });
  572. }
  573. return options
  574. };
  575. const ruleKeyListData = [{
  576. key: "name",
  577. name: "用户名(模糊匹配)",
  578. oldKey: "userNameKeyArr",
  579. oldName: "用户名黑名单模式(模糊匹配)"
  580. }, {
  581. key: "precise_name",
  582. name: "用户名(精确匹配)",
  583. oldKey: "userNameArr",
  584. oldName: "用户名黑名单模式(精确匹配)"
  585. }, {
  586. key: "nameCanonical",
  587. name: "用户名(正则匹配)"
  588. }, {
  589. key: "precise_uid",
  590. name: "用户uid(精确匹配)",
  591. oldKey: "userUIDArr",
  592. oldName: "用户uid黑名单模式(精确匹配)"
  593. }, {
  594. key: "precise_uid_white",
  595. name: "用户uid白名单(精确匹配)",
  596. oldKey: "userWhiteUIDArr",
  597. oldName: "用户uid白名单模式(精确匹配)"
  598. }, {
  599. key: "title",
  600. name: "标题(模糊匹配)",
  601. oldKey: "titleKeyArr",
  602. oldName: "标题黑名单模式(模糊匹配)"
  603. }, {
  604. key: "titleCanonical",
  605. name: "标题(正则匹配)",
  606. oldKey: "titleKeyCanonicalArr",
  607. oldName: "标题黑名单模式(正则匹配)"
  608. }, {
  609. key: "commentOn",
  610. name: "评论关键词(模糊匹配)",
  611. oldKey: "commentOnKeyArr",
  612. oldName: "评论关键词黑名单模式(模糊匹配)"
  613. }, {
  614. key: "commentOnCanonical",
  615. name: "评论关键词(正则匹配)",
  616. oldKey: "contentOnKeyCanonicalArr",
  617. oldName: "评论关键词黑名单模式(正则匹配)"
  618. }, {
  619. key: "precise_fanCard",
  620. name: "粉丝牌(精确匹配)",
  621. oldKey: "fanCardArr",
  622. oldName: "粉丝牌黑名单模式(精确匹配)"
  623. }, {
  624. key: "dynamic",
  625. name: "动态关键词(模糊匹配)",
  626. oldKey: "dynamicArr",
  627. oldName: "动态关键词内容黑名单模式(模糊匹配)"
  628. }, {
  629. key: "precise_tag",
  630. name: "话题tag标签(精确匹配)",
  631. }, {
  632. key: "tag",
  633. name: "话题tag标签(模糊匹配)",
  634. }, {
  635. key: "tagCanonical",
  636. name: "话题tag标签(正则匹配)"
  637. }, {
  638. key: "precise_partition",
  639. name: "直播分区(精确匹配)"
  640. }, {
  641. key: 'videoTag',
  642. name: '视频tag(模糊匹配)',
  643. }, {
  644. key: 'precise_videoTag',
  645. name: '视频tag(精确匹配)',
  646. }, {
  647. key: 'videoTagCanonical',
  648. name: '视频tag(正则匹配)',
  649. }, {
  650. key: 'videoTag_preciseCombination',
  651. name: '视频tag(组合精确匹配)'
  652. }, {
  653. key: 'hotSearchKey',
  654. name: '热搜关键词(模糊匹配)',
  655. }, {
  656. key: 'hotSearchKeyCanonical',
  657. name: '热搜关键词(正则匹配)'
  658. }, {
  659. key: 'precise_avatarPendantName',
  660. name: '头像挂件名(精确匹配)'
  661. }, {
  662. key: 'avatarPendantName',
  663. name: '头像挂件名(模糊匹配)'
  664. }, {
  665. key: 'signature',
  666. name: '用户签名(模糊匹配)'
  667. }, {
  668. key: 'signatureCanonical',
  669. name: '用户签名(正则匹配)'
  670. }, {
  671. key: 'videoDesc',
  672. name: '视频简介(模糊匹配)'
  673. }, {
  674. key: 'videoDescCanonical',
  675. name: '视频简介(正则匹配)'
  676. }, {
  677. key: 'precise_video_bv',
  678. name: '视频bv号(精确匹配)'
  679. }
  680. ];
  681. const otherKeyListData = [
  682. {
  683. name: '最小播放量',
  684. value: 'nMinimumPlay',
  685. associated: 'nMaximumPlayback',
  686. defVal: -1
  687. },
  688. {
  689. name: '最大播放量',
  690. value: 'nMaximumPlayback',
  691. associated: 'nMinimumPlay',
  692. bLarge: true,
  693. defVal: -1
  694. },
  695. {
  696. name: '最小弹幕数',
  697. value: 'nMinimumBarrage',
  698. associated: 'nMaximumBarrage',
  699. defVal: -1
  700. },
  701. {
  702. name: '最大弹幕数',
  703. value: 'nMaximumBarrage',
  704. associated: 'nMinimumBarrage',
  705. bLarge: true,
  706. defVal: -1
  707. },
  708. {
  709. name: '最小时长',
  710. value: 'nMinimumDuration',
  711. associated: 'nMaximumDuration',
  712. defVal: -1
  713. },
  714. {
  715. name: '最大时长',
  716. value: 'nMaximumDuration',
  717. associated: 'nMinimumDuration',
  718. bLarge: true,
  719. defVal: -1
  720. },
  721. {
  722. name: '最小用户等级过滤',
  723. value: 'nMinimumLevel',
  724. associated: 'nMaximumLevel',
  725. defVal: -1
  726. },
  727. {
  728. name: '最大用户等级过滤',
  729. value: 'nMaximumLevel',
  730. associated: 'nMinimumLevel',
  731. bLarge: true,
  732. defVal: -1
  733. }
  734. ];
  735. const getRuleKeyListData = () => {
  736. return ruleKeyListData;
  737. };
  738. const getRuleKeyList = () => {
  739. return ruleKeyListData.map(item => {
  740. return item.key
  741. })
  742. };
  743. const getNameArr = () => {
  744. return gmUtil.getData("name", []);
  745. };
  746. const getPreciseNameArr = () => {
  747. return gmUtil.getData("precise_name", []);
  748. };
  749. const getNameCanonical = () => {
  750. return gmUtil.getData("nameCanonical", []);
  751. };
  752. const getPreciseUidArr = () => {
  753. return gmUtil.getData("precise_uid", []);
  754. };
  755. const getPreciseUidWhiteArr = () => {
  756. return gmUtil.getData("precise_uid_white", []);
  757. };
  758. const getTitleArr = () => {
  759. return gmUtil.getData("title", []);
  760. };
  761. const getTitleCanonicalArr = () => {
  762. return gmUtil.getData("titleCanonical", []);
  763. };
  764. const getCommentOnArr = () => {
  765. return gmUtil.getData("commentOn", []);
  766. };
  767. const getCommentOnCanonicalArr = () => {
  768. return gmUtil.getData("commentOnCanonical", []);
  769. };
  770. const getPreciseTagArr = () => {
  771. return gmUtil.getData("precise_tag", []);
  772. };
  773. const getTagArr = () => {
  774. return gmUtil.getData("tag", []);
  775. };
  776. const getTagCanonicalArr = () => {
  777. return gmUtil.getData("tagCanonical", []);
  778. };
  779. const getPreciseFanCardArr = () => {
  780. return gmUtil.getData("precise_fanCard", []);
  781. };
  782. const getPrecisePartitionArr = () => {
  783. return gmUtil.getData("precise_partition", []);
  784. };
  785. const getVideoTagArr = () => {
  786. return gmUtil.getData("videoTag", []);
  787. };
  788. const getPreciseVideoTagArr = () => {
  789. return gmUtil.getData("precise_videoTag", []);
  790. };
  791. const getVideoTagCanonicalArr = () => {
  792. return gmUtil.getData("videoTagCanonical", []);
  793. };
  794. const getHotSearchKeyArr = () => {
  795. return gmUtil.getData("hotSearchKey", []);
  796. };
  797. const getHotSearchKeyCanonicalArr = () => {
  798. return gmUtil.getData("hotSearchKeyCanonical", []);
  799. };
  800. const clearKeyItem = (ruleKey) => {
  801. gmUtil.delData(ruleKey);
  802. };
  803. const getVideoTagPreciseCombination = () => {
  804. return gmUtil.getData("videoTag_preciseCombination", []);
  805. };
  806. const setVideoTagPreciseCombination = (list) => {
  807. gmUtil.setData("videoTag_preciseCombination", list);
  808. };
  809. const getPreciseVideoBV = () => {
  810. return gmUtil.getData("precise_video_bv", []);
  811. };
  812. var ruleKeyListData$1 = {
  813. getNameArr,
  814. getPreciseNameArr,
  815. getNameCanonical,
  816. getPreciseUidArr,
  817. getPreciseUidWhiteArr,
  818. getTitleArr,
  819. getTitleCanonicalArr,
  820. getCommentOnArr,
  821. getCommentOnCanonicalArr,
  822. getRuleKeyListData,
  823. getPreciseTagArr,
  824. getTagArr,
  825. getTagCanonicalArr,
  826. getPreciseFanCardArr,
  827. getPrecisePartitionArr,
  828. getVideoTagArr,
  829. getPreciseVideoTagArr,
  830. getVideoTagCanonicalArr,
  831. getHotSearchKeyArr,
  832. getHotSearchKeyCanonicalArr,
  833. otherKeyListData,
  834. clearKeyItem,
  835. getSelectOptions,
  836. getVideoTagPreciseCombination,
  837. setVideoTagPreciseCombination,
  838. getRuleKeyList,
  839. getPreciseVideoBV
  840. };
  841. const verificationInputValue = (ruleValue, type) => {
  842. if (ruleValue === null) return {status: false, res: '内容不能为空'};
  843. if (type === "precise_uid" || type === "precise_uid_white") {
  844. ruleValue = parseInt(ruleValue);
  845. if (isNaN(ruleValue)) {
  846. return {
  847. status: false,
  848. res: '请输入数字!'
  849. };
  850. }
  851. } else {
  852. ruleValue.trim();
  853. }
  854. if (ruleValue === '') {
  855. return {status: false, res: '内容不能为空'};
  856. }
  857. return {status: true, res: ruleValue};
  858. };
  859. const addRule = (ruleValue, type) => {
  860. const verificationRes = verificationInputValue(ruleValue, type);
  861. if (!verificationRes.status) {
  862. return verificationRes
  863. }
  864. const arr = gmUtil.getData(type, []);
  865. if (arr.includes(verificationRes.res)) {
  866. return {status: false, res: '已存在此内容'};
  867. }
  868. arr.push(verificationRes.res);
  869. gmUtil.setData(type, arr);
  870. return {status: true, res: '添加成功'};
  871. };
  872. const showAddRuleInput = async (type) => {
  873. let ruleValue;
  874. try {
  875. const {value} = await eventEmitter.invoke('el-prompt', '请输入要添加的规则内容', 'tip');
  876. ruleValue = value;
  877. } catch (e) {
  878. return
  879. }
  880. const {res, status} = addRule(ruleValue, type);
  881. eventEmitter.send('el-msg', res);
  882. status && eventEmitter.send('刷新规则信息');
  883. status && eventEmitter.send('通知屏蔽');
  884. };
  885. const delRule = (type, value) => {
  886. const verificationRes = verificationInputValue(value, type);
  887. if (!verificationRes.status) {
  888. return verificationRes
  889. }
  890. const {res} = verificationRes;
  891. const arr = gmUtil.getData(type, []);
  892. const indexOf = arr.indexOf(res);
  893. if (indexOf === -1) {
  894. return {status: false, res: '不存在此内容'};
  895. }
  896. arr.splice(indexOf, 1);
  897. gmUtil.setData(type, arr);
  898. return {status: true, res: "移除成功"}
  899. };
  900. const showDelRuleInput = async (type) => {
  901. let ruleValue;
  902. try {
  903. const {value} = await eventEmitter.invoke('el-prompt', '请输入要删除的规则内容', '删除指定规则');
  904. ruleValue = value;
  905. } catch (e) {
  906. return
  907. }
  908. const {status, res} = delRule(type, ruleValue);
  909. eventEmitter.send('el-msg', res);
  910. status && eventEmitter.send('刷新规则信息');
  911. };
  912. const getRuleContent = (space = 0) => {
  913. const ruleMap = {};
  914. for (let ruleKeyListDatum of ruleKeyListData$1.getRuleKeyListData()) {
  915. const key = ruleKeyListDatum.key;
  916. ruleMap[key] = gmUtil.getData(key, []);
  917. }
  918. return JSON.stringify(ruleMap, null, space);
  919. };
  920. const verificationRuleMap = (keyArr, content) => {
  921. let parse;
  922. try {
  923. parse = JSON.parse(content);
  924. } catch (e) {
  925. alert('规则内容有误');
  926. return false;
  927. }
  928. const newRule = {};
  929. for (const key in parse) {
  930. if (!Array.isArray(parse[key])) {
  931. continue;
  932. }
  933. if (parse[key].length === 0) {
  934. continue;
  935. }
  936. newRule[key] = parse[key];
  937. }
  938. if (Object.keys(newRule).length === 0) {
  939. alert('规则内容为空');
  940. return false;
  941. }
  942. return newRule;
  943. };
  944. const overwriteImportRules = (content) => {
  945. const map = verificationRuleMap(ruleKeyListData$1.getRuleKeyList(), content);
  946. if (map === false) return false;
  947. for (let key of Object.keys(map)) {
  948. gmUtil.setData(key, map[key]);
  949. }
  950. return true;
  951. };
  952. const appendImportRules = (content) => {
  953. const map = verificationRuleMap(ruleKeyListData$1.getRuleKeyList(), content);
  954. if (map === false) return false;
  955. for (let key of Object.keys(map)) {
  956. const arr = gmUtil.getData(key, []);
  957. for (let item of map[key]) {
  958. if (!arr.includes(item)) {
  959. arr.push(item);
  960. }
  961. }
  962. gmUtil.setData(key, arr);
  963. }
  964. return true;
  965. };
  966. const overwriteImportRulesV1 = (content) => {
  967. let parse;
  968. try {
  969. parse = JSON.parse(content);
  970. } catch (e) {
  971. alert('规则内容有误');
  972. return false;
  973. }
  974. for (let ruleKeyListDatum of ruleKeyListData$1.getRuleKeyListData()) {
  975. const name = ruleKeyListDatum.oldName;
  976. const jsonRuleList = parse[name];
  977. if (!jsonRuleList) {
  978. continue;
  979. }
  980. if (jsonRuleList.length === 0) {
  981. continue;
  982. }
  983. gmUtil.setData(ruleKeyListDatum.key, jsonRuleList);
  984. }
  985. return true;
  986. };
  987. const addRulePreciseUid = (uid, isTip = true) => {
  988. const results = addRule(uid, "precise_uid");
  989. if (isTip) {
  990. eventEmitter.send('el-notify', {
  991. title: '添加精确uid操作提示',
  992. message: results.res
  993. });
  994. return results
  995. }
  996. return results;
  997. };
  998. const delRUlePreciseUid = (uid, isTip = true) => {
  999. const results = delRule('precise_uid', uid);
  1000. if (isTip) {
  1001. eventEmitter.send('el-alert', results.res);
  1002. return null
  1003. }
  1004. return results
  1005. };
  1006. const addRulePreciseName = (name, tip = true) => {
  1007. const results = addRule(name, "precise_name");
  1008. if (tip) {
  1009. eventEmitter.send('el-msg', results.res);
  1010. }
  1011. return results;
  1012. };
  1013. const findRuleItemValue = (type, value) => {
  1014. return gmUtil.getData(type, []).find(item => item === value) || null
  1015. };
  1016. const addItemRule = (arr, key, coverage = true) => {
  1017. const complianceList = [];
  1018. for (let v of arr) {
  1019. const {status, res} = verificationInputValue(v, key);
  1020. if (!status) return {status: false, msg: `内容中有误:${res}`}
  1021. complianceList.push(v);
  1022. }
  1023. if (coverage) {
  1024. gmUtil.setData(key, complianceList);
  1025. return {status: true, msg: `添加成功-覆盖模式,数量:${complianceList.length}`}
  1026. }
  1027. const oldArr = gmUtil.getData(key, []);
  1028. const newList = complianceList.filter(item => !oldArr.includes(item));
  1029. if (newList.length === 0) {
  1030. return {status: false, msg: '内容重复'}
  1031. }
  1032. gmUtil.setData(key, oldArr.concat(newList));
  1033. return {status: true, msg: '添加成功-追加模式,新增数量:' + newList.length}
  1034. };
  1035. const addPreciseUidItemRule = (uidArr, isTip = true, coverage = true) => {
  1036. const {status, msg} = addItemRule(uidArr, 'precise_uid', coverage);
  1037. if (isTip) {
  1038. eventEmitter.send('el-alert', msg);
  1039. return status
  1040. }
  1041. return {status, msg}
  1042. };
  1043. var ruleUtil = {
  1044. addRule,
  1045. showAddRuleInput,
  1046. showDelRuleInput,
  1047. getRuleContent,
  1048. overwriteImportRules,
  1049. appendImportRules,
  1050. overwriteImportRulesV1,
  1051. addRulePreciseUid,
  1052. addRulePreciseName,
  1053. delRUlePreciseUid,
  1054. findRuleItemValue,
  1055. addItemRule,
  1056. addPreciseUidItemRule
  1057. };
  1058. const rule_set_value_dialog = {
  1059. template: `
  1060. <div>
  1061. <el-dialog :visible="show" width="30%" title="修改单项规则值"
  1062. :close-on-click-modal="false" :modal="false">
  1063. {{ ruleName }}-{{ ruleType }}
  1064. <el-form>
  1065. <el-form-item label="要修改的值">
  1066. <el-input type="text" v-model="oldVal" clearable/>
  1067. </el-form-item>
  1068. <el-form-item label="修改后的值">
  1069. <el-input v-model="newVal" clearable/>
  1070. </el-form-item>
  1071. </el-form>
  1072. <template #footer class="dialog-footer">
  1073. <el-button @click="show=false">取消</el-button>
  1074. <el-button @click="okBut">确定</el-button>
  1075. </template>
  1076. </el-dialog>
  1077. </div>`,
  1078. data() {
  1079. return {
  1080. show: false,
  1081. ruleType: "",
  1082. ruleName: "",
  1083. oldVal: '',
  1084. newVal: ''
  1085. }
  1086. },
  1087. methods: {
  1088. okBut() {
  1089. let tempOldVal = this.oldVal.trim();
  1090. let tempNewVal = this.newVal.trim();
  1091. if (tempOldVal.length === 0 || tempNewVal.length === 0) {
  1092. this.$alert("请输入要修改的值或新值");
  1093. return
  1094. }
  1095. if (tempNewVal === tempOldVal) {
  1096. this.$alert("新值不能和旧值相同");
  1097. return;
  1098. }
  1099. const tempRuleType = this.ruleType;
  1100. if (tempRuleType === 'precise_uid' || tempRuleType === 'precise_uid_white') {
  1101. tempOldVal = parseInt(tempOldVal);
  1102. tempNewVal = parseInt(tempNewVal);
  1103. if (isNaN(tempOldVal) || isNaN(tempNewVal)) {
  1104. this.$alert("请输入整数数字");
  1105. return
  1106. }
  1107. }
  1108. if (!ruleUtil.findRuleItemValue(tempRuleType, tempOldVal)) {
  1109. this.$alert("要修改的值不存在");
  1110. return;
  1111. }
  1112. if (ruleUtil.findRuleItemValue(tempRuleType, tempNewVal)) {
  1113. this.$alert("新值已存在");
  1114. return;
  1115. }
  1116. const ruleArr = gmUtil.getData(tempRuleType, []);
  1117. const indexOf = ruleArr.indexOf(tempOldVal);
  1118. ruleArr[indexOf] = tempNewVal;
  1119. gmUtil.setData(tempRuleType, ruleArr);
  1120. this.$alert(`已将旧值【${tempOldVal}】修改成【${tempNewVal}】`);
  1121. this.show = false;
  1122. }
  1123. },
  1124. watch: {
  1125. show(newVal) {
  1126. if (newVal === false) this.oldVal = this.newVal = '';
  1127. }
  1128. },
  1129. created() {
  1130. eventEmitter.on('修改规则对话框', ({type, name}) => {
  1131. this.show = true;
  1132. this.ruleType = type;
  1133. this.ruleName = name;
  1134. });
  1135. }
  1136. };
  1137. const arraysLooseEqual = (arr1, arr2) => {
  1138. if (arr1.length !== arr2.length) return false;
  1139. const countMap = {};
  1140. const getKey = (value) => {
  1141. if (typeof value === 'number' && Number.isNaN(value)) return '__NaN';
  1142. return JSON.stringify(value);
  1143. };
  1144. for (const elem of arr1) {
  1145. const key = getKey(elem);
  1146. countMap[key] = (countMap[key] || 0) + 1;
  1147. }
  1148. for (const elem of arr2) {
  1149. const key = getKey(elem);
  1150. if (!countMap[key]) return false; // 不存在或数量不足
  1151. countMap[key]--;
  1152. }
  1153. return true;
  1154. };
  1155. const arrayContains = (a, b) => {
  1156. if (b.length === 0) return true;
  1157. if (a.length < b.length) return false;
  1158. const countMap = {};
  1159. const getKey = (value) => {
  1160. if (typeof value === 'number' && Number.isNaN(value)) return '__NaN';
  1161. return JSON.stringify(value);
  1162. };
  1163. for (const elem of a) {
  1164. const key = getKey(elem);
  1165. countMap[key] = (countMap[key] || 0) + 1;
  1166. }
  1167. for (const elem of b) {
  1168. const key = getKey(elem);
  1169. if (!countMap[key] || countMap[key] <= 0) return false;
  1170. countMap[key]--;
  1171. }
  1172. return true;
  1173. };
  1174. var arrUtil = {
  1175. arraysLooseEqual,
  1176. arrayContains
  1177. };
  1178. const multiple_rule_edit_dialog_vue = {
  1179. template: `
  1180. <div>
  1181. <el-dialog :visible.sync="dialogVisible" title="多重规则"
  1182. :modal="false"
  1183. :close-on-click-modal="false" :close-on-press-escape="false">
  1184. <el-tag>{{ typeMap.name }}</el-tag>
  1185. <el-card>
  1186. <template #header>说明</template>
  1187. <div>1.组合类型每条项至少大于1</div>
  1188. <div>2.不能添加空项</div>
  1189. <div>3.每组中的项不能超过15个字符</div>
  1190. <div>4.不能重复添加已有的组合</div>
  1191. <div>5.每组不能添加过包括已有的组合</div>
  1192. <div>6.不能添加视频tag(精确匹配)已有的项,如需要,请先移除对应的项!包括视频tag(模糊匹配)</div>
  1193. </el-card>
  1194. <el-card>
  1195. <el-input
  1196. class="input-new-tag"
  1197. v-if="inputVisible"
  1198. v-model="inputValue"
  1199. ref="saveTagInput"
  1200. size="small"
  1201. placeholder="多个项时请用英文符号分割"
  1202. @keyup.enter.native="handleInputConfirm"
  1203. @blur="handleInputConfirm"
  1204. >
  1205. </el-input>
  1206. <el-button v-else size="small" @click="showInput">+ New Tag</el-button>
  1207. <el-tag closable v-for="(item,index) in showTags" @close="handleTagClose(item,index)">{{ item|filterTag }}
  1208. </el-tag>
  1209. </el-card>
  1210. </el-dialog>
  1211. </div>`,
  1212. data() {
  1213. return {
  1214. dialogVisible: false,
  1215. inputVisible: false,
  1216. inputValue: '',
  1217. min: 2,
  1218. typeMap: {},
  1219. showTags: [],
  1220. }
  1221. },
  1222. methods: {
  1223. updateShowTags() {
  1224. this.showTags = ruleKeyListData$1.getVideoTagPreciseCombination();
  1225. },
  1226. handleTagClose(tag, index) {
  1227. if (tag === '') return;
  1228. this.$confirm(`确定要删除 ${tag} 吗?`, '提示', {
  1229. confirmButtonText: '确定',
  1230. cancelButtonText: '取消',
  1231. type: 'warning'
  1232. }).then(() => {
  1233. this.showTags.splice(index, 1);
  1234. ruleKeyListData$1.setVideoTagPreciseCombination(this.showTags);
  1235. this.$message.success(`已移除 ${tag}`);
  1236. eventEmitter.send('刷新规则信息', false);
  1237. });
  1238. },
  1239. showInput() {
  1240. this.inputVisible = true;
  1241. this.$nextTick(_ => {
  1242. this.$refs.saveTagInput.$refs.input.focus();
  1243. });
  1244. },
  1245. handleInputConfirm() {
  1246. let inputValue = this.inputValue;
  1247. this.inputVisible = false;
  1248. if (inputValue === '') return;
  1249. this.submitBut(inputValue);
  1250. this.inputValue = '';
  1251. },
  1252. submitBut(inputValue) {
  1253. const split = inputValue.split(',');
  1254. if (split.length < this.min) {
  1255. this.$message.error('最少添加' + this.min + '项');
  1256. return;
  1257. }
  1258. const preciseVideoTagArr = ruleKeyListData$1.getPreciseVideoTagArr();
  1259. const videoTagArr = ruleKeyListData$1.getVideoTagArr();
  1260. for (let showTag of split) {
  1261. showTag = showTag.trim();
  1262. if (showTag === "") {
  1263. this.$message.error('不能添加空项');
  1264. return;
  1265. }
  1266. if (preciseVideoTagArr.includes(showTag)) {
  1267. this.$message.error('不能添加视频tag(精确匹配)已有的项,请先移除对应的项!');
  1268. return;
  1269. }
  1270. if (videoTagArr.includes(showTag)) {
  1271. this.$message.error('不能添加视频tag(模糊匹配)已有的项,请先移除对应的项!');
  1272. return;
  1273. }
  1274. if (showTag.length > 15) {
  1275. this.$message.error('项不能超过15个字符');
  1276. return;
  1277. }
  1278. }
  1279. const arr = ruleKeyListData$1.getVideoTagPreciseCombination();
  1280. for (let mk_arr of arr) {
  1281. if (arrUtil.arraysLooseEqual(mk_arr, split)) {
  1282. this.$message.error('不能重复添加已有的组合!');
  1283. return
  1284. }
  1285. if (arrUtil.arrayContains(mk_arr, split)) {
  1286. this.$message.error('该组合已添加过或包括该组合');
  1287. return
  1288. }
  1289. }
  1290. arr.push(split);
  1291. ruleKeyListData$1.setVideoTagPreciseCombination(arr);
  1292. console.log(this.typeMap, split, arr);
  1293. this.$message.success(`${this.typeMap.name}添加成功`);
  1294. this.updateShowTags();
  1295. eventEmitter.send('刷新规则信息', false);
  1296. }
  1297. },
  1298. created() {
  1299. eventEmitter.on('打开多重规则编辑对话框', (typeMap) => {
  1300. this.typeMap = typeMap;
  1301. this.dialogVisible = true;
  1302. this.updateShowTags();
  1303. });
  1304. },
  1305. filters: {
  1306. filterTag(tag) {
  1307. return tag.join('||')
  1308. }
  1309. }
  1310. };
  1311. const basic_rules_vue = {
  1312. components: {rule_set_value_dialog, multiple_rule_edit_dialog_vue},
  1313. template: `
  1314. <div>
  1315. <el-card shadow="never">
  1316. <template #header>
  1317. <span>使用说明</span>
  1318. </template>
  1319. <div>1.基础规则类型较多,下拉框支持搜索定位,鼠标点击出现光标时支持筛选</div>
  1320. <div>2.大部分情况下模糊匹配比精确匹配好用</div>
  1321. <div>3.如果可以的话,请优先考虑根据uid精确屏蔽,而非使用用户名相关屏蔽,因用户名可以随意更改</div>
  1322. <div>4.如果用户要添加自己的正则匹配相关的规则时,建议先去该网址进行测试再添加,避免浪费时间
  1323. <el-link href="https://www.jyshare.com/front-end/854/" target="_blank"
  1324. type="primary">>>>正则表达式在线测试<<<
  1325. </el-link>
  1326. </div>
  1327. <div>
  1328. 5.如果更新脚本之后规则全没了,请点击下面的【旧规则自动转新规则】按钮,进行转换,如不行请通过关于和问题反馈选项卡中的反馈渠道联系作者
  1329. </div>
  1330. <div>6.改动实时生效</div>
  1331. <div>7. 分区包括子分区属于视频tag范畴,如需按分区屏蔽在对应视频tag类型添加</div>
  1332. <div>8.
  1333. 基础规则中的项和组合规则互斥,如xxx添加到视频tag多重规则,则不能添加到对应基础规则视频tag,反之同理,限类型,如组合精确匹配
  1334. </div>
  1335. </el-card>
  1336. <el-card shadow="never">
  1337. <template #header>选择规则</template>
  1338. <el-cascader v-model="cascaderVal" @change="handleChangeCascader"
  1339. :options="cascaderOptions" :show-all-levels="false"
  1340. :props="{ expandTrigger: 'hover' }" filterable/>
  1341. <el-divider/>
  1342. <el-row>
  1343. <el-col :span="12">
  1344. <el-button-group>
  1345. <el-button @click="addRuleBut">添加</el-button>
  1346. <el-button @click="setRuleBut">修改</el-button>
  1347. <el-button @click="findItemAllBut">查询</el-button>
  1348. <el-button @click="delBut">移除</el-button>
  1349. </el-button-group>
  1350. </el-col>
  1351. <el-col :span="12">
  1352. <div class="el-horizontal-right">
  1353. <el-button-group>
  1354. <el-button @click="clearItemRuleBut" type="danger">清空项</el-button>
  1355. <el-button type="danger" @click="delAllBut">全部移除</el-button>
  1356. </el-button-group>
  1357. </div>
  1358. </el-col>
  1359. </el-row>
  1360. </el-card>
  1361. <rule_set_value_dialog/>
  1362. <multiple_rule_edit_dialog_vue/>
  1363. </div>`,
  1364. data() {
  1365. return {
  1366. cascaderVal: ["精确匹配", "precise_uid"],
  1367. cascaderOptions: ruleKeyListData$1.getSelectOptions(),
  1368. ruleInfoArr: [],
  1369. }
  1370. },
  1371. methods: {
  1372. handleChangeCascader(val) {
  1373. console.log(val);
  1374. },
  1375. addRuleBut() {
  1376. const [model, mk_type] = this.cascaderVal;
  1377. if (model === '多重匹配') {
  1378. const typeMap = this.ruleInfoArr.find(item => item.type === mk_type);
  1379. eventEmitter.send('打开多重规则编辑对话框', typeMap);
  1380. return
  1381. }
  1382. ruleUtil.showAddRuleInput(mk_type);
  1383. },
  1384. setRuleBut() {
  1385. const [model, type] = this.cascaderVal;
  1386. const typeMap = this.ruleInfoArr.find(item => item.type === type);
  1387. if (model === '多重匹配') {
  1388. eventEmitter.send('打开多重规则编辑对话框', typeMap);
  1389. return
  1390. }
  1391. eventEmitter.send('修改规则对话框', typeMap);
  1392. },
  1393. findItemAllBut() {
  1394. const [model, type] = this.cascaderVal;
  1395. const typeMap = this.ruleInfoArr.find(item => item.type === type);
  1396. if (model === '多重匹配') {
  1397. eventEmitter.send('打开多重规则编辑对话框', typeMap);
  1398. return
  1399. }
  1400. const ruleData = gmUtil.getData(type, []);
  1401. eventEmitter.send('展示内容对话框', JSON.stringify(ruleData, null, 4));
  1402. },
  1403. delAllBut() {
  1404. this.$confirm('确定要删除所有规则吗?').then(() => {
  1405. for (let x of this.ruleInfoArr) {
  1406. gmUtil.delData(x.type);
  1407. }
  1408. this.$alert("删除全部规则成功");
  1409. eventEmitter.send('刷新规则信息');
  1410. });
  1411. },
  1412. delBut() {
  1413. const [model, type] = this.cascaderVal;
  1414. const typeMap = this.ruleInfoArr.find(item => item.type === type);
  1415. if (model === '多重匹配') {
  1416. eventEmitter.send('打开多重规则编辑对话框', typeMap);
  1417. return
  1418. }
  1419. ruleUtil.showDelRuleInput(type);
  1420. },
  1421. clearItemRuleBut() {
  1422. const type = this.cascaderVal[1];
  1423. const find = this.ruleInfoArr.find(item => item.type === type);
  1424. this.$confirm(`是要清空${find.name}的规则内容吗?`, 'tip').then(() => {
  1425. ruleKeyListData$1.clearKeyItem(type);
  1426. this.$alert(`已清空${find.name}的规则内容`);
  1427. });
  1428. }
  1429. },
  1430. watch: {},
  1431. created() {
  1432. for (let newRuleKeyListElement of ruleKeyListData$1.getRuleKeyListData()) {
  1433. this.ruleInfoArr.push({
  1434. type: newRuleKeyListElement.key,
  1435. name: newRuleKeyListElement.name,
  1436. });
  1437. }
  1438. }
  1439. };
  1440. const oldToNewRule = () => {
  1441. const listData = ruleKeyListData$1.getRuleKeyListData().filter(item => item.oldKey);
  1442. for (let data of listData) {
  1443. const oldKeyDataArr = gmUtil.getData(data.oldKey, []);
  1444. if (oldKeyDataArr.length === 0) {
  1445. continue
  1446. }
  1447. const newKeyDataArr = gmUtil.getData(data.key, []);
  1448. if (newKeyDataArr.length === 0) {
  1449. gmUtil.setData(data.key, oldKeyDataArr);
  1450. gmUtil.delData(data.oldKey);
  1451. continue
  1452. }
  1453. for (let v of oldKeyDataArr) {
  1454. const isExist = newKeyDataArr.find(item => item === v);
  1455. if (!isExist) {
  1456. newKeyDataArr.push(v);
  1457. }
  1458. }
  1459. gmUtil.setData(data.key, newKeyDataArr);
  1460. }
  1461. };
  1462. var ruleConversion = {
  1463. oldToNewRule
  1464. };
  1465. const wait = (milliseconds = 1000) => {
  1466. return new Promise(resolve => setTimeout(resolve, milliseconds));
  1467. };
  1468. const fileDownload = (content, fileName) => {
  1469. const element = document.createElement('a');
  1470. element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(content));
  1471. element.setAttribute('download', fileName);
  1472. element.style.display = 'none';
  1473. document.body.appendChild(element);
  1474. element.click();
  1475. document.body.removeChild(element);
  1476. };
  1477. const handleFileReader = (event) => {
  1478. return new Promise((resolve, reject) => {
  1479. const file = event.target.files[0];
  1480. if (!file) {
  1481. reject('未读取到文件');
  1482. return;
  1483. }
  1484. let reader = new FileReader();
  1485. reader.onload = (e) => {
  1486. const fileContent = e.target.result;
  1487. resolve({file, content: fileContent});
  1488. reader = null;
  1489. };
  1490. reader.readAsText(file);
  1491. });
  1492. };
  1493. const isIterable = (obj) => {
  1494. return obj != null && typeof obj[Symbol.iterator] === 'function';
  1495. };
  1496. const toTimeString = () => {
  1497. return new Date().toLocaleString();
  1498. };
  1499. function smoothScroll(toTop = false, duration = 1000) {
  1500. return new Promise((resolve) => {
  1501. const start = window.scrollY;
  1502. const end = toTop ? 0 : document.documentElement.scrollHeight - window.innerHeight;
  1503. const change = end - start;
  1504. const startTime = performance.now();
  1505. function animateScroll(currentTime) {
  1506. const elapsedTime = currentTime - startTime;
  1507. const progress = Math.min(elapsedTime / duration, 1);
  1508. const easeInOutQuad = progress < 0.5 ? 2 * progress * progress : -1 + (4 - 2 * progress) * progress;
  1509. window.scrollTo(0, start + change * easeInOutQuad);
  1510. if (progress < 1) {
  1511. requestAnimationFrame(animateScroll);
  1512. } else {
  1513. resolve();
  1514. }
  1515. }
  1516. requestAnimationFrame(animateScroll);
  1517. });
  1518. }
  1519. function debounce(func, wait = 1000) {
  1520. let timeout;
  1521. return function (...args) {
  1522. const context = this;
  1523. clearTimeout(timeout);
  1524. timeout = setTimeout(() => func.apply(context, args), wait);
  1525. };
  1526. }
  1527. function debounceAsync(asyncFunc, wait = 1000) {
  1528. let timeout;
  1529. let pendingPromise;
  1530. return async function (...args) {
  1531. const context = this;
  1532. if (pendingPromise) {
  1533. clearTimeout(timeout);
  1534. await pendingPromise;
  1535. }
  1536. pendingPromise = new Promise((resolve) => {
  1537. timeout = setTimeout(() => {
  1538. pendingPromise = null; // 清除引用
  1539. resolve(asyncFunc.apply(context, args));
  1540. }, wait);
  1541. });
  1542. return pendingPromise;
  1543. };
  1544. }
  1545. function throttle(func, limit) {
  1546. let inThrottle;
  1547. return function (...args) {
  1548. const context = this;
  1549. if (!inThrottle) {
  1550. func.apply(context, args);
  1551. inThrottle = true;
  1552. setTimeout(() => inThrottle = false, limit);
  1553. }
  1554. };
  1555. }
  1556. function throttleAsync(asyncFunc, limit) {
  1557. let isThrottled = false;
  1558. let pendingArgs = null;
  1559. let pendingContext = null;
  1560. let timeoutId;
  1561. let pendingPromiseResolve;
  1562. const throttled = async function (...args) {
  1563. const context = this;
  1564. if (isThrottled) {
  1565. return new Promise((resolve) => {
  1566. pendingArgs = args;
  1567. pendingContext = context;
  1568. pendingPromiseResolve = resolve;
  1569. });
  1570. }
  1571. isThrottled = true;
  1572. try {
  1573. return await asyncFunc.apply(context, args);
  1574. } finally {
  1575. timeoutId = setTimeout(() => {
  1576. isThrottled = false;
  1577. if (pendingArgs) {
  1578. throttled.apply(pendingContext, pendingArgs).then(pendingPromiseResolve);
  1579. pendingArgs = null;
  1580. pendingContext = null;
  1581. pendingPromiseResolve = null;
  1582. }
  1583. }, limit);
  1584. }
  1585. };
  1586. throttled.cancel = () => {
  1587. clearTimeout(timeoutId);
  1588. isThrottled = false;
  1589. pendingArgs = null;
  1590. pendingContext = null;
  1591. pendingPromiseResolve = null;
  1592. };
  1593. return throttled;
  1594. }
  1595. const parseUrl = (urlString) => {
  1596. const url = new URL(urlString);
  1597. const pathSegments = url.pathname.split('/').filter(segment => segment !== '');
  1598. const searchParams = new URLSearchParams(url.search.slice(1));
  1599. const queryParams = {};
  1600. for (const [key, value] of searchParams.entries()) {
  1601. queryParams[key] = value;
  1602. }
  1603. return {
  1604. protocol: url.protocol,
  1605. hostname: url.hostname,
  1606. port: url.port,
  1607. pathname: url.pathname,
  1608. pathSegments,
  1609. search: url.search,
  1610. queryParams,
  1611. hash: url.hash
  1612. };
  1613. };
  1614. const getLocalStorage = (key, isList = false, defaultValue = null) => {
  1615. const item = localStorage.getItem(key);
  1616. if (item === null) {
  1617. return defaultValue
  1618. }
  1619. if (isList) {
  1620. try {
  1621. return JSON.parse(item)
  1622. } catch (e) {
  1623. console.error(`读取localStorage时尝试转换${key}的值失败`, e);
  1624. return defaultValue
  1625. }
  1626. }
  1627. return item
  1628. };
  1629. const formatTimestamp = (timestamp, options = {}) => {
  1630. if (!timestamp || isNaN(timestamp)) return 'Invalid Timestamp'
  1631. const ts = String(timestamp).length === 10 ? +timestamp * 1000 : +timestamp;
  1632. const timezoneOffset = (options.timezone || 0) * 60 * 60 * 1000;
  1633. const date = new Date(ts + timezoneOffset);
  1634. if (isNaN(date.getTime())) return 'Invalid Date'
  1635. const timeObj = {
  1636. year: date.getUTCFullYear(),
  1637. month: date.getUTCMonth() + 1,
  1638. day: date.getUTCDate(),
  1639. hours: date.getUTCHours(),
  1640. minutes: date.getUTCMinutes(),
  1641. seconds: date.getUTCSeconds()
  1642. };
  1643. if (options.returnObject) return timeObj
  1644. const format = options.format || 'YYYY-MM-DD HH:mm:ss';
  1645. const pad = (n) => n.toString().padStart(2, '0');
  1646. return format
  1647. .replace(/YYYY/g, timeObj.year)
  1648. .replace(/YY/g, String(timeObj.year).slice(-2))
  1649. .replace(/MM/g, pad(timeObj.month))
  1650. .replace(/M/g, timeObj.month)
  1651. .replace(/DD/g, pad(timeObj.day))
  1652. .replace(/D/g, timeObj.day)
  1653. .replace(/HH/g, pad(timeObj.hours))
  1654. .replace(/H/g, timeObj.hours)
  1655. .replace(/mm/g, pad(timeObj.minutes))
  1656. .replace(/m/g, timeObj.minutes)
  1657. .replace(/ss/g, pad(timeObj.seconds))
  1658. .replace(/s/g, timeObj.seconds)
  1659. };
  1660. const calculateLikeRate = (likeCount, viewCount) => {
  1661. if (viewCount === 0) {
  1662. return 0;
  1663. }
  1664. return parseInt((likeCount / viewCount) * 100)
  1665. };
  1666. const calculateInteractionRate = (danmaku, reply, view) => {
  1667. return parseInt((danmaku + reply) / view * 100)
  1668. };
  1669. const calculateTripleRate = (favorite, coin, share, view) => {
  1670. return parseInt((favorite + coin + share) / view * 100)
  1671. };
  1672. const calculateCoinLikesRatioRate = (coin, like) => {
  1673. return parseInt((coin + like) / view * 100)
  1674. };
  1675. var defUtil = {
  1676. wait,
  1677. fileDownload,
  1678. toTimeString,
  1679. smoothScroll,
  1680. debounce,
  1681. debounceAsync,
  1682. throttle,
  1683. throttleAsync,
  1684. parseUrl,
  1685. handleFileReader,
  1686. isIterable,
  1687. getLocalStorage,
  1688. formatTimestamp,
  1689. calculateLikeRate,
  1690. calculateInteractionRate,
  1691. calculateTripleRate,
  1692. calculateCoinLikesRatioRate
  1693. };
  1694. var rule_export_import_vue = {
  1695. template: `
  1696. <div>
  1697. <el-card shadow="never">
  1698. <template #header>
  1699. <span>导出规则</span>
  1700. </template>
  1701. <el-button @click="ruleOutToFIleBut">导出到文件</el-button>
  1702. <el-button @click="outToInputBut">导出到编辑框</el-button>
  1703. <el-button @click="ruleOutToConsoleBut">导出到控制台</el-button>
  1704. </el-card>
  1705. <el-card shadow="never">
  1706. <template #header>
  1707. <el-row>
  1708. <el-col :span="12">
  1709. <div class="el-horizontal-left">导入规则</div>
  1710. </el-col>
  1711. <el-col :span="12">
  1712. <div class="el-horizontal-right">
  1713. <el-button v-for="item in ruleReference" @click="xtipAlertBut(item.content,item.title)">
  1714. {{ item.title }}
  1715. </el-button>
  1716. </div>
  1717. </el-col>
  1718. </el-row>
  1719. </template>
  1720. <div>规则内容请在下面编辑框中导入</div>
  1721. <div>旧版本的需要使用下面的v1旧版本导入规则</div>
  1722. <div>旧版本的只能覆盖导入</div>
  1723. <div>v1之后的版本可以选择覆盖和追加</div>
  1724. <div>旧规则转新规则,用于2.0之前版本升上来旧规则内容丢失问题</div>
  1725. <el-divider/>
  1726. <div>
  1727. <el-button @click="inputFIleRuleBut">读取外部规则文件</el-button>
  1728. <el-button @click="overwriteImportRulesBut">覆盖导入规则</el-button>
  1729. <el-button @click="appendImportRulesBut">追加导入规则</el-button>
  1730. <el-button @click="overwriteImportRulesV1But">v1旧版本覆盖导入规则</el-button>
  1731. <el-button @click="ruleOldToNewBut">旧规则自动转新规则</el-button>
  1732. </div>
  1733. <el-divider/>
  1734. <div>
  1735. <el-input autosize
  1736. :autosize="{ minRows: 10, maxRows: 50}"
  1737. type="textarea" v-model="ruleContentImport" placeholder="要导入的规则内容"></el-input>
  1738. </div>
  1739. </el-card>
  1740. <input ref="file" type="file" accept="application/json" @change="handleFileUpload"
  1741. style="display: none">
  1742. </div>`,
  1743. data() {
  1744. return {
  1745. ruleContentImport: "",
  1746. ruleReference: [
  1747. {
  1748. title: "旧版本规则参考",
  1749. content: ` {"用户名黑名单模式(精确匹配)":["账号已注销"],"BV号黑名单模式(精确匹配)":[],
  1750. "用户名黑名单模式(模糊匹配)":["bili_","_bili"],"用户uid黑名单模式(精确匹配)":[442010132,76525078,225219967,3493283164588093],
  1751. "用户uid白名单模式(精确匹配)":[344490740,1861980711],"标题黑名单模式(模糊匹配)":["激励计划","蚌不住","手游激励","游戏活动打卡"],
  1752. "标题黑名单模式(正则匹配)":["感觉.*不如","不要笑.*挑战"],"评论关键词黑名单模式(模糊匹配)":["感觉不如","差不多的了"],
  1753. "评论关键词黑名单模式(正则匹配)":["这不.+吗","玩.*的","不要笑.*挑战"],"粉丝牌黑名单模式(精确匹配)":[],
  1754. "专栏关键词内容黑名单模式(模糊匹配)":[],"动态关键词内容黑名单模式(模糊匹配)":["拼多多","京东红包","京东618红包","618活动"]}`
  1755. },
  1756. {
  1757. title: "新版本规则参考",
  1758. content: "待补充"
  1759. }
  1760. ],
  1761. }
  1762. },
  1763. methods: {
  1764. overwriteImportRulesBut() {
  1765. this.$confirm('是否要覆盖导入规则?').then(() => {
  1766. const trim = this.ruleContentImport.trim();
  1767. if (ruleUtil.overwriteImportRules(trim)) {
  1768. this.$alert('已覆盖导入成功!');
  1769. eventEmitter.send('刷新规则信息');
  1770. }
  1771. });
  1772. },
  1773. appendImportRulesBut() {
  1774. this.$confirm('是否要追加导入规则?').then(() => {
  1775. const trim = this.ruleContentImport.trim();
  1776. if (ruleUtil.appendImportRules(trim)) {
  1777. this.$message('已追加导入成功!');
  1778. eventEmitter.send('刷新规则信息');
  1779. }
  1780. });
  1781. },
  1782. overwriteImportRulesV1But() {
  1783. this.$confirm('旧版本-是否导入规则?').then(() => {
  1784. const trim = this.ruleContentImport.trim();
  1785. if (ruleUtil.overwriteImportRulesV1(trim)) {
  1786. this.$message('已导入成功!');
  1787. eventEmitter.send('刷新规则信息');
  1788. }
  1789. });
  1790. },
  1791. xtipAlertBut(content, title) {
  1792. this.$alert(content, title);
  1793. },
  1794. ruleOldToNewBut() {
  1795. ruleConversion.oldToNewRule();
  1796. eventEmitter.send('刷新规则信息');
  1797. this.$message('已转换成功!');
  1798. },
  1799. handleFileUpload(event) {
  1800. defUtil.handleFileReader(event).then(data => {
  1801. const {content} = data;
  1802. try {
  1803. JSON.parse(content);
  1804. } catch (e) {
  1805. this.$message('文件内容有误');
  1806. return;
  1807. }
  1808. this.ruleContentImport = content;
  1809. this.$message('读取到内容,请按需覆盖或追加');
  1810. });
  1811. },
  1812. inputFIleRuleBut() {
  1813. this.$refs.file.click();
  1814. },
  1815. outToInputBut() {
  1816. this.ruleContentImport = ruleUtil.getRuleContent(2);
  1817. this.$message('已导出到输入框中');
  1818. },
  1819. ruleOutToFIleBut() {
  1820. let fileName = "b站屏蔽器规则-" + defUtil.toTimeString();
  1821. this.$prompt('请输入文件名', '保存为', {
  1822. inputValue: fileName
  1823. }).then(({value}) => {
  1824. if (value === "" && value.includes(' ')) {
  1825. this.$alert('文件名不能为空或包含空格');
  1826. return
  1827. }
  1828. const ruleContent = ruleUtil.getRuleContent(4);
  1829. defUtil.fileDownload(ruleContent, value + ".json");
  1830. });
  1831. },
  1832. ruleOutToConsoleBut() {
  1833. console.log(ruleUtil.getRuleContent());
  1834. this.$message('已导出到控制台上,F12打开控制台查看');
  1835. },
  1836. },
  1837. };
  1838. const comment_word_limit_vue = {
  1839. template: `
  1840. <div>
  1841. <el-card>
  1842. <template #header>评论字数限制</template>
  1843. <div>超出设置限制的字数时屏蔽(不包括),低于3则不生效</div>
  1844. <div>改动即生效</div>
  1845. <el-input-number v-model="value"/>
  1846. </el-card>
  1847. </div>`,
  1848. data() {
  1849. return {
  1850. value: localMKData.getCommentWordLimitVal()
  1851. }
  1852. },
  1853. watch: {
  1854. value(newVal, oldVal) {
  1855. if (oldVal <= 3) return;
  1856. if (newVal < 3) {
  1857. this.$notify({
  1858. message: '已关闭屏蔽字数限制功能',
  1859. type: 'warning',
  1860. });
  1861. }
  1862. gmUtil.setData('comment_word_limit', newVal);
  1863. }
  1864. }
  1865. };
  1866. var other_parameter_filter = {
  1867. components: {comment_word_limit_vue},
  1868. template: `
  1869. <div>
  1870. <div style="display: flex">
  1871. <div style="width: 70vw">
  1872. <el-card>
  1873. <template #header>
  1874. <span>使用说明</span>
  1875. </template>
  1876. <ol>
  1877. <li>如设置时长相关单位为秒</li>
  1878. <li>如设置播放量和弹幕量相关单位为个</li>
  1879. <li>设置最小播放量则小于该值的视频会屏蔽</li>
  1880. <li>设置最大播放量则大于该值的视频会屏蔽</li>
  1881. <li>设置最小弹幕量则小于该值的视频会屏蔽</li>
  1882. <li>设置最大弹幕量则大于该值的视频会屏蔽</li>
  1883. <li>设置最小时长则小于该值的视频会屏蔽</li>
  1884. <li>设置最大时长则大于该值的视频会屏蔽</li>
  1885. <li>设置最小用户等级则小于该值的会屏蔽,低于该值的会屏蔽掉</li>
  1886. <li>设置最大用户等级则大于该值的会屏蔽,高于该值的会屏蔽掉</li>
  1887. <li>取消相关限制条件则不做限制处理</li>
  1888. <li>右侧信息关键条件-1则为未做任何限制处理</li>
  1889. <li>最后因为设置限制条件冲突或限制太多,视频未能限制的情况下,请按需设置限制条件</li>
  1890. </ol>
  1891. </el-card>
  1892. <input gz_type type="number" :min="inputMin" :max="inputMax" v-model="num">
  1893. <el-select v-model="selectValue" filterable>
  1894. <el-option :value="item.value" v-for="item in selectList" :label="item.name"></el-option>
  1895. </el-select>
  1896. <div>
  1897. <el-button @click="okVideoSelectBut">设置</el-button>
  1898. <el-button @click="cancelBut">取消</el-button>
  1899. <el-button @click="allCancelBut">全部取消</el-button>
  1900. </div>
  1901. </div>
  1902. <div>
  1903. <el-button @click="updateInfoBut">刷新</el-button>
  1904. <div v-for="item in selectList" style="padding: 5px">
  1905. {{ item.name }}{{ item.defVal }}
  1906. {{ item.name.includes('时长') ? '秒' : '' }}
  1907. </div>
  1908. </div>
  1909. </div>
  1910. <comment_word_limit_vue/>
  1911. </div>`,
  1912. data() {
  1913. return {
  1914. num: 0,
  1915. selectList: ruleKeyListData$1.otherKeyListData,
  1916. selectValue: 'nMinimumPlay',
  1917. inputMax: "",
  1918. inputMin: 0
  1919. }
  1920. },
  1921. methods: {
  1922. okVideoSelectBut() {
  1923. const find = this.selectList.find(item => item.value === this.selectValue);
  1924. const associatedVal = gmUtil.getData(find.associated, -1);
  1925. const associatedFind = this.selectList.find(item => item.value === find.associated);
  1926. if (this.num > associatedVal && associatedVal !== -1) {
  1927. if (associatedFind.bLarge) {
  1928. this.$alert(`要设置的${find.name}值不能大于${associatedFind.name}的值`);
  1929. return
  1930. }
  1931. console.log('正常修改');
  1932. }
  1933. this.$alert(`已设置${find.name},值为${this.num}`);
  1934. gmUtil.setData(this.selectValue, this.num);
  1935. this.updateInfo();
  1936. },
  1937. cancelBut() {
  1938. gmUtil.setData(this.selectValue, -1);
  1939. const find = this.selectList.find(item => item.value === this.selectValue);
  1940. this.$alert(`已取消${find.name}的限制`);
  1941. this.updateInfo();
  1942. },
  1943. allCancelBut() {
  1944. for (let item of this.selectList) {
  1945. gmUtil.setData(item.value, -1);
  1946. }
  1947. this.updateInfo();
  1948. },
  1949. updateInfo() {
  1950. for (let item of this.selectList) {
  1951. item.defVal = gmUtil.getData(item.value, -1);
  1952. }
  1953. },
  1954. updateInfoBut() {
  1955. this.updateInfo();
  1956. this.$alert('已刷新');
  1957. },
  1958. },
  1959. watch: {
  1960. selectValue(newVal) {
  1961. const find = this.selectList.find(item => item.value === newVal);
  1962. if (find.name.includes('用户等级')) {
  1963. this.inputMin = 3;
  1964. this.inputMax = 6;
  1965. if (this.num > 6) {
  1966. this.num = 6;
  1967. }
  1968. if (this.num < 3) {
  1969. this.num = 3;
  1970. }
  1971. } else {
  1972. this.inputMin = 0;
  1973. this.inputMax = '';
  1974. }
  1975. }
  1976. },
  1977. created() {
  1978. this.updateInfo();
  1979. }
  1980. };
  1981. var rule_information_vue = {
  1982. template: `
  1983. <div>
  1984. <el-card>
  1985. <template #header>
  1986. <el-button @click="refreshInfoBut">刷新信息</el-button>
  1987. </template>
  1988. <div style=" display: grid;
  1989. grid-template-columns: repeat(4, 1fr);
  1990. gap: 10px;">
  1991. <div v-for="item in ruleInfoArr" :key="item.name">
  1992. <el-badge :value="item.len">
  1993. <el-button>{{ item.name }}</el-button>
  1994. </el-badge>
  1995. </div>
  1996. </div>
  1997. </el-card>
  1998. </div>`,
  1999. data() {
  2000. return {
  2001. ruleInfoArr: [],
  2002. }
  2003. },
  2004. methods: {
  2005. refreshInfo(isTip = true) {
  2006. for (let x of this.ruleInfoArr) {
  2007. x.len = gmUtil.getData(x.type, []).length;
  2008. }
  2009. if (!isTip) return;
  2010. this.$notify({
  2011. title: 'tip',
  2012. message: '刷新规则信息成功',
  2013. type: 'success',
  2014. });
  2015. },
  2016. refreshInfoBut() {
  2017. this.refreshInfo();
  2018. },
  2019. },
  2020. created() {
  2021. for (let newRuleKeyListElement of ruleKeyListData$1.getRuleKeyListData()) {
  2022. this.ruleInfoArr.push({
  2023. type: newRuleKeyListElement.key,
  2024. name: newRuleKeyListElement.name,
  2025. len: 0
  2026. });
  2027. }
  2028. this.refreshInfo(false);
  2029. eventEmitter.on('刷新规则信息', (isTip = true) => {
  2030. this.refreshInfo(isTip);
  2031. });
  2032. }
  2033. };
  2034. class asynchronousIntervalQueue {
  2035. #isProcessing = false;
  2036. #pendingQueue = [];
  2037. #interval = 200;
  2038. constructor(options = {}) {
  2039. this.#interval = options.interval || 200;
  2040. }
  2041. setInterval(interval) {
  2042. this.#interval = interval;
  2043. }
  2044. add(func, config = {}) {
  2045. return new Promise((resolve, reject) => {
  2046. this.#pendingQueue.push({
  2047. funcFn: func,
  2048. config: {
  2049. interval: config.interval || null,
  2050. },
  2051. resolve,
  2052. reject
  2053. });
  2054. if (!this.#isProcessing) {
  2055. this.#processQueue();
  2056. }
  2057. });
  2058. }
  2059. async #processQueue() {
  2060. this.#isProcessing = true;
  2061. while (this.#pendingQueue.length > 0) {
  2062. const task = this.#pendingQueue.shift();
  2063. try {
  2064. let result;
  2065. const funcFn = task.funcFn;
  2066. if (funcFn instanceof Promise) {
  2067. const template = await funcFn;
  2068. if (template instanceof Function) {
  2069. result = template();
  2070. } else {
  2071. result = template;
  2072. }
  2073. }
  2074. if (funcFn instanceof Function) {
  2075. const template = funcFn();
  2076. if (template instanceof Promise) {
  2077. result = await template;
  2078. } else {
  2079. result = template;
  2080. }
  2081. }
  2082. task.resolve(result);
  2083. } catch (error) {
  2084. task.reject(error);
  2085. } finally {
  2086. const interval = task.config.interval || this.#interval;
  2087. await new Promise(resolve =>
  2088. setTimeout(resolve, interval)
  2089. );
  2090. }
  2091. }
  2092. this.#isProcessing = false;
  2093. }
  2094. clearPendingQueue() {
  2095. this.#pendingQueue = [];
  2096. this.#isProcessing = false;
  2097. }
  2098. }
  2099. const requestIntervalQueue = new asynchronousIntervalQueue({
  2100. interval: localMKData.isRequestFrequencyVal() * 1000
  2101. });
  2102. var conditionalityVue = {
  2103. template: `
  2104. <div>
  2105. <el-switch v-model="bOnlyTheHomepageIsBlocked" active-text="仅首页屏蔽生效屏蔽"/>
  2106. <el-tooltip content="模糊和正则匹配时,将匹配词转小写与规则值匹配。修改后刷新页面生效">
  2107. <el-switch v-model="bFuzzyAndRegularMatchingWordsToLowercase"
  2108. active-text="模糊和正则匹配词转小写"/>
  2109. </el-tooltip>
  2110. <el-card>
  2111. <template #header>
  2112. <span>网络请求频率(单位秒)</span>
  2113. <div>如设置0,则为不限制,比如设置2,则为每个请求之间隔2秒,可有效降低对Bapi接口的压力,降低风控</div>
  2114. <div>注意:设置过低可能会导致部分接口风控</div>
  2115. <div>如接口风控了请先勾选下面的【禁用根据bv号网络请求获取视频信息】</div>
  2116. <div>修改实时生效</div>
  2117. </template>
  2118. <el-switch v-model="isDisableNetRequestsBvVideoInfo" active-text="禁用根据bv号网络请求获取视频信息"/>
  2119. <el-slider v-model="requestFrequencyVal" max="5" step="0.1" show-stops show-input
  2120. :disabled="isDisableNetRequestsBvVideoInfo"
  2121. ></el-slider>
  2122. </el-card>
  2123. </div>`,
  2124. data() {
  2125. return {
  2126. requestFrequencyVal: localMKData.isRequestFrequencyVal(),
  2127. bOnlyTheHomepageIsBlocked: globalValue.bOnlyTheHomepageIsBlocked,
  2128. bFuzzyAndRegularMatchingWordsToLowercase: localMKData.bFuzzyAndRegularMatchingWordsToLowercase(),
  2129. isDisableNetRequestsBvVideoInfo: false
  2130. }
  2131. },
  2132. methods: {},
  2133. watch: {
  2134. bOnlyTheHomepageIsBlocked(newVal) {
  2135. gmUtil.setData("bOnlyTheHomepageIsBlocked", newVal === true);
  2136. },
  2137. bFuzzyAndRegularMatchingWordsToLowercase(newVal) {
  2138. gmUtil.setData("bFuzzyAndRegularMatchingWordsToLowercase", newVal === true);
  2139. },
  2140. isDisableNetRequestsBvVideoInfo(b) {
  2141. gmUtil.setData('isDisableNetRequestsBvVideoInfo', b);
  2142. },
  2143. requestFrequencyVal(n) {
  2144. gmUtil.setData('requestFrequencyVal', n > 0 && n <= 5 ? n : 0.2);
  2145. requestIntervalQueue.setInterval(n * 1000);
  2146. }
  2147. },
  2148. created() {
  2149. eventEmitter.on('更新根据bv号网络请求获取视频信息状态', (b) => {
  2150. this.isDisableNetRequestsBvVideoInfo = b;
  2151. });
  2152. }
  2153. };
  2154. const queue = new asynchronousIntervalQueue();
  2155. const getData = async (page = 1) => {
  2156. const response = await fetch(`https://api.bilibili.com/x/relation/blacks?pn=${page}&ps=50&jsonp=jsonp`, {
  2157. credentials: 'include'
  2158. });
  2159. if (response.status !== 200) {
  2160. eventEmitter.send('el-msg', '拉取黑名单数据响应失败.');
  2161. return {state: false}
  2162. }
  2163. const resJson = await response.json();
  2164. const {data: {list, total}, message, code} = resJson;
  2165. if (code !== 0) {
  2166. eventEmitter.send('el-msg', '请求相应内容失败:code=' + code);
  2167. return {state: false, msg: `请求相应内容失败:msg=${message} code=` + code}
  2168. }
  2169. const newList = list.map(({face, mid, mtime, uname, sign}) => {
  2170. return {face, mid, mtime, uname, sign}
  2171. });
  2172. return {state: true, list: newList, total};
  2173. };
  2174. const blacklist_management_vue = {
  2175. template: `
  2176. <div>
  2177. <div>1.注意:该功能为b站自身的黑名单</div>
  2178. <div>1.对应地址
  2179. <el-link target="_blank" href="https://account.bilibili.com/account/blacklist">
  2180. https://account.bilibili.com/account/blacklist
  2181. </el-link>
  2182. </div>
  2183. <div>3.需要登录(不可用)才可以使用</div>
  2184. <el-card shadow="never" v-loading="isDivLoading" element-loading-text="拼命加载中">
  2185. <template #header>
  2186. <el-row>
  2187. <el-col :span="8">
  2188. <el-badge :value="total">
  2189. <el-tag>累计</el-tag>
  2190. </el-badge>
  2191. <el-badge :value="showList.length" style="margin-left: 45px">
  2192. <el-tag>显示数</el-tag>
  2193. </el-badge>
  2194. <div>
  2195. <el-card shadow="never">
  2196. <template #header>请求的间隔({{ sliderInterval }}S)</template>
  2197. <el-slider v-model="sliderInterval" step="0.1" max="10"></el-slider>
  2198. </el-card>
  2199. <el-button @click="getOnePageDataBut">获取第一页</el-button>
  2200. <el-button @click="getAllBut">获取全部</el-button>
  2201. <el-button type="warning" @click="clearTableBut">清空列表</el-button>
  2202. <el-button @click="outDataToConsoleBut">导出控制台</el-button>
  2203. <el-button @click="outDataToFileBut">导出文件</el-button>
  2204. </div>
  2205. </el-col>
  2206. <el-col :span="16">
  2207. <el-card shadow="never">
  2208. <template #header><span>过滤</span></template>
  2209. <div>
  2210. <el-switch v-model="isCancelMaxLimit" active-text="取消列表显示最大限制"/>
  2211. </div>
  2212. <el-select v-model="select.val">
  2213. <el-option
  2214. v-for="item in select.options"
  2215. :key="item.value"
  2216. :label="item.label"
  2217. :value="item.value">
  2218. </el-option>
  2219. </el-select>
  2220. <el-input v-model="findVal"></el-input>
  2221. </el-card>
  2222. </el-col>
  2223. </el-row>
  2224. </template>
  2225. <el-table :data="showList" stripe border>
  2226. <el-table-column prop="mtime" label="时间" width="155px">
  2227. <template v-slot="scope">
  2228. {{ new Date(scope.row.mtime * 1000).toLocaleString() }}
  2229. </template>
  2230. </el-table-column>
  2231. <el-table-column label="头像" width="55px">
  2232. <template v-slot="scope">
  2233. <el-avatar shape="square" :src="scope.row.face"></el-avatar>
  2234. </template>
  2235. </el-table-column>
  2236. <el-table-column prop="uname" label="用户名" width="190px"></el-table-column>
  2237. <el-table-column prop="mid" label="用户ID" width="180px"></el-table-column>
  2238. <el-table-column prop="sign" label="签名"></el-table-column>
  2239. <el-table-column label="标记" width="50px">
  2240. <template v-slot="scope">
  2241. 未定
  2242. </template>
  2243. </el-table-column>
  2244. <el-table-column label="操作">
  2245. <template #header>
  2246. <el-button @click="tableAddUidBlackButAll">一键添加uid屏蔽</el-button>
  2247. </template>
  2248. <template v-slot="scope">
  2249. <el-button @click="tableOpenAddressBut(scope.row)">打开地址</el-button>
  2250. <el-button @click="tableAddUidBlackBut(scope.row)">uid屏蔽</el-button>
  2251. </template>
  2252. </el-table-column>
  2253. </el-table>
  2254. <el-pagination
  2255. :page-size="pageSize"
  2256. background
  2257. layout="prev, pager, next"
  2258. :total="list.length"
  2259. @current-change="handleCurrentChange"
  2260. >
  2261. </el-pagination>
  2262. </el-card>
  2263. </div>`,
  2264. data() {
  2265. return {
  2266. select: {
  2267. val: 'uname',
  2268. options: [{
  2269. label: "用户UID",
  2270. value: 'mid',
  2271. }, {
  2272. label: "用户名",
  2273. value: 'uname',
  2274. }, {
  2275. label: '用户签名',
  2276. value: 'sign'
  2277. }]
  2278. },
  2279. total: 0,
  2280. list: [],
  2281. showList: [],
  2282. findVal: '',
  2283. sliderInterval: 0.6,
  2284. isDivLoading: false,
  2285. isCancelMaxLimit: false,
  2286. pageSize: 50
  2287. }
  2288. },
  2289. methods: {
  2290. filterTable(list, val) {
  2291. const filter = list.filter(x => {
  2292. const x1 = x[this.select.val];
  2293. if (Number.isInteger(x1)) {
  2294. return x1.toString().includes(val)
  2295. }
  2296. return x1.includes(val);
  2297. });
  2298. if (filter.length === 0) {
  2299. this.$notify({
  2300. title: '没有匹配到数据',
  2301. type: 'warning',
  2302. duration: 2000
  2303. });
  2304. return []
  2305. }
  2306. if (filter.length > 50 && !this.isCancelMaxLimit) {
  2307. this.$notify({
  2308. title: '数据过多,已截取前50条',
  2309. type: 'warning',
  2310. duration: 2000
  2311. });
  2312. return filter.slice(0, 50);
  2313. }
  2314. return filter;
  2315. },
  2316. async getOnePageDataBut() {
  2317. const {state, list, total} = await getData();
  2318. if (!state) {
  2319. return
  2320. }
  2321. this.list = list;
  2322. this.showList = list;
  2323. this.total = total;
  2324. this.$message('获取成功');
  2325. },
  2326. tableOpenAddressBut(row) {
  2327. gmUtil.openInTab(`https://space.bilibili.com/${row.mid}`);
  2328. },
  2329. tableAddUidBlackBut(row) {
  2330. const uid = row.mid;
  2331. const name = row.uname;
  2332. if (ruleUtil.findRuleItemValue('precise_uid', uid)) {
  2333. this.$message(`该用户:${name}的uid:${uid}已添加过`);
  2334. return;
  2335. }
  2336. this.$confirm(`确定添加${name}的uid:${uid}到uid精确屏蔽吗?`).then(() => {
  2337. ruleUtil.addRulePreciseUid(uid);
  2338. });
  2339. console.log(row);
  2340. },
  2341. outDataToConsoleBut() {
  2342. console.log('黑名单管理列表====start');
  2343. console.log(JSON.parse(JSON.stringify(this.list)));
  2344. console.log('黑名单管理列表====end');
  2345. this.$alert('已导出到控制台,可通过f12查看');
  2346. },
  2347. outDataToFileBut() {
  2348. this.$prompt('请输入文件名', '保存为', {
  2349. inputValue: 'B站黑名单列表'
  2350. }).then(({value}) => {
  2351. if (value.trim() === '') {
  2352. return
  2353. }
  2354. const tempData = {
  2355. total: this.total,
  2356. list: this.list
  2357. };
  2358. const s = JSON.stringify(tempData, null, 4);
  2359. defUtil.fileDownload(s, +value.trim() + '.json');
  2360. this.$alert('已导出到文件,请按需保存');
  2361. });
  2362. },
  2363. async getAllBut() {
  2364. this.isDivLoading = true;
  2365. const {state, list, total} = await getData();
  2366. if (!state) return
  2367. if (total === 0) {
  2368. this.isDivLoading = false;
  2369. this.$message('没有更多数据了');
  2370. return;
  2371. }
  2372. this.total = total;
  2373. const totalPage = Math.ceil(total / 50);
  2374. if (totalPage === 1) {
  2375. this.list = list;
  2376. this.isDivLoading = false;
  2377. return
  2378. }
  2379. this.list = list;
  2380. for (let i = 2; i <= totalPage; i++) {
  2381. const {state, list: resList} = await queue.add(() => getData(i));
  2382. if (!state) return
  2383. list.push(...resList);
  2384. }
  2385. if (this.list.length > 50 && !this.isCancelMaxLimit) {
  2386. this.showList = list.slice(0, 50);
  2387. } else {
  2388. this.showList = list;
  2389. }
  2390. this.showList = list;
  2391. this.$message('获取成功');
  2392. this.isDivLoading = false;
  2393. },
  2394. handleCurrentChange(page) {
  2395. this.showList = this.list.slice((page - 1) * 50, page * 50);
  2396. },
  2397. clearTableBut() {
  2398. this.showList = this.list = [];
  2399. this.$message('已清空列表');
  2400. },
  2401. tableAddUidBlackButAll() {
  2402. if (this.list.length === 0) {
  2403. this.$message('列表为空');
  2404. return
  2405. }
  2406. this.$confirm(`确定添加所有用户到uid精确屏蔽吗?`).then(() => {
  2407. if (ruleUtil.addPreciseUidItemRule(this.list.map(x => x.mid), true, false)) {
  2408. eventEmitter.send('刷新规则信息');
  2409. }
  2410. });
  2411. }
  2412. },
  2413. watch: {
  2414. findVal(n) {
  2415. this.showList = this.filterTable(this.list, n);
  2416. },
  2417. sliderInterval(n) {
  2418. queue.setInterval(n * 1000);
  2419. },
  2420. isCancelMaxLimit(n) {
  2421. this.pageSize = n ? 1000000 : 50;
  2422. }
  2423. },
  2424. created() {
  2425. queue.setInterval(this.sliderInterval * 1000);
  2426. }
  2427. };
  2428. const uid_range_masking_vue = {
  2429. template: `
  2430. <div>
  2431. <el-card>
  2432. <template #header>
  2433. uid范围屏蔽
  2434. </template>
  2435. <el-switch v-model="status" active-text="启用" style="margin-bottom: 10px"/>
  2436. <div style="margin-bottom: 10px">
  2437. 范围内的uid都会被屏蔽掉,改动需重新设置方可生效,且再下次检查时屏蔽(如视频列表加载,评论加载)。比较关系【最小>=uid<=最大】
  2438. </div>
  2439. <el-form label-position="left" :disabled="!status" style="width: 20%">
  2440. <el-form-item label="最小">
  2441. <el-input v-model.number="head"></el-input>
  2442. </el-form-item>
  2443. <el-form-item label="最大">
  2444. <el-input v-model.number="tail"></el-input>
  2445. </el-form-item>
  2446. <el-form-item class="el-horizontal-right">
  2447. <el-button @click="setRangeBut">设置</el-button>
  2448. </el-form-item>
  2449. </el-form>
  2450. </el-card>
  2451. </div>`,
  2452. data() {
  2453. return {
  2454. status: localMKData.isUidRangeMaskingStatus(),
  2455. head: 0,
  2456. tail: 100
  2457. }
  2458. },
  2459. methods: {
  2460. setRangeBut() {
  2461. this.$alert('设置成功');
  2462. gmUtil.setData('uid_range_masking', [this.head, this.tail]);
  2463. }
  2464. },
  2465. watch: {
  2466. head(newVal, oldVal) {
  2467. if (newVal > this.tail) {
  2468. this.$message('最小值不能大于最大值');
  2469. this.head = oldVal;
  2470. }
  2471. },
  2472. tail(newVal, oldVal) {
  2473. if (newVal < this.head) {
  2474. this.$message('最大值不能小于最小值');
  2475. this.tail = oldVal;
  2476. }
  2477. },
  2478. status(n) {
  2479. gmUtil.setData('uid_range_masking_status', n);
  2480. }
  2481. },
  2482. created() {
  2483. const arr = localMKData.getUidRangeMasking();
  2484. this.head = arr[0];
  2485. this.tail = arr[1];
  2486. }
  2487. };
  2488. const high_level_rule_vue = {
  2489. components: {
  2490. uid_range_masking_vue,
  2491. },
  2492. template: `
  2493. <div>
  2494. <uid_range_masking_vue/>
  2495. <el-card>
  2496. <template #header>视频类型</template>
  2497. <div>选中的类型会被屏蔽</div>
  2498. <el-radio-group v-model="copyrightRadioVal">
  2499. <el-radio-button label="原创"></el-radio-button>
  2500. <el-radio-button label="转载"></el-radio-button>
  2501. <el-radio-button label="不处理"></el-radio-button>
  2502. </el-radio-group>
  2503. <el-divider/>
  2504. <el-switch v-model="is_vertical_val" active-text="屏蔽竖屏类视频"/>
  2505. <el-switch v-model="blockFollowed" active-text="屏蔽已关注"/>
  2506. <el-switch v-model="is_up_owner_exclusive" active-text="屏蔽充电专属视频"></el-switch>
  2507. <el-switch v-model="is_senior_member_val" active-text="屏蔽硬核会员"/>
  2508. <el-row>
  2509. <el-col :span="12">
  2510. <el-card shadow="never">
  2511. <template #header>会员类型屏蔽</template>
  2512. <el-radio-group v-model="vipTypeRadioVal">
  2513. <el-radio-button label="无"></el-radio-button>
  2514. <el-radio-button label="月大会员"></el-radio-button>
  2515. <el-radio-button label="年度及以上大会员"></el-radio-button>
  2516. <el-radio-button label="不处理"></el-radio-button>
  2517. </el-radio-group>
  2518. </el-card>
  2519. </el-col>
  2520. <el-col :span="12">
  2521. <el-card shadow="never">
  2522. <template #header>性别屏蔽</template>
  2523. <el-radio-group v-model="genderRadioVal">
  2524. <el-radio-button label="男性"></el-radio-button>
  2525. <el-radio-button label="女性"></el-radio-button>
  2526. <el-radio-button label="保密"></el-radio-button>
  2527. <el-radio-button label="不处理"></el-radio-button>
  2528. </el-radio-group>
  2529. </el-card>
  2530. </el-col>
  2531. </el-row>
  2532. </el-card>
  2533. <el-card>
  2534. <template #header>计算创作团队</template>
  2535. <el-tooltip content="当作者未匹配上时检查其他成员"></el-tooltip>
  2536. <el-switch v-model="is_check_team_member" active-text="检查创作团队中成员"/>
  2537. </el-card>
  2538. </div>`,
  2539. data() {
  2540. return {
  2541. blockFollowed: localMKData.isBlockFollowed(),
  2542. is_up_owner_exclusive: localMKData.isUpOwnerExclusive(),
  2543. genderRadioVal: localMKData.isGenderRadioVal(),
  2544. vipTypeRadioVal: localMKData.isVipTypeRadioVal(),
  2545. is_senior_member_val: localMKData.isSeniorMember(),
  2546. copyrightRadioVal: localMKData.isCopyrightRadio(),
  2547. is_vertical_val: localMKData.isBlockVerticalVideo(),
  2548. is_check_team_member: localMKData.isCheckTeamMember()
  2549. }
  2550. },
  2551. methods: {},
  2552. watch: {
  2553. blockFollowed(n) {
  2554. gmUtil.setData('blockFollowed', n);
  2555. },
  2556. is_up_owner_exclusive(n) {
  2557. gmUtil.setData('is_up_owner_exclusive', n);
  2558. },
  2559. genderRadioVal(n) {
  2560. gmUtil.setData('genderRadioVal', n);
  2561. },
  2562. vipTypeRadioVal(n) {
  2563. gmUtil.setData('vipTypeRadioVal', n);
  2564. },
  2565. is_senior_member_val(n) {
  2566. gmUtil.setData('is_senior_member', n);
  2567. },
  2568. copyrightRadioVal(n) {
  2569. gmUtil.setData('copyrightRadioVal', n);
  2570. },
  2571. is_vertical_val(n) {
  2572. gmUtil.setData('blockVerticalVideo', n);
  2573. },
  2574. is_check_team_member(n) {
  2575. gmUtil.setData('checkTeamMember', n);
  2576. }
  2577. }
  2578. };
  2579. const card_slider_vue = {
  2580. template: `
  2581. <div>
  2582. <el-card shadow="never">
  2583. <template #header>
  2584. <slot name="header"></slot>
  2585. </template>
  2586. <slot name="describe"></slot>
  2587. <div style="display: flex; align-items: center">
  2588. <el-switch v-model="local_switchVal" :active-text="switchActiveText"/>
  2589. <div style="flex: 1;margin-left: 15px">
  2590. <el-slider v-model="sliderVal" :step="step" :min="min" :max="max" show-input :range="range"
  2591. :format-tooltip="formatTooltip"
  2592. :disabled="disabled"></el-slider>
  2593. </div>
  2594. </div>
  2595. </el-card>
  2596. </div>`,
  2597. props: {
  2598. formatTooltip: {
  2599. type: Function
  2600. },
  2601. switchActiveText: {type: String, default: '启用'},
  2602. step: {type: Number, default: 1},
  2603. min: {type: Number, default: 0},
  2604. max: {type: Number, default: 100},
  2605. value: {type: Number, default: 0},
  2606. switchVal: {type: Boolean, default: false},
  2607. range: {type: Boolean, default: false}
  2608. },
  2609. data() {
  2610. return {
  2611. local_switchVal: this.switchVal,
  2612. disabled: !this.switchVal,
  2613. sliderVal: this.value
  2614. }
  2615. },
  2616. methods: {},
  2617. watch: {
  2618. value(n) {
  2619. this.sliderVal = n;
  2620. },
  2621. sliderVal(n) {
  2622. this.$emit('input', n);
  2623. },
  2624. disabled(n) {
  2625. this.$emit('slider-disabled-change', n);
  2626. },
  2627. switchVal(n) {
  2628. this.local_switchVal = n;
  2629. },
  2630. local_switchVal(n) {
  2631. this.disabled = !n;
  2632. this.$emit('update:switchVal', n);
  2633. }
  2634. }
  2635. };
  2636. const video_metrics_filter_item_vue = {
  2637. components: {card_slider_vue},
  2638. props: {
  2639. headerTitle: {type: String},
  2640. describe: {type: String},
  2641. mkTypeRateKey: {type: String},
  2642. mkRateStatusKey: {type: String},
  2643. },
  2644. template: `
  2645. <div>
  2646. <card_slider_vue v-model="ratioRateVal" :step="0.01" :min="0" :max="1"
  2647. :switch-val.sync="rateBlockingStatus" :format-tooltip="reteFormatTooltip">
  2648. <template #header>{{ headerTitle }}</template>
  2649. <template #describe>{{ describe }}</template>
  2650. </card_slider_vue>
  2651. </div>`,
  2652. data() {
  2653. return {
  2654. rateBlockingStatus: gmUtil.getData(this.mkRateStatusKey, false),
  2655. ratioRateVal: gmUtil.getData(this.mkTypeRateKey, 0.05),
  2656. }
  2657. },
  2658. methods: {
  2659. reteFormatTooltip(val) {
  2660. return (val * 100).toFixed(0) + '%'
  2661. }
  2662. },
  2663. watch: {
  2664. ratioRateVal(n) {
  2665. gmUtil.setData(this.mkTypeRateKey, n);
  2666. },
  2667. rateBlockingStatus(n) {
  2668. gmUtil.setData(this.mkRateStatusKey, n);
  2669. }
  2670. }
  2671. };
  2672. const video_metrics_filter_vue = {
  2673. components: {video_metrics_filter_item_vue},
  2674. template: `
  2675. <div>
  2676. <el-card>
  2677. <template #header>指标屏蔽(改动实时生效)</template>
  2678. <video_metrics_filter_item_vue v-for="item in metricsFilterList" :key="item.headerTitle"
  2679. :header-title="item.headerTitle"
  2680. :describe="item.describe"
  2681. :mk-rate-status-key="item.mkRateStatusKey"
  2682. :mk-type-rate-key="item.mkTypeRateKey"
  2683. />
  2684. </el-card>
  2685. </div>`,
  2686. data() {
  2687. return {
  2688. metricsFilterList: [
  2689. {
  2690. headerTitle: '视频点赞率屏蔽',
  2691. describe: '限制的点赞率,默认为2%,小于或等于值限时制的屏蔽该视频,公式【点赞率=点赞数/播放量*100】',
  2692. mkRateStatusKey: 'video_like_rate_blocking_status',
  2693. mkTypeRateKey: 'video_like_rate'
  2694. },
  2695. {
  2696. headerTitle: '视频互动率屏蔽',
  2697. describe: '限制的占比率,默认为2%,小于或等于值限时制的屏蔽该视频,公式【(弹幕数+评论数)/播放数*100】',
  2698. mkRateStatusKey: 'interactive_rate_blocking_status',
  2699. mkTypeRateKey: 'interactive_rate'
  2700. },
  2701. {
  2702. headerTitle: '视频三连率屏蔽',
  2703. describe: '限制的占比率,默认为2%,小于或等于值限时制的屏蔽该视频,公式【(收藏数+投币数+分享数)/播放数*100】',
  2704. mkRateStatusKey: 'triple_rate_blocking_status',
  2705. mkTypeRateKey: 'triple_rate'
  2706. },
  2707. {
  2708. headerTitle: '视频投币/点赞比(内容价值)屏蔽',
  2709. describe: '限制的占比率,默认为2%,小于或等于值限时制的屏蔽该视频,投币成本较高,比值越高内容越优质。公式【投币数 / 获赞数】',
  2710. mkRateStatusKey: 'coin_likes_ratio_rate_blocking_status',
  2711. mkTypeRateKey: 'coin_likes_ratio_rate'
  2712. }
  2713. ]
  2714. }
  2715. }
  2716. };
  2717. const saveTable = (tableData) => {
  2718. const newList = [];
  2719. for (let {status, r} of tableData) {
  2720. if (r === null) {
  2721. eventEmitter.send('el-alert', '表格内还有未设置时间范围的项,请先设置或删除才可以保存!');
  2722. return
  2723. }
  2724. const [startTime, endTime] = r;
  2725. newList.push({
  2726. status,
  2727. r: [startTime.getTime(), endTime.getTime()]
  2728. });
  2729. }
  2730. if (newList.length === 0) return;
  2731. gmUtil.setData('time_range_masking', newList);
  2732. eventEmitter.send('el-notify', {
  2733. title: '保存成功',
  2734. message: '已保存该时间范围屏蔽',
  2735. type: 'success'
  2736. });
  2737. };
  2738. const time_range_masking_table_vue = {
  2739. template: `
  2740. <div>
  2741. <el-table :data="tableData" stripe border>
  2742. <el-table-column label="状态" width="120px">
  2743. <template v-slot="scope">
  2744. <el-switch v-model="scope.row.status" active-text="启用" @change="tableSwitchChange(scope.row)"/>
  2745. </template>
  2746. </el-table-column>
  2747. <el-table-column label="时间范围" width="400px">
  2748. <template v-slot="scope">
  2749. <el-date-picker
  2750. @change="tableDatePickerChange(scope.row)"
  2751. v-model="scope.row.r"
  2752. type="datetimerange"
  2753. :picker-options="pickerOptions"
  2754. range-separator="至"
  2755. start-placeholder="开始日期"
  2756. end-placeholder="结束日期">
  2757. </el-date-picker>
  2758. <el-tag>{{ scope.row.r }}</el-tag>
  2759. </template>
  2760. </el-table-column>
  2761. <el-table-column>
  2762. <template #header>
  2763. <el-button type="info" @click="addBut">添加</el-button>
  2764. <el-button @click="refreshTableData">刷新</el-button>
  2765. <el-button @click="saveTableBut">保存</el-button>
  2766. </template>
  2767. <template v-slot="scope">
  2768. <el-button type="warning" @click="delItemBut(scope.row)">删除</el-button>
  2769. </template>
  2770. </el-table-column>
  2771. </el-table>
  2772. </div>`,
  2773. data() {
  2774. return {
  2775. tableData: [],
  2776. pickerOptions: {
  2777. shortcuts: [
  2778. {
  2779. text: '最近一周',
  2780. onClick(picker) {
  2781. const end = new Date();
  2782. const start = new Date();
  2783. start.setTime(start.getTime() - 3600 * 1000 * 24 * 7);
  2784. picker.$emit('pick', [start, end]);
  2785. }
  2786. },
  2787. {
  2788. text: '最近一个月',
  2789. onClick(picker) {
  2790. const end = new Date();
  2791. const start = new Date();
  2792. start.setTime(start.getTime() - 3600 * 1000 * 24 * 30);
  2793. picker.$emit('pick', [start, end]);
  2794. }
  2795. },
  2796. {
  2797. text: '最近三个月',
  2798. onClick(picker) {
  2799. const end = new Date();
  2800. const start = new Date();
  2801. start.setTime(start.getTime() - 3600 * 1000 * 24 * 90);
  2802. picker.$emit('pick', [start, end]);
  2803. }
  2804. }
  2805. ]
  2806. }
  2807. }
  2808. },
  2809. methods: {
  2810. refreshTableData() {
  2811. if (this.tableData.length > 0) {
  2812. this.tableData.splice(0, this.tableData.length);
  2813. }
  2814. const timeRangeMaskingArr = localMKData.getTimeRangeMaskingArr();
  2815. if (timeRangeMaskingArr.length !== 0) {
  2816. let index = 0;
  2817. for (let {status, r} of timeRangeMaskingArr) {
  2818. this.tableData.push({
  2819. index: index++,
  2820. status,
  2821. r: [new Date(r[0]), new Date(r[1])],
  2822. startTimeStamp: r[0],
  2823. endTimeStamp: r[1],
  2824. });
  2825. }
  2826. }
  2827. },
  2828. restoreTheLastTimeRange(row) {
  2829. let {startTimeStamp, endTimeStamp} = row;
  2830. console.log('上次时间戳', startTimeStamp, endTimeStamp);
  2831. if (startTimeStamp === null || startTimeStamp === undefined) {
  2832. row.r = null;
  2833. return
  2834. }
  2835. row.r = [new Date(startTimeStamp), new Date(endTimeStamp)];
  2836. console.log('已恢复上次时间范围', row);
  2837. },
  2838. tableDatePickerChange(row) {
  2839. const rowR = row.r;
  2840. if (rowR === null) return
  2841. let {oldStartTimeStamp, oldEndTimeStamp} = row;
  2842. const newStartTimeStamp = rowR[0].getTime();
  2843. const newEndTimeStamp = rowR[1].getTime();
  2844. const comparisonSTS = newStartTimeStamp || oldStartTimeStamp;
  2845. const comparisonETS = newEndTimeStamp || oldEndTimeStamp;
  2846. for (let v of this.tableData) {
  2847. if (v.r === null) continue;
  2848. if (v.index === row.index) continue;
  2849. const tempStartTimeStamp = v.r[0].getTime();
  2850. const tempEndTimeStamp = v.r[1].getTime();
  2851. if (tempStartTimeStamp === comparisonSTS && tempEndTimeStamp === comparisonETS) {
  2852. this.$alert('已存在该时间范围屏蔽');
  2853. this.restoreTheLastTimeRange(row);
  2854. return;
  2855. }
  2856. if (comparisonSTS >= tempStartTimeStamp && comparisonETS <= tempEndTimeStamp) {
  2857. this.$alert('小于已添加过的时间范围');
  2858. this.restoreTheLastTimeRange(row);
  2859. return;
  2860. }
  2861. }
  2862. row.startTimeStamp = newStartTimeStamp;
  2863. row.endTimeStamp = newEndTimeStamp;
  2864. saveTable(this.tableData);
  2865. },
  2866. tableSwitchChange(row) {
  2867. if (row.r === null) return
  2868. saveTable(this.tableData);
  2869. },
  2870. addBut() {
  2871. const length = this.tableData.length;
  2872. this.tableData.push({
  2873. index: length, status: true, r: null,
  2874. startTimeStamp: null, endTimeStamp: null
  2875. });
  2876. this.$notify({message: '已添加一条时间范围屏蔽到底部'});
  2877. },
  2878. delItemBut(row) {
  2879. if (row.startTimeStamp === null) {
  2880. this.tableData.splice(row.index, 1);
  2881. return;
  2882. }
  2883. for (let {r} of this.tableData) {
  2884. if (r === null) {
  2885. this.$alert('表格内还有未设置时间范围的项,请先设置或删除才可以保存!');
  2886. return
  2887. }
  2888. }
  2889. this.$confirm('确定删除该条时间范围屏蔽吗?', '提示', {
  2890. confirmButtonText: '确定',
  2891. cancelButtonText: '取消',
  2892. type: 'warning'
  2893. }).then(() => {
  2894. this.tableData.splice(row.index, 1);
  2895. saveTable(this.tableData);
  2896. this.$message({
  2897. type: 'success',
  2898. message: '删除成功!'
  2899. });
  2900. });
  2901. },
  2902. saveTableBut() {
  2903. saveTable(this.tableData);
  2904. }
  2905. },
  2906. created() {
  2907. this.refreshTableData();
  2908. }
  2909. };
  2910. const time_range_masking_vue = {
  2911. components: {time_range_masking_table_vue},
  2912. template: `
  2913. <div>
  2914. <el-card>
  2915. <template #header>时间范围</template>
  2916. <div>使用说明</div>
  2917. <div>1.不能添加重复的时间范围或者小于已添加过的时间范围</div>
  2918. <div>2.修改时间范围的值和状态会自动保存(包括删除),会有提示</div>
  2919. <div>3.每个时间范围可独立控制开关状态,关闭则该条范围不生效</div>
  2920. <div>4.总开关优先级最高,关闭则所有时间范围不生效</div>
  2921. <el-switch v-model="status" active-text="总开关"/>
  2922. </el-card>
  2923. <time_range_masking_table_vue/>
  2924. </div>`,
  2925. data() {
  2926. return {
  2927. status: localMKData.isTimeRangeMaskingStatus()
  2928. }
  2929. },
  2930. methods: {
  2931. },
  2932. watch: {
  2933. status(n) {
  2934. this.$notify({
  2935. message: n ? '时间范围屏蔽已开启' : '时间范围屏蔽已关闭',
  2936. type: n ? 'success' : 'warning'
  2937. });
  2938. gmUtil.setData('time_range_masking_status', n);
  2939. }
  2940. },
  2941. created() {
  2942. }
  2943. };
  2944. const repl_processing_vue = {
  2945. template: `
  2946. <div>
  2947. <el-card shadow="never">
  2948. <template #header>说明
  2949. <el-row>
  2950. <el-col :span="12">
  2951. <div>1.评论内容暂不支持替换表情</div>
  2952. <div>2.如修改后或添加数据需保存方可生效</div>
  2953. <div>3.暂不支持标题替换</div>
  2954. <div>4.支持正则替换,
  2955. <el-link target="_blank" type="primary"
  2956. href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/String/replaceAll">
  2957. 详情参考js中的replaceAll用法
  2958. </el-link>
  2959. </div>
  2960. <div>5.搜索暂时先用快捷键ctrl+f代替</div>
  2961. <div>6.作用域中,选择了评论表情再选其他之前需要取消选项评论表情</div>
  2962. <div>7.评论表情,查找时要用英文输入法[]包裹表情关键词,留空为评论中移除该表情。反之替换普通文本内容。
  2963. <el-link target="_blank" href="https://docs.qq.com/doc/DSlJNR1NVcGR3eEto"
  2964. title="页面中用搜索定位表情包对照表" type="primary">表情包对照表
  2965. </el-link>
  2966. </div>
  2967. </el-col>
  2968. <el-col :span="12">
  2969. <el-card shadow="never">
  2970. <template #header>全局</template>
  2971. <el-tooltip content="当该选项未启用时下面表格中的不生效">
  2972. <el-switch active-text="启用" v-model="enableReplacementProcessingVal"/>
  2973. </el-tooltip>
  2974. <el-switch v-model="clearCommentEmoticonsVal" active-text="清除评论表情"/>
  2975. <el-tooltip content="将评论中的蓝色关键词带搜索小图标的内容替换成普通文本内容">
  2976. <el-switch v-model="isReplaceCommentSearchTermsVal" active-text="替换评论搜索词"/>
  2977. </el-tooltip>
  2978. </el-card>
  2979. </el-col>
  2980. </el-row>
  2981. </template>
  2982. </el-card>
  2983. <el-table :data="tableData" border stripe>
  2984. <el-table-column label="作用域" width="450px">
  2985. <template v-slot="scope">
  2986. <el-checkbox-group v-model="scope.row.actionScopes" @change="actionScopesChange">
  2987. <el-checkbox disabled border label="视频标题"/>
  2988. <el-checkbox border label="评论内容"/>
  2989. <el-checkbox border label="评论表情"/>
  2990. </el-checkbox-group>
  2991. </template>
  2992. </el-table-column>
  2993. <el-table-column label="查找">
  2994. <template v-slot="scope">
  2995. <el-input v-model="scope.row.findVal" maxlength="10" clearable @change="verifyDuplicate"/>
  2996. </template>
  2997. </el-table-column>
  2998. <el-table-column label="替换">
  2999. <template v-slot="scope">
  3000. <el-input v-model="scope.row.replaceVal" maxlength="10" clearable/>
  3001. </template>
  3002. </el-table-column>
  3003. <el-table-column label="操作">
  3004. <template #header>
  3005. <el-button @click="addBut">添加</el-button>
  3006. <el-button @click="refreshBut">刷新</el-button>
  3007. <el-button type="success" @click="saveBut">保存</el-button>
  3008. </template>
  3009. <template v-slot="scope">
  3010. <el-button type="warning" @click="delItemBut(scope.row,scope.$index)">删除</el-button>
  3011. </template>
  3012. </el-table-column>
  3013. </el-table>
  3014. </div>`,
  3015. data() {
  3016. return {
  3017. tableData: getSubstituteWordsArr(),
  3018. enableReplacementProcessingVal: enableReplacementProcessing(),
  3019. clearCommentEmoticonsVal: isClearCommentEmoticons(),
  3020. isReplaceCommentSearchTermsVal: isReplaceCommentSearchTerms()
  3021. }
  3022. },
  3023. methods: {
  3024. validate(item) {
  3025. if (item.actionScopes.length === 0) {
  3026. this.$message.error('请选择作用域再后续处理');
  3027. return
  3028. }
  3029. if (item.findVal === '') {
  3030. this.$message.error('请输入查找内容再后续处理');
  3031. return
  3032. }
  3033. return true
  3034. },
  3035. verifyDuplicate(val) {
  3036. if (val === '') return;
  3037. const set = new Set();
  3038. for (const v of this.tableData) {
  3039. if (set.has(v.findVal)) {
  3040. this.$alert(`已添加过该查找值,不可重复添加【${v.findVal}】`, '错误', {
  3041. type: 'error'
  3042. });
  3043. return;
  3044. }
  3045. set.add(v.findVal);
  3046. }
  3047. },
  3048. addBut() {
  3049. this.tableData.unshift({
  3050. actionScopes: ['评论内容'],
  3051. findVal: '',
  3052. replaceVal: ''
  3053. });
  3054. this.$notify({message: '已添加一条替换处理到顶部'});
  3055. },
  3056. delItemBut(row, index) {
  3057. if (row.findVal === '' && row.replaceVal === '') {
  3058. this.tableData.splice(index, 1);
  3059. this.$notify({message: '已删除一条替换处理'});
  3060. return;
  3061. }
  3062. if (this.validate(row) !== true) return;
  3063. this.$confirm('确定删除该条替换处理吗?', '提示', {
  3064. confirmButtonText: '确定',
  3065. cancelButtonText: '取消',
  3066. type: 'warning'
  3067. }).then(() => {
  3068. this.tableData.splice(index, 1);
  3069. this.$notify({message: '已删除一条替换处理'});
  3070. });
  3071. },
  3072. refreshBut() {
  3073. this.tableData = getSubstituteWordsArr();
  3074. this.$message.info('已刷新');
  3075. },
  3076. saveBut() {
  3077. if (this.tableData.length === 0) {
  3078. this.$message.error('请先添加数据再保存!');
  3079. return
  3080. }
  3081. for (let item of this.tableData) {
  3082. if (this.validate(item) !== true) return;
  3083. }
  3084. const duplicateRemoval = new Set();
  3085. for (const v of this.tableData) {
  3086. if (duplicateRemoval.has(v.findVal)) {
  3087. this.$alert(`查找内容不能重复【${v.findVal}】`, '错误', {
  3088. type: 'error'
  3089. });
  3090. return;
  3091. }
  3092. duplicateRemoval.add(v.findVal);
  3093. }
  3094. gmUtil.setData('substitute_words', this.tableData);
  3095. this.$message.success('已保存');
  3096. },
  3097. actionScopesChange(newArr) {
  3098. if (newArr.length === 0) return;
  3099. if (newArr.some(v => v === '评论表情')) {
  3100. newArr.splice(0, newArr.length, '评论表情');
  3101. }
  3102. }
  3103. },
  3104. watch: {
  3105. clearCommentEmoticonsVal(n) {
  3106. gmUtil.setData('is_clear_comment_emoticons', n);
  3107. },
  3108. isReplaceCommentSearchTermsVal(n) {
  3109. gmUtil.setData('is_replace_comment_search_terms', n);
  3110. }
  3111. }
  3112. };
  3113. var ruleManagementVue = {
  3114. components: {
  3115. rule_export_import_vue,
  3116. other_parameter_filter,
  3117. rule_information_vue,
  3118. conditionalityVue,
  3119. basic_rules_vue,
  3120. blacklist_management_vue,
  3121. high_level_rule_vue,
  3122. video_metrics_filter_vue,
  3123. time_range_masking_vue,
  3124. repl_processing_vue
  3125. },
  3126. template: `
  3127. <div>
  3128. <el-tabs type="border-card" tab-position="left">
  3129. <el-tab-pane label="基础规则">
  3130. <basic_rules_vue/>
  3131. </el-tab-pane>
  3132. <el-tab-pane label="高级规则" lazy>
  3133. <high_level_rule_vue/>
  3134. </el-tab-pane>
  3135. <el-tab-pane label="其他规则" lazy>
  3136. <other_parameter_filter/>
  3137. </el-tab-pane>
  3138. <el-tab-pane label="指标屏蔽" lazy>
  3139. <video_metrics_filter_vue/>
  3140. </el-tab-pane>
  3141. <el-tab-pane label="范围屏蔽" lazy>
  3142. <time_range_masking_vue/>
  3143. </el-tab-pane>
  3144. <el-tab-pane label="替换处理" lazy>
  3145. <repl_processing_vue/>
  3146. </el-tab-pane>
  3147. <el-tab-pane label="导出导入" lazy>
  3148. <rule_export_import_vue/>
  3149. </el-tab-pane>
  3150. <el-tab-pane label="条件限制" lazy>
  3151. <conditionalityVue/>
  3152. </el-tab-pane>
  3153. <el-tab-pane label="规则信息">
  3154. <rule_information_vue/>
  3155. </el-tab-pane>
  3156. <el-tab-pane label="黑名单管理" lazy>
  3157. <blacklist_management_vue/>
  3158. </el-tab-pane>
  3159. </el-tabs>
  3160. </div>`,
  3161. data() {
  3162. return {}
  3163. }
  3164. };
  3165. const compatible_setting_vue = {
  3166. template: `
  3167. <div>
  3168. <el-card>
  3169. <template #header>说明</template>
  3170. <div>如果用户没有安装并使用对应脚本或插件,就不要开启相关兼容选项</div>
  3171. </el-card>
  3172. <el-card>
  3173. <template #header>Bilibili-Gate脚本(bilibili-app-recommend)</template>
  3174. <el-switch v-model="adaptationBAppRecommend" active-text="首页屏蔽适配"/>
  3175. </el-card>
  3176. <el-card>
  3177. <template #header>BewlyBewly插件</template>
  3178. <el-switch v-model="compatible_BEWLY_BEWLY" active-text="首页适配"/>
  3179. </el-card>
  3180. <el-card>
  3181. <template #header>评论区</template>
  3182. 使用之后需刷新对应页面才可生效,勾选即评论区使用新版获取方式,不再使用旧版方式
  3183. <div>
  3184. <el-switch v-model="discardOldCommentAreasV" active-text="弃用旧版评论区处理"/>
  3185. </div>
  3186. </el-card>
  3187. </div>`,
  3188. data() {
  3189. return {
  3190. adaptationBAppRecommend: globalValue.adaptationBAppCommerce,
  3191. compatible_BEWLY_BEWLY: globalValue.compatibleBEWLYBEWLY,
  3192. discardOldCommentAreasV: localMKData.isDiscardOldCommentAreas()
  3193. }
  3194. },
  3195. watch: {
  3196. adaptationBAppRecommend(newVal) {
  3197. gmUtil.setData("adaptation-b-app-recommend", newVal === true);
  3198. },
  3199. compatible_BEWLY_BEWLY(newVal) {
  3200. gmUtil.setData("compatible_BEWLY_BEWLY", newVal === true);
  3201. },
  3202. discardOldCommentAreasV(newVal) {
  3203. gmUtil.setData("discardOldCommentAreas", newVal === true);
  3204. }
  3205. }
  3206. };
  3207. const mk_db = new Dexie('mk-db');
  3208. mk_db.version(1).stores({
  3209. videoInfos: 'bv,tags,userInfo,videoInfo',
  3210. });
  3211. const addVideoData = async (bv, data) => {
  3212. const {tags, userInfo, videoInfo} = data;
  3213. try {
  3214. await mk_db.videoInfos.add({
  3215. bv, tags, userInfo, videoInfo
  3216. });
  3217. } catch (e) {
  3218. console.log(`添加视频数据失败`, data, e);
  3219. return false
  3220. }
  3221. return true
  3222. };
  3223. const bulkImportVideoInfos = async (friendsData) => {
  3224. try {
  3225. const lastKeyItem = await mk_db.videoInfos.bulkPut(friendsData);
  3226. console.info('批量导入成功,最后一个插入的主键:', lastKeyItem);
  3227. return {state: true, lastKeyItem}
  3228. } catch (error) {
  3229. console.error('批量导入时出错:', error);
  3230. return {state: false, error}
  3231. }
  3232. };
  3233. const getVideoInfo = async () => {
  3234. return await mk_db.videoInfos.toArray()
  3235. };
  3236. const getVideoInfoCount = async () => {
  3237. return await mk_db.videoInfos.count()
  3238. };
  3239. const findVideoInfoByBv = async (bv) => {
  3240. return await mk_db.videoInfos.get(bv)
  3241. };
  3242. const clearVideoInfosTable = async () => {
  3243. try {
  3244. await mk_db.videoInfos.clear();
  3245. return true
  3246. } catch (e) {
  3247. console.log('清除videoInfos表失败', e);
  3248. return false
  3249. }
  3250. };
  3251. const delVideoInfoItem = async (bv) => {
  3252. try {
  3253. const item = await findVideoInfoByBv(bv);
  3254. if (!item) return false;
  3255. await mk_db.videoInfos.delete(bv);
  3256. return true
  3257. } catch (e) {
  3258. return false
  3259. }
  3260. };
  3261. const bulkDelVideoInfoItem = async (bvArr) => {
  3262. const data = {state: false, success: [], fail: []};
  3263. try {
  3264. const existingItem = await mk_db.videoInfos.bulkGet(bvArr);
  3265. const existingKeys = existingItem.filter(item => item).map(item => item.bv);
  3266. if (existingKeys.length === 0) {
  3267. data.fail = bvArr;
  3268. return data
  3269. }
  3270. data.state = true;
  3271. data.success.push(...existingKeys);
  3272. if (existingKeys.length !== bvArr.length) {
  3273. data.fail.push(...bvArr.filter(item => !existingKeys.includes(item)));
  3274. }
  3275. await mk_db.videoInfos.bulkDelete(bvArr);
  3276. return data
  3277. } catch (e) {
  3278. console.log('批量删除数据库中指定bv号失败:', e);
  3279. return data
  3280. }
  3281. };
  3282. var bvDexie = {
  3283. addVideoData,
  3284. clearVideoInfosTable,
  3285. bulkImportVideoInfos,
  3286. getVideoInfo,
  3287. getVideoInfoCount,
  3288. delVideoInfoItem,
  3289. bulkDelVideoInfoItem
  3290. };
  3291. const cache_management_vue = {
  3292. template: `
  3293. <div>
  3294. <el-card>
  3295. <template #header>说明</template>
  3296. <div>1.每个域名中的缓存数据不同</div>
  3297. <div>2.仅仅支持导入json格式</div>
  3298. <div>3.下面导入默认追加模式</div>
  3299. <div>4.当前域名
  3300. <el-tag>{{ hostname }}</el-tag>
  3301. </div>
  3302. </el-card>
  3303. <el-card>
  3304. <template #header>操作</template>
  3305. <el-button @click="inputFIleBut">追加导入视频缓存数据</el-button>
  3306. <input ref="inputDemo" type="file" @change="handleFileUpload" accept="application/json"
  3307. style="display: none">
  3308. <el-button @click="clearPageVideoCacheDataBut">清空当前域名的视频缓存数据</el-button>
  3309. <el-button @click="lookContentBut">查看内容</el-button>
  3310. <el-button @click="lookContentLenBut">查看数据量</el-button>
  3311. <el-button type="warning" @click="batchDelBut">批量删除</el-button>
  3312. </el-card>
  3313. <el-card>
  3314. <template #header>导出</template>
  3315. <el-button @click="outDbDataBut">导出至文件</el-button>
  3316. <el-button @click="outToConsoleBut">导出至控制台</el-button>
  3317. </el-card>
  3318. </div>`,
  3319. data() {
  3320. return {
  3321. hostname: window.location.hostname
  3322. }
  3323. },
  3324. methods: {
  3325. outDbDataBut() {
  3326. bvDexie.getVideoInfo().then((data) => {
  3327. if (data.length === 0) {
  3328. this.$message('当前域名下没有缓存视频数据');
  3329. return
  3330. }
  3331. data = {
  3332. hostName: this.hostname,
  3333. size: data.length,
  3334. data: data
  3335. };
  3336. defUtil.fileDownload(JSON.stringify(data, null, 4), 'mk-db-videoInfos-cache.json');
  3337. this.$message('已导出当前域名的缓存数据');
  3338. console.log(data);
  3339. });
  3340. },
  3341. handleFileUpload(event) {
  3342. defUtil.handleFileReader(event).then(data => {
  3343. const {content} = data;
  3344. let parse;
  3345. try {
  3346. parse = JSON.parse(content);
  3347. } catch (e) {
  3348. this.$message('文件内容有误');
  3349. return;
  3350. }
  3351. const {hostName = null, videoInfos = []} = parse;
  3352. if (!hostName) {
  3353. this.$message('hostName字段不存在');
  3354. return;
  3355. }
  3356. if (!defUtil.isIterable(videoInfos)) {
  3357. this.$message('文件内容有误,非可迭代的数组!');
  3358. return;
  3359. }
  3360. if (videoInfos.length === 0) {
  3361. this.$message('tags数据为空');
  3362. return;
  3363. }
  3364. for (let item of videoInfos) {
  3365. if (!item['bv']) {
  3366. this.$message('bv字段不存在');
  3367. return;
  3368. }
  3369. if (!item['tags']) {
  3370. this.$message('tags字段不存在');
  3371. return;
  3372. }
  3373. if (!item['userInfo']) {
  3374. this.$message('userInfo字段不存在');
  3375. return;
  3376. }
  3377. if (!item['videoInfo']) {
  3378. this.$message('videoInfo字段不存在');
  3379. return;
  3380. }
  3381. }
  3382. bvDexie.bulkImportVideoInfos(videoInfos).then((bool) => {
  3383. if (bool) {
  3384. this.$message('导入成功');
  3385. } else {
  3386. this.$message('导入失败');
  3387. }
  3388. });
  3389. });
  3390. },
  3391. inputFIleBut() {
  3392. this.$refs.inputDemo.click();
  3393. },
  3394. clearPageVideoCacheDataBut() {
  3395. this.$confirm('是否清空当前域名下的tags数据').then(() => {
  3396. bvDexie.clearVideoInfosTable().then((bool) => {
  3397. if (bool) {
  3398. this.$message('已清空当前域名下的视频缓存数据');
  3399. } else {
  3400. this.$message('清空失败');
  3401. }
  3402. });
  3403. });
  3404. },
  3405. lookContentBut() {
  3406. this.$confirm('当数据量过大时,可能卡顿,等待时间会较为长,是要继续吗').then(async () => {
  3407. const loading = this.$loading({text: "获取中..."});
  3408. const r = await bvDexie.getVideoInfo();
  3409. loading.close();
  3410. eventEmitter.send('展示内容对话框', JSON.stringify(r));
  3411. this.$message('获取成功');
  3412. });
  3413. },
  3414. outToConsoleBut() {
  3415. bvDexie.getVideoInfo().then(r => {
  3416. this.$alert('已导出至控制台上,可通过f12等方式查看');
  3417. const hostname = this.hostname;
  3418. console.log(`${hostname}的视频数据===start`);
  3419. console.log(r);
  3420. console.log(`${hostname}的视频数据=====end`);
  3421. });
  3422. },
  3423. lookContentLenBut() {
  3424. bvDexie.getVideoInfoCount().then((len) => {
  3425. this.$alert(`数据量${len}`);
  3426. });
  3427. },
  3428. batchDelBut() {
  3429. this.$prompt('请输入删除的bv号,多个bv号用逗号隔开', '提示', {
  3430. confirmButtonText: '确定',
  3431. cancelButtonText: '取消',
  3432. }).then(async ({value}) => {
  3433. value = value?.trim() || null || "";
  3434. if (value === null) return;
  3435. const bvs = value.split(',');
  3436. if (bvs.length === 1) {
  3437. const bool = await bvDexie.delVideoInfoItem(bvs[0]);
  3438. if (bool) {
  3439. this.$message.success(`删除${value}的视频缓存数据成功`);
  3440. } else {
  3441. this.$message.warning(`删除失败,未找到${value}的视频缓存数据`);
  3442. }
  3443. return
  3444. }
  3445. const data = await bvDexie.bulkDelVideoInfoItem(bvs);
  3446. if (data.state) {
  3447. if (data.success.length === bvs.length) {
  3448. this.$alert(`删除${data.success.join(',')}的视频缓存数据成功`, {
  3449. type: 'success'
  3450. });
  3451. } else {
  3452. this.$alert(`删除${data.success.join(',')}的视频缓存数据成功,${data.fail.join(',')}的视频缓存数据未找到`, {
  3453. type: 'warning'
  3454. });
  3455. }
  3456. } else {
  3457. this.$message.warning(`删除失败,错误信息请看控制台`);
  3458. }
  3459. });
  3460. }
  3461. },
  3462. created() {
  3463. }
  3464. };
  3465. var donateLayoutVue = {
  3466. template: `
  3467. <div>
  3468. <el-card shadow="hover">
  3469. <template #header>
  3470. <span>零钱赞助</span>
  3471. </template>
  3472. <span>1元不嫌少,10元不嫌多,感谢支持!</span>
  3473. <el-divider/>
  3474. <span>生活不易,作者叹息</span>
  3475. <el-divider/>
  3476. <span>用爱发电不容易,您的支持是我最大的更新动力</span>
  3477. </el-card>
  3478. <el-divider/>
  3479. <div class="el-vertical-center" @click="gotoAuthorBut">
  3480. <el-avatar size="large"
  3481. src="//i0.hdslb.com/bfs/face/87e9c69a15f7d2b68294be165073c8e07a541e28.jpg@128w_128h_1c_1s.webp"></el-avatar>
  3482. </div>
  3483. <div class="el-vertical-center">
  3484. <el-button round type="primary" @click="showDialogBut">打赏点猫粮</el-button>
  3485. </div>
  3486. <el-dialog
  3487. center
  3488. :title="dialogIni.title"
  3489. :visible.sync="dialogIni.show">
  3490. <div class="el-vertical-center">
  3491. <el-image v-for="item in list"
  3492. :src="item.src"
  3493. style="height: 300px"
  3494. :preview-src-list="dialogIni.srcList"/>
  3495. </div>
  3496. </el-dialog>
  3497. </div>`,
  3498. data() {
  3499. return {
  3500. list: [
  3501. {
  3502. name: "支付宝赞助",
  3503. alt: "支付宝支持",
  3504. src: "https://www.mikuchase.ltd/img/paymentCodeZFB.webp"
  3505. },
  3506. {name: "微信赞助", alt: "微信支持", src: "https://www.mikuchase.ltd/img/paymentCodeWX.webp"},
  3507. {name: "QQ赞助", alt: "QQ支持", src: "https://www.mikuchase.ltd/img/paymentCodeQQ.webp"},
  3508. ],
  3509. dialogIni: {
  3510. title: "打赏点猫粮",
  3511. show: false,
  3512. srcList: []
  3513. }
  3514. }
  3515. },
  3516. methods: {
  3517. showDialogBut() {
  3518. this.dialogIni.show = true;
  3519. },
  3520. gotoAuthorBut() {
  3521. gmUtil.openInTab(globalValue.b_url);
  3522. }
  3523. },
  3524. created() {
  3525. this.dialogIni.srcList = this.list.map(x => x.src);
  3526. }
  3527. };
  3528. const outputInformationFontColor$1 = localMKData.getOutputInformationFontColor();
  3529. const highlightInformationColor$1 = localMKData.getHighlightInformationColor();
  3530. var outputInformationVue = {
  3531. template: `
  3532. <div>
  3533. <el-button type="info" @click="clearInfoBut">清空消息</el-button>
  3534. <div v-for="item in outputInfoArr" v-html="item"></div>
  3535. </div>`,
  3536. data() {
  3537. return {
  3538. outputInfoArr: [],
  3539. }
  3540. },
  3541. methods: {
  3542. clearInfoBut() {
  3543. this.$confirm('是否清空信息', '提示', {
  3544. confirmButtonText: '确定',
  3545. cancelButtonText: '取消',
  3546. type: 'warning'
  3547. }).then(() => {
  3548. this.outputInfoArr = [];
  3549. this.$message('已清空信息');
  3550. });
  3551. }
  3552. },
  3553. created() {
  3554. eventEmitter.on('打印信息', (content) => {
  3555. const liEL = document.createElement("li");
  3556. liEL.innerHTML = content;
  3557. this.outputInfoArr.push(liEL.innerHTML);
  3558. });
  3559. eventEmitter.on('屏蔽视频信息', (type, matching, videoData) => {
  3560. const toTimeString = defUtil.toTimeString();
  3561. const {name, uid, title, videoUrl} = videoData;
  3562. const info = `<b style="color: ${outputInformationFontColor$1}; " gz_bezel>
  3563. ${toTimeString}-根据${type}-${matching ? `<b style="color: ${highlightInformationColor$1}">【${matching}】</b>` : ""}-屏蔽用户【${name}】uid=
  3564. <a href="https://space.bilibili.com/${uid}"
  3565. style="color: ${highlightInformationColor$1}"
  3566. target="_blank">【${uid}】</a>
  3567. 标题【<a href="${videoUrl}" target="_blank" style="color: ${highlightInformationColor$1}">${title}</a>】
  3568. </b>`;
  3569. this.outputInfoArr.push(info);
  3570. });
  3571. eventEmitter.on('屏蔽评论信息', (type, matching, commentData) => {
  3572. const toTimeString = defUtil.toTimeString();
  3573. const {name, uid, content} = commentData;
  3574. this.outputInfoArr.push(`<b style="color: ${outputInformationFontColor$1}; " gz_bezel>
  3575. ${toTimeString}-根据${type}-${matching ? `<b style="color: ${highlightInformationColor$1}">【${matching}】</b>` : ""}-屏蔽用户【${name}】uid=
  3576. <a href="https://space.bilibili.com/${uid}"
  3577. style="color: ${highlightInformationColor$1}"
  3578. target="_blank">【${uid}】</a>
  3579. 评论【${content}】
  3580. </b>`);
  3581. });
  3582. eventEmitter.on('正则匹配时异常', (errorData) => {
  3583. const {msg, e} = errorData;
  3584. this.outputInfoArr.push(msg);
  3585. console.error(msg);
  3586. throw new Error(e)
  3587. });
  3588. }
  3589. };
  3590. const look_content_dialog_vue = {
  3591. template: `
  3592. <div>
  3593. <el-dialog
  3594. :fullscreen="true"
  3595. title="提示"
  3596. :visible.sync="dialogVisible"
  3597. width="30%"
  3598. :before-close="handleClose">
  3599. <el-input autosize
  3600. type="textarea"
  3601. v-model="content"></el-input>
  3602. <span slot="footer" class="dialog-footer">
  3603. <el-button @click="dialogVisible = false">取 消</el-button>
  3604. <el-button type="primary" @click="dialogVisible = false">确 定</el-button>
  3605. </span>
  3606. </el-dialog>
  3607. </div>`,
  3608. data() {
  3609. return {
  3610. dialogVisible: false,
  3611. content: ''
  3612. }
  3613. },
  3614. methods: {
  3615. handleClose(done) {
  3616. this.$confirm('确认关闭?')
  3617. .then(_ => {
  3618. done();
  3619. })
  3620. .catch(_ => {
  3621. });
  3622. }
  3623. },
  3624. created() {
  3625. eventEmitter.on('展示内容对话框', (newContent) => {
  3626. this.content = newContent;
  3627. this.$message('已更新内容');
  3628. this.dialogVisible = true;
  3629. });
  3630. }
  3631. };
  3632. class ValueCache {
  3633. #mapCache = new Map();
  3634. set(key, value) {
  3635. this.#mapCache.set(key, value);
  3636. return value;
  3637. }
  3638. get(key, defaultValue = null) {
  3639. const newVar = this.#mapCache.get(key);
  3640. if (newVar) {
  3641. return newVar;
  3642. }
  3643. return defaultValue;
  3644. }
  3645. getAll() {
  3646. return this.#mapCache;
  3647. }
  3648. }
  3649. const valueCache = new ValueCache();
  3650. var video_zone = {
  3651. "动画": [
  3652. "MAD·AMV",
  3653. "MMD·3D",
  3654. "短片·手书",
  3655. "配音",
  3656. "手办·模玩",
  3657. "特摄",
  3658. "动漫杂谈"
  3659. ],
  3660. "番剧": [
  3661. "资讯",
  3662. "官方延伸",
  3663. "完结动画"
  3664. ],
  3665. "国创": [
  3666. "国产动画",
  3667. "国产原创相关",
  3668. "布袋戏",
  3669. "资讯"
  3670. ],
  3671. "音乐": [
  3672. "原创音乐",
  3673. "翻唱",
  3674. "VOCALOID·UTAU",
  3675. "演奏",
  3676. "MV",
  3677. "音乐现场",
  3678. "音乐综合",
  3679. "乐评盘点",
  3680. "音乐教学"
  3681. ],
  3682. "舞蹈": [
  3683. "宅舞",
  3684. "舞蹈综合",
  3685. "舞蹈教程",
  3686. "街舞",
  3687. "明星舞蹈",
  3688. "国风舞蹈"
  3689. ],
  3690. "游戏": [
  3691. "单机游戏",
  3692. "电子竞技",
  3693. "手机游戏",
  3694. "网络游戏",
  3695. "桌游棋牌",
  3696. "GMV",
  3697. "音游"
  3698. ],
  3699. "知识": [
  3700. "科学科普",
  3701. "社科·法律·心理(原社科人文、原趣味科普人文)",
  3702. "人文历史",
  3703. "财经商业",
  3704. "校园学习",
  3705. "职业职场",
  3706. "设计·创意",
  3707. "野生技术协会",
  3708. "演讲·公开课(已下线)",
  3709. "星海(已下线)"
  3710. ],
  3711. "科技": [
  3712. "数码(原手机平板)",
  3713. "软件应用",
  3714. "计算机技术",
  3715. "科工机械 (原工业·工程·机械)",
  3716. "极客DIY",
  3717. "电脑装机(已下线)",
  3718. "摄影摄像(已下线)"
  3719. ],
  3720. "运动": [
  3721. "篮球",
  3722. "足球",
  3723. "健身",
  3724. "竞技体育",
  3725. "运动文化"
  3726. ],
  3727. "汽车": [
  3728. "汽车知识科普",
  3729. "赛车",
  3730. "改装玩车",
  3731. "新能源车",
  3732. "房车",
  3733. "摩托车",
  3734. "购车攻略",
  3735. "汽车生活",
  3736. "汽车文化(已下线)",
  3737. "汽车极客(已下线)"
  3738. ],
  3739. "生活": [
  3740. "搞笑",
  3741. "出行",
  3742. "三农",
  3743. "家居房产",
  3744. "手工",
  3745. "绘画",
  3746. "日常",
  3747. "亲子",
  3748. "美食圈(重定向)",
  3749. "动物圈(重定向)",
  3750. "运动(重定向)",
  3751. "汽车(重定向)"
  3752. ],
  3753. "美食": [
  3754. "美食制作(原[生活]->[美食圈])",
  3755. "美食侦探",
  3756. "美食测评",
  3757. "田园美食"
  3758. ],
  3759. "动物圈": [
  3760. "喵星人",
  3761. "汪星人",
  3762. "动物二创",
  3763. "野生动物",
  3764. "小宠异宠"
  3765. ],
  3766. "鬼畜": [
  3767. "鬼畜调教",
  3768. "音MAD",
  3769. "人力VOCALOID",
  3770. "鬼畜剧场"
  3771. ],
  3772. "时尚": [
  3773. "美妆护肤",
  3774. "仿妆cos",
  3775. "穿搭",
  3776. "时尚潮流",
  3777. "健身(重定向)"
  3778. ],
  3779. "资讯": [
  3780. "热点",
  3781. "环球",
  3782. "社会"
  3783. ],
  3784. "广告": [],
  3785. "娱乐": [
  3786. "综艺",
  3787. "娱乐杂谈",
  3788. "粉丝创作",
  3789. "明星综合"
  3790. ],
  3791. "影视": [
  3792. "影视杂谈",
  3793. "影视剪辑",
  3794. "小剧场",
  3795. "预告·资讯"
  3796. ],
  3797. "纪录片": [
  3798. "人文·历史",
  3799. "科学·探索·自然",
  3800. "军事"
  3801. ],
  3802. "电影": [
  3803. "华语电影",
  3804. "欧美电影",
  3805. "日本电影"
  3806. ],
  3807. "电视剧": [
  3808. "国产剧"
  3809. ]
  3810. };
  3811. const findKey = (itemKey) => {
  3812. for (let key in video_zone) {
  3813. const arr = video_zone[key];
  3814. if (arr.some((i) => i === itemKey)) return key;
  3815. }
  3816. return null;
  3817. };
  3818. var video_zoneData = {findKey};
  3819. const fetchGetBarrageBlockingWords = () => {
  3820. return new Promise((resolve, reject) => {
  3821. fetch('https://api.bilibili.com/x/dm/filter/user', {
  3822. credentials: 'include'
  3823. })
  3824. .then(response => response.json())
  3825. .then(({code, data, message}) => {
  3826. if (code !== 0) {
  3827. reject({state: false, msg: `请求相应内容失败:msg=${message} code=` + code});
  3828. return
  3829. }
  3830. const {rule} = data;
  3831. const list = [];
  3832. for (let r of rule) {
  3833. const {type, filter, ctime} = r;
  3834. if (type === 2) {
  3835. continue
  3836. }
  3837. list.push({type, filter, ctime});
  3838. }
  3839. resolve({state: true, data, list, msg: '获取成功'});
  3840. });
  3841. })
  3842. };
  3843. const fetchGetVideoInfo = async (bvId) => {
  3844. const response = await fetch(`https://api.bilibili.com/x/web-interface/view/detail?bvid=${bvId}`);
  3845. if (response.status !== 200) {
  3846. eventEmitter.send('请求获取视频信息失败', response, bvId);
  3847. return {state: false, msg: '网络请求失败', data: response}
  3848. }
  3849. const {code, data, message} = await response.json();
  3850. const defData = {state: false, msg: '默认失败信息'};
  3851. if (code !== 0) {
  3852. defData.msg = message;
  3853. return defData
  3854. }
  3855. defData.state = true;
  3856. defData.msg = '获取成功';
  3857. const {
  3858. View: {
  3859. staff,
  3860. tname,
  3861. tname_v2,
  3862. desc,
  3863. pubdate,
  3864. ctime,
  3865. copyright,
  3866. is_upower_exclusive,
  3867. duration,
  3868. dimension,
  3869. stat: {
  3870. view,
  3871. danmaku,
  3872. reply,
  3873. favorite,
  3874. coin,
  3875. share,
  3876. like
  3877. },
  3878. }, Card: {
  3879. follower,
  3880. like_num,
  3881. archive_count,
  3882. following,
  3883. article_count, card: {
  3884. mid: uid,
  3885. name,
  3886. sex, level_info: {
  3887. current_level
  3888. },
  3889. pendant,
  3890. nameplate,
  3891. Official,
  3892. official_verify,
  3893. vip,
  3894. sign,
  3895. is_senior_member
  3896. }
  3897. }, Tags,
  3898. participle
  3899. } = data;
  3900. const videoInfo = {
  3901. staff,
  3902. tname,
  3903. tname_v2,
  3904. desc,
  3905. pubdate,
  3906. ctime,
  3907. copyright,
  3908. is_upower_exclusive,
  3909. duration,
  3910. view,
  3911. danmaku,
  3912. reply,
  3913. favorite,
  3914. coin,
  3915. share,
  3916. participle,
  3917. dimension,
  3918. like
  3919. };
  3920. const userInfo = {
  3921. follower,
  3922. like_num,
  3923. archive_count,
  3924. article_count,
  3925. Official,
  3926. official_verify,
  3927. vip,
  3928. uid,
  3929. name,
  3930. sex,
  3931. current_level,
  3932. pendant,
  3933. nameplate,
  3934. following,
  3935. sign,
  3936. is_senior_member
  3937. };
  3938. const tags = [];
  3939. for (let tag of Tags) {
  3940. tags.push(tag['tag_name']);
  3941. }
  3942. tags.unshift(tname, tname_v2);
  3943. const findKey = video_zoneData.findKey(tname);
  3944. if (findKey) {
  3945. tags.unshift(findKey);
  3946. }
  3947. defData.data = {videoInfo, userInfo, tags};
  3948. return defData
  3949. };
  3950. var bFetch = {
  3951. fetchGetVideoInfo,
  3952. fetchGetBarrageBlockingWords
  3953. };
  3954. const bAfterLoadingThePageOpenMainPanel = () => {
  3955. return gmUtil.getData('bAfterLoadingThePageOpenMainPanel', false)
  3956. };
  3957. const setBAfterLoadingThePageOpenMainPanel = (b) => {
  3958. gmUtil.setData('bAfterLoadingThePageOpenMainPanel', b === true);
  3959. };
  3960. const debugger_management_vue = {
  3961. template: `
  3962. <div>
  3963. <el-tabs tab-position="left">
  3964. <el-tab-pane label="基础">
  3965. <el-card shadow="never">
  3966. <template #header><span>测试</span></template>
  3967. <el-button @click="demoBut">测试网络请求</el-button>
  3968. <el-button @click="fetchGetVideoInfoBut">请求获取视频信息</el-button>
  3969. <el-button @click="printValueCacheBut">打印valueCache值</el-button>
  3970. <el-button @click="printEventBut">打印事件中心值</el-button>
  3971. <el-button @click="printReqIntervalQueueVal">打印requestIntervalQueue值</el-button>
  3972. <el-divider/>
  3973. <el-switch v-model="bAfterLoadingThePageOpenMainPanel" active-text="加载完页面打开主面板"/>
  3974. </el-card>
  3975. </el-tab-pane>
  3976. </el-tabs>
  3977. </div>`,
  3978. data() {
  3979. return {
  3980. bAfterLoadingThePageOpenMainPanel: bAfterLoadingThePageOpenMainPanel()
  3981. }
  3982. },
  3983. methods: {
  3984. printValueCacheBut() {
  3985. console.log(valueCache.getAll());
  3986. },
  3987. demoBut() {
  3988. bFetch.fetchGetVideoInfo('BV152cWeXEhW').then(data => {
  3989. console.log(data);
  3990. debugger
  3991. });
  3992. },
  3993. fetchGetVideoInfoBut() {
  3994. this.$prompt('请输入视频bv号', {
  3995. title: '请输入视频bv号',
  3996. confirmButtonText: '确定',
  3997. cancelButtonText: '取消',
  3998. inputPattern: /^BV[A-Za-z0-9]{10}$/,
  3999. inputErrorMessage: '请输入正确的视频bv号'
  4000. }).then(({value}) => {
  4001. bFetch.fetchGetVideoInfo(value).then(data => {
  4002. console.log(data);
  4003. debugger
  4004. });
  4005. });
  4006. },
  4007. printEventBut() {
  4008. console.log(eventEmitter.getEvents());
  4009. },
  4010. printReqIntervalQueueVal() {
  4011. console.log(requestIntervalQueue);
  4012. }
  4013. },
  4014. watch: {
  4015. bAfterLoadingThePageOpenMainPanel(b) {
  4016. setBAfterLoadingThePageOpenMainPanel(b);
  4017. }
  4018. }
  4019. };
  4020. const getUrlUID = (url) => {
  4021. let uid;
  4022. if (url.startsWith('http')) {
  4023. const parseUrl = defUtil.parseUrl(url);
  4024. uid = parseUrl.pathSegments[0]?.trim();
  4025. return parseInt(uid)
  4026. }
  4027. const isDoYouHaveAnyParameters = url.indexOf('?');
  4028. const lastIndexOf = url.lastIndexOf("/");
  4029. if (isDoYouHaveAnyParameters === -1) {
  4030. if (url.endsWith('/')) {
  4031. const nTheIndexOfTheLastSecondOccurrenceOfTheSlash = url.lastIndexOf('/', url.length - 2);
  4032. uid = url.substring(nTheIndexOfTheLastSecondOccurrenceOfTheSlash + 1, url.length - 1);
  4033. } else {
  4034. uid = url.substring(lastIndexOf + 1);
  4035. }
  4036. } else {
  4037. uid = url.substring(lastIndexOf + 1, isDoYouHaveAnyParameters);
  4038. }
  4039. return parseInt(uid);
  4040. };
  4041. const getUrlBV = (url) => {
  4042. let match = url.match(/video\/(.+)\//);
  4043. if (match === null) {
  4044. match = url.match(/video\/(.+)\?/);
  4045. }
  4046. if (match === null) {
  4047. match = url.match(/video\/(.+)/);
  4048. }
  4049. return match?.[1]?.trim() || null;
  4050. };
  4051. function findElementUntilFound(selector, config = {}) {
  4052. const defConfig = {
  4053. doc: document,
  4054. interval: 1000,
  4055. timeout: -1,
  4056. };
  4057. config = {...defConfig, ...config};
  4058. return new Promise((resolve, reject) => {
  4059. const i1 = setInterval(() => {
  4060. const element = config.doc.querySelector(selector);
  4061. if (element) {
  4062. resolve(element);
  4063. clearInterval(i1);
  4064. }
  4065. }, config.interval);
  4066. if (config.timeout > 0) {
  4067. setTimeout(() => {
  4068. clearInterval(i1);
  4069. reject(null); // 超时则返回 null
  4070. }, config.timeout);
  4071. }
  4072. });
  4073. }
  4074. const findElement = async (selector, config = {}) => {
  4075. try {
  4076. const defConfig = {
  4077. doc: document,
  4078. interval: 1000,
  4079. timeout: -1,
  4080. };
  4081. config = {...defConfig, ...config};
  4082. const el = await findElementUntilFound(selector, config);
  4083. if (config.timeout === -1) {
  4084. return el
  4085. }
  4086. return {state: true, data: el}
  4087. } catch (e) {
  4088. return {state: false, data: e}
  4089. }
  4090. };
  4091. const findElements = async (selector, config = {}) => {
  4092. const defConfig = {doc: document, interval: 1000, timeout: -1};
  4093. config = {...defConfig, ...config};
  4094. try {
  4095. const elList = await findElementsUntilFound(selector, config);
  4096. if (config.timeout === -1) {
  4097. return elList
  4098. }
  4099. return {state: true, data: elList}
  4100. } catch (e) {
  4101. return {state: false, data: e}
  4102. }
  4103. };
  4104. function findElementsUntilFound(selector, config = {}) {
  4105. const defConfig = {doc: document, interval: 1000, timeout: -1};
  4106. config = {...defConfig, ...config};
  4107. return new Promise((resolve, reject) => {
  4108. const i1 = setInterval(() => {
  4109. const elements = config.doc.querySelectorAll(selector);
  4110. if (elements.length > 0) {
  4111. resolve(Array.from(elements));
  4112. clearInterval(i1);
  4113. }
  4114. }, config.interval);
  4115. if (config.timeout > 0) {
  4116. setTimeout(() => {
  4117. clearInterval(i1);
  4118. reject(null); // 超时则返回 null
  4119. }, config.timeout);
  4120. }
  4121. });
  4122. }
  4123. const findElementsAndBindEvents = (css, callback, config = {}) => {
  4124. config = {
  4125. ...{
  4126. interval: 2000,
  4127. timeOut: 3000
  4128. }, config
  4129. };
  4130. setTimeout(() => {
  4131. findElementUntilFound(css, {interval: config.interval}).then((el) => {
  4132. el.addEventListener("click", () => {
  4133. callback();
  4134. });
  4135. });
  4136. }, config.timeOut);
  4137. };
  4138. const updateCssVModal = () => {
  4139. findElement('.v-modal').then(() => {
  4140. const styleEl = document.createElement('style');
  4141. styleEl.innerHTML = `.v-modal {
  4142. z-index: auto !important;
  4143. }`;
  4144. document.head.appendChild(styleEl);
  4145. });
  4146. };
  4147. var elUtil = {
  4148. getUrlUID,
  4149. getUrlBV,
  4150. findElement,
  4151. findElements,
  4152. findElementUntilFound,
  4153. findElementsUntilFound,
  4154. findElementsAndBindEvents,
  4155. updateCssVModal
  4156. };
  4157. const setTopInputPlaceholder = async () => {
  4158. if (globalValue.compatibleBEWLYBEWLY) {
  4159. return
  4160. }
  4161. const placeholder = valueCache.get('topInputPlaceholder');
  4162. if (placeholder === null) {
  4163. return
  4164. }
  4165. const targetInput = await elUtil.findElement('.nav-search-input');
  4166. targetInput.placeholder = placeholder;
  4167. eventEmitter.send('el-notify', {
  4168. title: "tip",
  4169. message: '已恢复顶部搜索框提示内容',
  4170. position: 'bottom-right',
  4171. });
  4172. };
  4173. const processTopInputContent = async () => {
  4174. if (globalValue.compatibleBEWLYBEWLY) {
  4175. return
  4176. }
  4177. if (!gmUtil.getData('isClearTopInputTipContent', false)) {
  4178. return;
  4179. }
  4180. const targetInput = await elUtil.findElement('.nav-search-input');
  4181. if (targetInput.placeholder === '') {
  4182. await defUtil.wait(1500);
  4183. await processTopInputContent();
  4184. return
  4185. }
  4186. valueCache.set('topInputPlaceholder', targetInput.placeholder);
  4187. targetInput.placeholder = '';
  4188. eventEmitter.send('el-msg', '清空了搜索框提示内容');
  4189. };
  4190. eventEmitter.on('执行清空顶部搜索框提示内容', () => {
  4191. processTopInputContent();
  4192. });
  4193. var topInput = {processTopInputContent, setTopInputPlaceholder};
  4194. const page_processing_vue = {
  4195. template: `
  4196. <div>
  4197. <el-card>
  4198. <template #header>
  4199. <span>搜索页</span>
  4200. </template>
  4201. <el-switch v-model="isRemoveSearchBottomContent"
  4202. active-text="屏蔽底部额外内容"/>
  4203. </el-card>
  4204. <el-card>
  4205. <template #header>
  4206. <span>播放页</span>
  4207. </template>
  4208. <el-switch v-model="isDelPlayerPageAd" active-text="屏蔽页面元素广告"/>
  4209. <el-switch v-model="isDelPlayerPageRightGameAd" active-text="屏蔽右侧游戏推荐"/>
  4210. <el-tooltip content="移除整个推荐列表,状态刷新生效">
  4211. <el-switch v-model="isDelPlayerPageRightVideoList" active-text="移除右侧推荐列表"/>
  4212. </el-tooltip>
  4213. <el-tooltip content="状态刷新生效">
  4214. <el-switch v-model="isDelBottomComment" active-text="移除评论区"/>
  4215. </el-tooltip>
  4216. <el-tooltip content="视频播放完之后会在播放器上显示推荐内容,开启之后移除播放器上整个推荐内容">
  4217. <el-switch v-model="isDelPlayerEndingPanelVal" active-text="移除播放完推荐层"/>
  4218. </el-tooltip>
  4219. </el-card>
  4220. <el-card>
  4221. <template #header>
  4222. <span>顶部搜索框</span>
  4223. </template>
  4224. <el-switch v-model="isClearTopInputTipContent" active-text="清空内容"/>
  4225. </el-card>
  4226. </div>`,
  4227. data() {
  4228. return {
  4229. isRemoveSearchBottomContent: gmUtil.getData('isRemoveSearchBottomContent', false),
  4230. isDelPlayerPageAd: gmUtil.getData('isDelPlayerPageAd', false),
  4231. isDelPlayerPageRightGameAd: gmUtil.getData('isDelPlayerPageRightGameAd', false),
  4232. isDelPlayerPageRightVideoList: localMKData.isDelPlayerPageRightVideoList(),
  4233. isDelBottomComment: localMKData.isDelBottomComment(),
  4234. isClearTopInputTipContent: gmUtil.getData('isClearTopInputTipContent', false),
  4235. isDelPlayerEndingPanelVal: localMKData.isDelPlayerEndingPanel()
  4236. }
  4237. },
  4238. methods: {},
  4239. watch: {
  4240. isRemoveSearchBottomContent(b) {
  4241. gmUtil.setData('isRemoveSearchBottomContent', b);
  4242. },
  4243. isDelPlayerPageAd(b) {
  4244. gmUtil.setData('isDelPlayerPageAd', b);
  4245. },
  4246. isDelPlayerPageRightGameAd(b) {
  4247. gmUtil.setData('isDelPlayerPageRightGameAd', b);
  4248. },
  4249. isDelPlayerPageRightVideoList(b) {
  4250. gmUtil.setData('isDelPlayerPageRightVideoList', b);
  4251. },
  4252. isDelBottomComment(b) {
  4253. gmUtil.setData('isDelBottomComment', b);
  4254. },
  4255. isClearTopInputTipContent(b) {
  4256. gmUtil.setData('isClearTopInputTipContent', b);
  4257. if (b) {
  4258. eventEmitter.send('执行清空顶部搜索框提示内容');
  4259. return
  4260. }
  4261. topInput.setTopInputPlaceholder();
  4262. },
  4263. isDelPlayerEndingPanelVal(n) {
  4264. gmUtil.setData('is_del_player_ending_panel', n);
  4265. }
  4266. }
  4267. };
  4268. const about_and_feedback_vue = {
  4269. template: `
  4270. <div>
  4271. <el-card>
  4272. <template #header>
  4273. <span>作者b站</span>
  4274. </template>
  4275. <el-link target="_blank" :href="b_url" type="primary">b站传送门</el-link>
  4276. </el-card>
  4277. <el-card>
  4278. <template #header>
  4279. <span>交流群</span>
  4280. </template>
  4281. <el-link
  4282. :href='group_url' target="_blank" type="primary">====》Q群传送门《====
  4283. </el-link>
  4284. <el-tooltip content="点击查看群二维码">
  4285. <el-tag @click="lookImgBut">876295632</el-tag>
  4286. </el-tooltip>
  4287. </el-card>
  4288. <el-card>
  4289. <template #header>
  4290. <span>发布、更新、反馈地址</span>
  4291. </template>
  4292. <el-row>
  4293. <el-col :span="12">
  4294. <el-card>
  4295. <span>greasyfork</span>
  4296. <el-link target="_blank" type="primary" href="https://gf.qytechs.cn/scripts/461382/">===》传送门《===
  4297. </el-link>
  4298. </el-card>
  4299. </el-col>
  4300. <el-col :span="12">
  4301. <el-card>
  4302. <span>脚本猫</span>
  4303. <el-link target="_blank" type="primary" :href="scriptCat_js_url">
  4304. ===》传送门《===
  4305. </el-link>
  4306. </el-card>
  4307. </el-col>
  4308. </el-row>
  4309. </el-card>
  4310. <el-card>
  4311. <template #header>
  4312. <span>开源地址</span>
  4313. </template>
  4314. <el-row>
  4315. <el-col :span="12">
  4316. <el-card>
  4317. <span>gitee</span>
  4318. <el-link target="_blank" type="primary" href="https://gitee.com/hangexi/BiBiBSPUserVideoMonkeyScript"
  4319. >https://gitee.com/hangexi/BiBiBSPUserVideoMonkeyScript
  4320. </el-link>
  4321. </el-card>
  4322. </el-col>
  4323. <el-col :span="12">
  4324. <el-card>
  4325. <span>github</span>
  4326. <el-link target="_blank" type="primary" href="https://github.com/hgztask/BiBiBSPUserVideoMonkeyScript"
  4327. >https://github.com/hgztask/BiBiBSPUserVideoMonkeyScript
  4328. </el-link>
  4329. </el-card>
  4330. </el-col>
  4331. </el-row>
  4332. </el-card>
  4333. <el-card>
  4334. <template #header>常见问题和使用文档</template>
  4335. <el-row>
  4336. <el-col :span="12">
  4337. 常见问题
  4338. <el-link target="_blank" type="primary" :href="common_question_url">==>传送门<==
  4339. </el-link>
  4340. </el-col>
  4341. <el-col :span="12">
  4342. 使用文档
  4343. <el-link target="_blank" type="primary"
  4344. href="https://docs.qq.com/doc/DSmJqSkhFaktBeUdk?u=1a1ff7b128d64f188a8bfb71b5acb28c">==>传送门<==
  4345. </el-link>
  4346. </el-col>
  4347. <div>
  4348. 更新日志
  4349. <el-link target="_blank" type="primary" :href="update_log_url">==>传送门<==</el-link>
  4350. </div>
  4351. </el-row>
  4352. </el-card>
  4353. </div>`,
  4354. data() {
  4355. return {
  4356. group_url: globalValue.group_url,
  4357. scriptCat_js_url: globalValue.scriptCat_js_url,
  4358. b_url: globalValue.b_url,
  4359. common_question_url: globalValue.common_question_url,
  4360. update_log_url: globalValue.update_log_url
  4361. }
  4362. },
  4363. methods: {
  4364. lookImgBut() {
  4365. eventEmitter.send('显示图片对话框', {image: "https://www.mikuchase.ltd/img/qq_group_876295632.webp"});
  4366. }
  4367. },
  4368. created() {
  4369. }
  4370. };
  4371. const show_img_dialog_vue = {
  4372. template: `
  4373. <div>
  4374. <el-dialog
  4375. center
  4376. :title="title"
  4377. :modal="isModal"
  4378. :visible.sync="show">
  4379. <div class="el-vertical-center">
  4380. <el-image
  4381. :src="imgSrc" :preview-src-list="imgList"/>
  4382. </div>
  4383. </el-dialog>
  4384. </div>`,
  4385. data() {
  4386. return {
  4387. show: false,
  4388. title: "图片查看",
  4389. imgList: [],
  4390. imgSrc: '',
  4391. isModal: true
  4392. }
  4393. },
  4394. created() {
  4395. eventEmitter.on('显示图片对话框', ({image, title, images, isModal}) => {
  4396. this.imgSrc = image;
  4397. if (title) {
  4398. this.title = title;
  4399. }
  4400. if (images) {
  4401. this.imgList = images;
  4402. } else {
  4403. this.imgList = [image];
  4404. }
  4405. if (isModal) {
  4406. this.isModal = isModal;
  4407. }
  4408. this.show = true;
  4409. });
  4410. }
  4411. };
  4412. const sheet_dialog_vue = {
  4413. props: {
  4414. show: {
  4415. type: Boolean,
  4416. default: false
  4417. },
  4418. list: {
  4419. type: Array,
  4420. default: () => []
  4421. },
  4422. closeOnClickModal: {
  4423. type: Boolean,
  4424. default: true
  4425. },
  4426. title: {
  4427. type: String,
  4428. default: '选项'
  4429. },
  4430. clickItemClose: {
  4431. type: Boolean,
  4432. default: false
  4433. }
  4434. },
  4435. template: `
  4436. <div>
  4437. <el-dialog :visible="show" :title="title"
  4438. width="30%" center
  4439. :close-on-click-modal="closeOnClickModal"
  4440. @close="$emit('close')">
  4441. <div>
  4442. <el-row>
  4443. <el-col v-for="item in list" :key="item.label">
  4444. <el-button style="width: 100%" @click="handleClick(item)">项目{{ item.label }}</el-button>
  4445. </el-col>
  4446. </el-row>
  4447. </div>
  4448. </el-dialog>
  4449. </div>`,
  4450. data() {
  4451. return {
  4452. dialogShow: true,
  4453. list: []
  4454. }
  4455. },
  4456. methods: {
  4457. handleClick(item) {
  4458. if (this.clickItemClose) {
  4459. return;
  4460. }
  4461. this.$emit('options-click', item);
  4462. }
  4463. }
  4464. };
  4465. const bullet_word_management_vue = {
  4466. template: `
  4467. <div>
  4468. <el-button @click="fetchGetBarrageBlockingWordsBut">获取弹幕屏蔽词</el-button>
  4469. <el-button @click="outToJsonFIleBut">导出至json文件</el-button>
  4470. </div>`,
  4471. data: () => {
  4472. return {
  4473. resData: {},
  4474. resList: [],
  4475. }
  4476. },
  4477. methods: {
  4478. async initial() {
  4479. const {state, list, msg} = await bFetch.fetchGetBarrageBlockingWords();
  4480. if (!state) {
  4481. this.$message.warning(msg);
  4482. return false
  4483. }
  4484. this.resList = list;
  4485. this.$notify({message: '已初始化', type: 'success'});
  4486. return true
  4487. },
  4488. fetchGetBarrageBlockingWordsBut() {
  4489. if (this.resList.length === 0) {
  4490. this.$message.info('未有弹幕屏蔽词内容或未初始化');
  4491. return
  4492. }
  4493. const list = this.resList;
  4494. this.$message.success(`已打印在控制台上,数量${list.length}`);
  4495. console.log('获取弹幕屏蔽词_start=====');
  4496. console.log(list);
  4497. console.log('获取弹幕屏蔽词_end=======');
  4498. },
  4499. outToJsonFIleBut() {
  4500. }
  4501. },
  4502. created() {
  4503. this.initial();
  4504. }
  4505. };
  4506. const mainLayoutEl = document.createElement('div');
  4507. if (document.head.querySelector('#element-ui-css') === null) {
  4508. const linkElement = document.createElement('link');
  4509. linkElement.rel = 'stylesheet';
  4510. linkElement.href = 'https://unpkg.com/element-ui/lib/theme-chalk/index.css';
  4511. linkElement.id = 'element-ui-css';
  4512. document.head.appendChild(linkElement);
  4513. console.log('挂载element-ui样式成功');
  4514. }
  4515. window.addEventListener('load', () => {
  4516. document.body.appendChild(mainLayoutEl);
  4517. new Vue({
  4518. el: mainLayoutEl,
  4519. template: `
  4520. <div>
  4521. <el-drawer style="position: fixed"
  4522. :visible.sync="drawer"
  4523. direction="ltr"
  4524. size="100%"
  4525. :modal="false"
  4526. :with-header="false">
  4527. <el-tabs type="border-card" v-model="tabsActiveName"
  4528. @tab-click="tabClick">
  4529. <el-tab-pane label="面板设置" name="面板设置" lazy>
  4530. <panel_settings_vue/>
  4531. </el-tab-pane>
  4532. <el-tab-pane label="规则管理" name="规则管理" lazy>
  4533. <rule_management_vue/>
  4534. </el-tab-pane>
  4535. <el-tab-pane label="兼容设置" name="兼容设置" lazy>
  4536. <compatible_setting_vue/>
  4537. </el-tab-pane>
  4538. <el-tab-pane label="缓存管理" name="缓存管理" lazy>
  4539. <cache_management_vue/>
  4540. </el-tab-pane>
  4541. <el-tab-pane label="页面处理" name="页面处理" lazy>
  4542. <page_processing_vue/>
  4543. </el-tab-pane>
  4544. <el-tab-pane v-if="debug_panel_show" label="弹幕词管理" name="弹幕词管理" lazy>
  4545. <bullet_word_management_vue/>
  4546. </el-tab-pane>
  4547. <el-tab-pane label="输出信息" name="输出信息" lazy>
  4548. <output_information_vue/>
  4549. </el-tab-pane>
  4550. <el-tab-pane label="支持打赏" name="支持打赏" lazy>
  4551. <donate_layout_vue/>
  4552. </el-tab-pane>
  4553. <el-tab-pane label="关于和问题反馈" name="关于和问题反馈" lazy>
  4554. <about_and_feedback_vue/>
  4555. </el-tab-pane>
  4556. <el-tab-pane label="调试测试" name="调试测试" lazy v-if="debug_panel_show">
  4557. <div v-show="debug_panel_show">
  4558. <debugger_management_vue/>
  4559. </div>
  4560. </el-tab-pane>
  4561. </el-tabs>
  4562. </el-drawer>
  4563. <look_content_dialog_vue/>
  4564. <show_img_dialog_vue/>
  4565. <sheet_dialog_vue :show="sheet_dialog.show" :list="sheet_dialog.list" :title="sheet_dialog.title"
  4566. @close="handleClose"
  4567. :close-on-click-modal="sheet_dialog.closeOnClickModal"
  4568. @options-click="handleOptionsClick"/>
  4569. </div>`,
  4570. components: {
  4571. output_information_vue: outputInformationVue,
  4572. donate_layout_vue: donateLayoutVue,
  4573. rule_management_vue: ruleManagementVue,
  4574. cache_management_vue,
  4575. panel_settings_vue,
  4576. compatible_setting_vue,
  4577. look_content_dialog_vue,
  4578. debugger_management_vue,
  4579. page_processing_vue,
  4580. about_and_feedback_vue,
  4581. show_img_dialog_vue,
  4582. sheet_dialog_vue,
  4583. bullet_word_management_vue
  4584. },
  4585. data() {
  4586. return {
  4587. drawer: false,
  4588. tabsActiveName: gmUtil.getData('mainTabsActiveName', '规则管理'),
  4589. debug_panel_show: isOpenDev(),
  4590. sheet_dialog: {
  4591. show: false,
  4592. list: [],
  4593. title: "",
  4594. optionsClick: null,
  4595. closeOnClickModal: true
  4596. }
  4597. }
  4598. },
  4599. methods: {
  4600. tabClick(tab) {
  4601. gmUtil.setData('mainTabsActiveName', tab.name);
  4602. },
  4603. handleClose() {
  4604. this.sheet_dialog.show = false;
  4605. },
  4606. handleOptionsClick(item) {
  4607. let tempBool;
  4608. const temp = this.sheet_dialog.optionsClick(item);
  4609. if (temp === undefined) {
  4610. tempBool = false;
  4611. } else {
  4612. tempBool = temp;
  4613. }
  4614. this.sheet_dialog.show = tempBool === true;
  4615. }
  4616. },
  4617. created() {
  4618. eventEmitter.on('主面板开关', () => {
  4619. const tempBool = this.drawer;
  4620. this.drawer = !tempBool;
  4621. });
  4622. eventEmitter.on('el-notify', (options) => {
  4623. this.$notify(options);
  4624. });
  4625. eventEmitter.on('el-msg', (...options) => {
  4626. this.$message(...options);
  4627. });
  4628. eventEmitter.on('el-alert', (...options) => {
  4629. this.$alert(...options);
  4630. });
  4631. eventEmitter.handler('el-confirm', (...options) => {
  4632. return this.$confirm(...options);
  4633. });
  4634. eventEmitter.on('debugger-dev-show', (bool) => {
  4635. debugger
  4636. this.debug_panel_show = bool;
  4637. if (bool) {
  4638. this.$alert('已开启测试调试面板', 'tip');
  4639. } else {
  4640. this.$alert('已关闭测试调试面板', 'tip');
  4641. }
  4642. });
  4643. eventEmitter.on('sheet-dialog', ({list, optionsClick, title = '选项', closeOnClickModal = false}) => {
  4644. this.sheet_dialog.show = true;
  4645. this.sheet_dialog.list = list;
  4646. this.sheet_dialog.title = title;
  4647. this.sheet_dialog.optionsClick = optionsClick;
  4648. this.sheet_dialog.closeOnClickModal = closeOnClickModal;
  4649. });
  4650. eventEmitter.handler('el-prompt', (...options) => {
  4651. return this.$prompt(...options)
  4652. });
  4653. eventEmitter.on('请求获取视频信息失败', (response, bvId) => {
  4654. requestIntervalQueue.clearPendingQueue();
  4655. eventEmitter.send('更新根据bv号网络请求获取视频信息状态', true);
  4656. this.$alert(`请求获取视频信息失败,状态码:${response.status},bv号:${bvId}
  4657. \n。已自动禁用根据bv号网络请求获取视频信息状态
  4658. \n如需关闭,请在面板条件限制里手动关闭。`, '错误', {
  4659. confirmButtonText: '确定',
  4660. type: 'error'
  4661. });
  4662. });
  4663. if (bAfterLoadingThePageOpenMainPanel()) {
  4664. this.drawer = true;
  4665. }
  4666. }
  4667. });
  4668. });
  4669. var defCss = `
  4670. .el-vertical-center {
  4671. display: flex;
  4672. justify-content: center;
  4673. }
  4674. .el-horizontal-center {
  4675. display: flex;
  4676. align-items: center;
  4677. }
  4678. .el-horizontal-right {
  4679. display: flex;
  4680. justify-content: flex-end;
  4681. }
  4682. .el-horizontal-left {
  4683. display: flex;
  4684. justify-content: flex-start;
  4685. }
  4686. `;
  4687. gmUtil.addStyle(`
  4688. [gz_bezel]{
  4689. border:1px solid ${localMKData.getBorderColor()}
  4690. }
  4691. `);
  4692. gmUtil.addStyle(defCss);
  4693. const toPlayCountOrBulletChat = (str) => {
  4694. if (!str) {
  4695. return -1
  4696. }
  4697. str = str.split(/[\t\r\f\n\s]*/g).join("");
  4698. const replace = str.replace(/[^\d.]/g, '');
  4699. if (str.endsWith('万') || str.endsWith('万次') || str.endsWith('万弹幕')) {
  4700. return parseFloat(replace) * 10000;
  4701. }
  4702. if (str.endsWith('次') || str.endsWith('弹幕')) {
  4703. return parseInt(replace);
  4704. }
  4705. return parseInt(str)
  4706. };
  4707. const timeStringToSeconds = (timeStr) => {
  4708. if (!timeStr) {
  4709. return -1
  4710. }
  4711. const parts = timeStr.split(':');
  4712. switch (parts.length) {
  4713. case 1: // 只有秒
  4714. return Number(parts[0]);
  4715. case 2: // 分钟和秒
  4716. return Number(parts[0]) * 60 + Number(parts[1]);
  4717. case 3: // 小时、分钟和秒
  4718. return Number(parts[0]) * 3600 + Number(parts[1]) * 60 + Number(parts[2]);
  4719. default:
  4720. throw new Error('Invalid time format');
  4721. }
  4722. };
  4723. var sFormatUtil = {
  4724. toPlayCountOrBulletChat,
  4725. timeStringToSeconds
  4726. };
  4727. const exactMatch = (ruleList, value) => {
  4728. if (ruleList === null || ruleList === undefined) return false;
  4729. if (!Array.isArray(ruleList)) return false
  4730. return ruleList.some(item => item === value);
  4731. };
  4732. const bFuzzyAndRegularMatchingWordsToLowercase = localMKData.bFuzzyAndRegularMatchingWordsToLowercase();
  4733. const regexMatch = (ruleList, value) => {
  4734. if (ruleList === null || ruleList === undefined) return null;
  4735. if (!Array.isArray(ruleList)) return null
  4736. if (bFuzzyAndRegularMatchingWordsToLowercase) {
  4737. value = value.toLowerCase();
  4738. }
  4739. value = value.split(/[\t\r\f\n\s]*/g).join("");
  4740. const find = ruleList.find(item => {
  4741. try {
  4742. return value.search(item) !== -1;
  4743. } catch (e) {
  4744. const msg = `正则匹配失败,请检查规则列表中的正则表达式是否正确,错误信息:${e.message}`;
  4745. eventEmitter.send('正则匹配时异常', {e, msg});
  4746. return false;
  4747. }
  4748. });
  4749. return find === undefined ? null : find;
  4750. };
  4751. const fuzzyMatch = (ruleList, value) => {
  4752. if (ruleList === null || ruleList === undefined || value === null) return null;
  4753. if (!Array.isArray(ruleList)) return null
  4754. const find = ruleList.find(item => value.toLowerCase().includes(item));
  4755. return find === undefined ? null : find;
  4756. };
  4757. var ruleMatchingUtil = {
  4758. exactMatch,
  4759. regexMatch,
  4760. fuzzyMatch
  4761. };
  4762. const outputInformationFontColor = localMKData.getOutputInformationFontColor();
  4763. const highlightInformationColor = localMKData.getHighlightInformationColor();
  4764. const getLiveRoomCommentInfoHtml = (type, matching, commentData) => {
  4765. const toTimeString = defUtil.toTimeString();
  4766. const {name, uid, content} = commentData;
  4767. return `<b style="color: ${outputInformationFontColor}; " gz_bezel>
  4768. ${toTimeString}-根据${type}-${matching ? `<b style="color: ${highlightInformationColor}">【${matching}】</b>` : ""}-屏蔽用户【${name}】uid=
  4769. <a href="https://space.bilibili.com/${uid}"
  4770. style="color: ${highlightInformationColor}"
  4771. target="_blank">【${uid}】</a>
  4772. 直播评论【${content}】
  4773. </b>`
  4774. };
  4775. const getDynamicContentInfoHtml = (type, matching, dynamicData) => {
  4776. const toTimeString = defUtil.toTimeString();
  4777. const {name, uid, content} = dynamicData;
  4778. return `<b style="color: ${outputInformationFontColor}; " gz_bezel>
  4779. ${toTimeString}-根据${type}-${matching ? `<b style="color: ${highlightInformationColor}">【${matching}】</b>` : ""}-屏蔽用户【${name}】uid=
  4780. <a href="https://space.bilibili.com/${uid}"
  4781. style="color: ${highlightInformationColor}"
  4782. target="_blank">【${uid}】</a>
  4783. 动态【${content}】
  4784. </b>`
  4785. };
  4786. const getLiveRoomInfoHtml = (type, matching, liveRoomData) => {
  4787. const toTimeString = defUtil.toTimeString();
  4788. const {name = null, uid = -1, title, liveUrl} = liveRoomData;
  4789. return `<b style="color: ${outputInformationFontColor};" gz_bezel>
  4790. ${toTimeString}-根据${type}${matching ? `<b style="color: ${highlightInformationColor}">【${matching}】</b>` : ""}-屏蔽用户【${name === null ? '' : name}】${uid === -1 ? "" : `uid=
  4791. <a href="https://space.bilibili.com/${uid}"
  4792. style="color: ${highlightInformationColor}"
  4793. target="_blank">【${uid}】</a>`}
  4794. 直播间标题【<a href="${liveUrl}" target="_blank" style="color: ${highlightInformationColor}">${title}</a>】
  4795. </b>`
  4796. };
  4797. var output_informationTab = {
  4798. getLiveRoomCommentInfoHtml,
  4799. getDynamicContentInfoHtml,
  4800. getLiveRoomInfoHtml
  4801. };
  4802. class ElEventEmitter {
  4803. #elEvents = new Map()
  4804. addEvent(el, eventName, callback, repeated = false) {
  4805. const elEvents = this.#elEvents;
  4806. if (!elEvents.has(el)) {
  4807. elEvents.set(el, {events: [], attrs: []});
  4808. }
  4809. const {events, attrs} = elEvents.get(el);
  4810. if (!repeated) {
  4811. if (attrs.includes(eventName)) {
  4812. return
  4813. }
  4814. }
  4815. attrs.push(eventName);
  4816. events.push({eventName, callback});
  4817. el.setAttribute(`gz-event`, JSON.stringify(attrs));
  4818. el.addEventListener(eventName, callback);
  4819. }
  4820. hasEventName(el, eventName) {
  4821. const elEvents = this.#elEvents;
  4822. if (elEvents.has(el)) {
  4823. return true
  4824. }
  4825. const {attrs} = elEvents.get(el);
  4826. return attrs.includes(eventName)
  4827. }
  4828. }
  4829. const elEventEmitter = new ElEventEmitter();
  4830. const addBlockButton$1 = (data, tagCss = '', position = []) => {
  4831. const {insertionPositionEl, explicitSubjectEl, css} = data.data;
  4832. if (tagCss !== '') {
  4833. if (insertionPositionEl.querySelector("." + tagCss)) return;
  4834. }
  4835. const buttonEL = document.createElement("button");
  4836. buttonEL.setAttribute("gz_type", "");
  4837. if (tagCss !== '') {
  4838. buttonEL.className = tagCss;
  4839. }
  4840. buttonEL.textContent = "屏蔽";
  4841. if (position.length !== 0) {
  4842. buttonEL.style.position = "absolute";
  4843. }
  4844. if (position.includes("right")) {
  4845. buttonEL.style.right = "0";
  4846. }
  4847. if (position.includes("bottom")) {
  4848. buttonEL.style.bottom = "0";
  4849. }
  4850. if (css !== undefined) {
  4851. for (let key of Object.keys(css)) {
  4852. buttonEL.style[key] = css[key];
  4853. }
  4854. }
  4855. if (explicitSubjectEl) {
  4856. buttonEL.style.display = "none";
  4857. elEventEmitter.addEvent(explicitSubjectEl, "mouseout", () => buttonEL.style.display = "none");
  4858. elEventEmitter.addEvent(explicitSubjectEl, "mouseover", () => buttonEL.style.display = "");
  4859. }
  4860. insertionPositionEl.appendChild(buttonEL);
  4861. buttonEL.addEventListener("click", (event) => {
  4862. event.stopImmediatePropagation(); // 阻止事件冒泡和同一元素上的其他事件处理器
  4863. event.preventDefault(); // 阻止默认行为
  4864. const {uid = -1, name = null} = data.data;
  4865. const showList = [];
  4866. showList.push(uid !== -1 ? {
  4867. label: `uid精确屏蔽-用户uid=${uid}-name=${name}`,
  4868. value: "uid"
  4869. } : {
  4870. label: `用户名精确屏蔽(不推荐)-用户name=${name}`,
  4871. value: 'name'
  4872. });
  4873. eventEmitter.send('sheet-dialog', {
  4874. title: "屏蔽选项",
  4875. list: showList,
  4876. optionsClick: (item) => {
  4877. const {value} = item;
  4878. if (value === 'uid') {
  4879. if (uid === -1) {
  4880. eventEmitter.send('el-msg', "该页面数据不存在uid字段");
  4881. return;
  4882. }
  4883. const {status} = ruleUtil.addRulePreciseUid(uid);
  4884. if (status) {
  4885. data.maskingFunc();
  4886. }
  4887. return;
  4888. }
  4889. eventEmitter.invoke('el-confirm', '不推荐用户使用精确用户名来屏蔽,确定继续吗?').then(() => {
  4890. ruleUtil.addRulePreciseName(name);
  4891. });
  4892. }
  4893. });
  4894. });
  4895. };
  4896. const addTopicDetailVideoBlockButton = (data) => {
  4897. addBlockButton$1(data, "gz_shielding_button");
  4898. };
  4899. const addTopicDetailContentsBlockButton = (data) => {
  4900. const position = data.data.position;
  4901. const loop = position !== undefined;
  4902. addBlockButton$1(data, "gz_shielding_topic_detail_button", loop ? position : []);
  4903. };
  4904. const blockUserUid = (uid) => {
  4905. if (ruleMatchingUtil.exactMatch(ruleKeyListData$1.getPreciseUidArr(), uid)) {
  4906. return {state: true, type: "精确uid"};
  4907. }
  4908. return returnTempVal;
  4909. };
  4910. const blockCheckWhiteUserUid = (uid) => {
  4911. return ruleMatchingUtil.exactMatch(ruleKeyListData$1.getPreciseUidWhiteArr(), uid);
  4912. };
  4913. const blockExactAndFuzzyMatching = (val, config) => {
  4914. if (!val) {
  4915. return returnTempVal
  4916. }
  4917. if (config.exactKey) {
  4918. if (ruleMatchingUtil.exactMatch(gmUtil.getData(config.exactKey, []), val)) {
  4919. return {state: true, type: config.exactTypeName, matching: val}
  4920. }
  4921. }
  4922. let matching;
  4923. if (config.fuzzyKey) {
  4924. matching = ruleMatchingUtil.fuzzyMatch(gmUtil.getData(config.fuzzyKey, []), val);
  4925. if (matching) {
  4926. return {state: true, type: config.fuzzyTypeName, matching}
  4927. }
  4928. }
  4929. if (config.regexKey) {
  4930. matching = ruleMatchingUtil.regexMatch(gmUtil.getData(config.regexKey, []), val);
  4931. if (matching) {
  4932. return {state: true, type: config.regexTypeName, matching}
  4933. }
  4934. }
  4935. return returnTempVal
  4936. };
  4937. const blockComment = (comment) => {
  4938. return blockExactAndFuzzyMatching(comment, {
  4939. fuzzyKey: 'commentOn', fuzzyTypeName: '模糊评论',
  4940. regexKey: 'commentOnCanonical', regexTypeName: '正则评论'
  4941. })
  4942. };
  4943. const blockAvatarPendant = (name) => {
  4944. return blockExactAndFuzzyMatching(name, {
  4945. exactKey: 'precise_avatarPendantName',
  4946. exactTypeName: '精确头像挂件名', fuzzyKey: 'avatarPendantName', fuzzyTypeName: '模糊头像挂件名'
  4947. })
  4948. };
  4949. const asyncBlockAvatarPendant = async (name) => {
  4950. const res = blockAvatarPendant(name);
  4951. if (res.state) return Promise.reject(res);
  4952. };
  4953. const blockSignature = (signature) => {
  4954. return blockExactAndFuzzyMatching(signature, {
  4955. fuzzyKey: 'signature', fuzzyTypeName: '模糊用户签名', regexKey: 'signatureCanonical', regexTypeName: '正则用户签名'
  4956. })
  4957. };
  4958. const asyncBlockSignature = async (signature) => {
  4959. const res = blockSignature(signature);
  4960. if (res.state) return Promise.reject(res);
  4961. };
  4962. const blockVideoDesc = (desc) => {
  4963. return blockExactAndFuzzyMatching(desc, {
  4964. fuzzyKey: 'videoDesc', fuzzyTypeName: '视频简介(模糊匹配)'
  4965. , regexKey: 'videoDescCanonical', regexTypeName: '视频简介(正则匹配)'
  4966. })
  4967. };
  4968. const asyncBlockVideoDesc = async (desc) => {
  4969. const res = blockVideoDesc(desc);
  4970. if (res.state) return Promise.reject(res);
  4971. };
  4972. const blockGender = (gender) => {
  4973. const val = localMKData.isGenderRadioVal();
  4974. const state = val === gender && val !== '不处理';
  4975. if (state) {
  4976. return {state: true, type: '性别屏蔽', matching: val}
  4977. }
  4978. return returnTempVal;
  4979. };
  4980. const asyncBlockGender = async (gender) => {
  4981. const res = blockGender(gender);
  4982. if (res.state) {
  4983. return Promise.reject(res)
  4984. }
  4985. };
  4986. const blockUserVip = (vipId) => {
  4987. const val = localMKData.isVipTypeRadioVal();
  4988. const vipMap = {
  4989. 0: '无',
  4990. 1: '月大会员',
  4991. 2: '年度及以上大会员'
  4992. };
  4993. if (val === vipMap[vipId]) {
  4994. return {state: true, type: '会员类型屏蔽', matching: val}
  4995. }
  4996. return returnTempVal
  4997. };
  4998. const asyncBlockUserVip = async (vipId) => {
  4999. const res = blockUserVip(vipId);
  5000. if (res.state) {
  5001. return Promise.reject(res)
  5002. }
  5003. };
  5004. const blockSeniorMember = (num) => {
  5005. if (num === 1 && localMKData.isSeniorMember()) {
  5006. return {state: true, type: '屏蔽硬核会员'}
  5007. }
  5008. return returnTempVal
  5009. };
  5010. const asyncBlockSeniorMember = async (num) => {
  5011. const res = blockSeniorMember(num);
  5012. if (res.state) {
  5013. return Promise.reject(res)
  5014. }
  5015. };
  5016. const blockVideoCopyright = (num) => {
  5017. const val = localMKData.isCopyrightRadio();
  5018. const tempMap = {
  5019. 1: '原创',
  5020. 2: '转载'
  5021. };
  5022. if (val === tempMap[num]) {
  5023. return {state: true, type: '视频类型屏蔽', matching: val}
  5024. }
  5025. return returnTempVal
  5026. };
  5027. const asyncBlockVideoCopyright = async (num) => {
  5028. const res = blockVideoCopyright(num);
  5029. if (res.state) {
  5030. return Promise.reject(res)
  5031. }
  5032. };
  5033. const blockVerticalVideo = (dimension) => {
  5034. if (!localMKData.isBlockVerticalVideo()) {
  5035. return returnTempVal
  5036. }
  5037. if (!dimension) {
  5038. return returnTempVal
  5039. }
  5040. const vertical = dimension.width < dimension.height;
  5041. if (vertical) {
  5042. return {state: true, type: '竖屏视频屏蔽', matching: vertical}
  5043. }
  5044. return returnTempVal
  5045. };
  5046. const asyncBlockVerticalVideo = async (dimension) => {
  5047. const res = blockVerticalVideo(dimension);
  5048. if (res.state) return Promise.reject(res);
  5049. };
  5050. const blockVideoLikeRate = (like, view) => {
  5051. if (!like || !view || !localMKData.isVideoLikeRateBlockingStatus()) {
  5052. return returnTempVal
  5053. }
  5054. const mk_likeRate = parseInt(localMKData.getVideoLikeRate() * 100);
  5055. if (isNaN(mk_likeRate)) {
  5056. return returnTempVal
  5057. }
  5058. const likeRate = defUtil.calculateLikeRate(like, view);
  5059. if (likeRate <= mk_likeRate) {
  5060. return {
  5061. state: true, type: '视频点赞率屏蔽', matching: mk_likeRate + '%'
  5062. , msg: `视频的点赞率为${likeRate}%,低于用户指定的限制${mk_likeRate}%,屏蔽该视频`
  5063. }
  5064. }
  5065. return returnTempVal
  5066. };
  5067. const asyncBlockVideoLikeRate = async (like, view) => {
  5068. const res = blockVideoLikeRate(like, view);
  5069. if (res.state) return Promise.reject(res);
  5070. };
  5071. const blockVideoInteractiveRate = (danmaku, reply, view) => {
  5072. if (!danmaku || !view || !localMKData.isInteractiveRateBlockingStatus()) {
  5073. return returnTempVal
  5074. }
  5075. const mk_interactionRate = parseInt(localMKData.getInteractiveRate() * 100);
  5076. const interactionRate = defUtil.calculateInteractionRate(danmaku, reply, view);
  5077. if (interactionRate <= mk_interactionRate) {
  5078. return {
  5079. state: true, type: '视频互动率屏蔽', matching: mk_interactionRate + '%'
  5080. , msg: `视频的互动率为${interactionRate}%,低于用户指定的限制${mk_interactionRate}%,屏蔽该视频`
  5081. }
  5082. }
  5083. return returnTempVal
  5084. };
  5085. const asyncBlockVideoInteractiveRate = async (danmaku, reply, view) => {
  5086. const res = blockVideoInteractiveRate(danmaku, reply, view);
  5087. if (res.state) return Promise.reject(res);
  5088. };
  5089. const blockVideoTripleRate = (favorite, coin, share, view) => {
  5090. if (!favorite || !coin || !share || !view || !localMKData.isTripleRateBlockingStatus()) {
  5091. return returnTempVal
  5092. }
  5093. const mk_tripleRate = parseInt(localMKData.getTripleRate() * 100);
  5094. const tripleRate = defUtil.calculateTripleRate(favorite, coin, share, view);
  5095. if (tripleRate <= mk_tripleRate) {
  5096. return {
  5097. state: true, type: '视频三连率屏蔽', matching: mk_tripleRate + '%'
  5098. , msg: `视频的三连率为${tripleRate}%,低于用户指定的限制${mk_tripleRate}%,屏蔽该视频`
  5099. }
  5100. }
  5101. return returnTempVal
  5102. };
  5103. const asyncBlockVideoTripleRate = async (favorite, coin, share, view) => {
  5104. const res = blockVideoTripleRate(favorite, coin, share, view);
  5105. if (res.state) return Promise.reject(res);
  5106. };
  5107. const blockVideoCoinLikesRatioRate = (coin, like) => {
  5108. if (!coin || !like || !localMKData.isCoinLikesRatioRateBlockingStatus()) {
  5109. return returnTempVal
  5110. }
  5111. const mk_coinLikesRatioRate = parseInt(localMKData.getCoinLikesRatioRate() * 100);
  5112. const coinLikesRatioRate = defUtil.calculateCoinLikesRatioRate(coin, like);
  5113. if (coinLikesRatioRate <= mk_coinLikesRatioRate) {
  5114. return {
  5115. state: true,
  5116. type: '视频投币/点赞比(内容价值)屏蔽',
  5117. matching: mk_coinLikesRatioRate + '%',
  5118. msg: `视频的投币/点赞比(内容价值)为${coinLikesRatioRate}%,低于用户指定的限制${mk_coinLikesRatioRate}%,屏蔽该视频`
  5119. }
  5120. }
  5121. return returnTempVal
  5122. };
  5123. const asyncBlockVideoCoinLikesRatioRate = async (coin, like) => {
  5124. const res = blockVideoCoinLikesRatioRate(coin, like);
  5125. if (res.state) return Promise.reject(res);
  5126. };
  5127. const blockByLevel = (level) => {
  5128. if (!level) {
  5129. return returnTempVal
  5130. }
  5131. const min = gmUtil.getData('nMinimumLevel', -1);
  5132. if (min > level) {
  5133. return {state: true, type: "最小用户等级过滤", matching: min};
  5134. }
  5135. const max = gmUtil.getData('nMaximumLevel', -1);
  5136. if (max > level) {
  5137. return {state: true, type: "最大用户等级过滤", matching: max};
  5138. }
  5139. return returnTempVal
  5140. };
  5141. const asyncBlockByLevel = async (level) => {
  5142. const res = blockByLevel(level);
  5143. if (res.state) return Promise.reject(res);
  5144. };
  5145. const blockUserUidAndName = (uid, name) => {
  5146. if (!uid || !name) {
  5147. return returnTempVal
  5148. }
  5149. let returnVal = blockUidWholeProcess(uid);
  5150. if (returnVal.state) {
  5151. return returnVal
  5152. }
  5153. returnVal = blockUserName(name);
  5154. if (returnVal.state) {
  5155. return returnVal
  5156. }
  5157. return returnTempVal
  5158. };
  5159. const asyncBlockUserUidAndName = async (uid, name) => {
  5160. const res = blockUserUidAndName(uid, name);
  5161. if (res.state) {
  5162. return Promise.reject(res)
  5163. }
  5164. };
  5165. const blockVideoTeamMember = (teamMember) => {
  5166. if (!teamMember) {
  5167. return returnTempVal
  5168. }
  5169. for (let u of teamMember) {
  5170. const returnVal = blockUserUidAndName(u.mid, u.name);
  5171. if (returnVal.state) {
  5172. return returnVal
  5173. }
  5174. }
  5175. return returnTempVal
  5176. };
  5177. const asyncBlockVideoTeamMember = async (teamMember) => {
  5178. const res = blockVideoTeamMember(teamMember);
  5179. if (res.state) return Promise.reject(res)
  5180. };
  5181. const blockUserName = (name) => {
  5182. return blockExactAndFuzzyMatching(name, {
  5183. exactKey: 'precise_name',
  5184. exactTypeName: '精确用户名', fuzzyKey: 'name', fuzzyTypeName: '模糊用户名',
  5185. regexKey: 'nameCanonical', regexTypeName: '正则用户名'
  5186. })
  5187. };
  5188. const blockVideoOrOtherTitle = (title) => {
  5189. return blockExactAndFuzzyMatching(title, {
  5190. fuzzyKey: 'title', fuzzyTypeName: '模糊标题',
  5191. regexKey: 'titleCanonical', regexTypeName: '正则标题'
  5192. })
  5193. };
  5194. const blockBasedVideoTag = (tags) => {
  5195. const preciseVideoTagArr = ruleKeyListData$1.getPreciseVideoTagArr();
  5196. const videoTagArr = ruleKeyListData$1.getVideoTagArr();
  5197. if (preciseVideoTagArr.length <= 0 && videoTagArr.length <= 0) {
  5198. return returnTempVal
  5199. }
  5200. for (let tag of tags) {
  5201. if (ruleMatchingUtil.exactMatch(preciseVideoTagArr, tag)) {
  5202. return {state: true, type: "精确视频tag", matching: tag}
  5203. }
  5204. let fuzzyMatch = ruleMatchingUtil.fuzzyMatch(videoTagArr, tag);
  5205. if (fuzzyMatch) {
  5206. return {state: true, type: "模糊视频tag", matching: fuzzyMatch}
  5207. }
  5208. fuzzyMatch = ruleMatchingUtil.regexMatch(ruleKeyListData$1.getVideoTagCanonicalArr(), tag);
  5209. if (fuzzyMatch) {
  5210. return {state: true, type: "正则视频tag", matching: fuzzyMatch}
  5211. }
  5212. }
  5213. return returnTempVal
  5214. };
  5215. const asyncBlockBasedVideoTag = async (tags) => {
  5216. const res = blockBasedVideoTag(tags);
  5217. if (res.state) return Promise.reject(res);
  5218. };
  5219. const blockByUidRange = (uid) => {
  5220. if (!localMKData.isUidRangeMaskingStatus()) {
  5221. return returnTempVal
  5222. }
  5223. const [head, tail] = localMKData.getUidRangeMasking();
  5224. if (head >= uid <= tail) {
  5225. return {state: true, type: "uid范围屏蔽", matching: `${head}=>${uid}<=${tail}`}
  5226. }
  5227. return returnTempVal
  5228. };
  5229. const blockUidWholeProcess = (uid) => {
  5230. if (!uid || blockCheckWhiteUserUid(uid)) return returnTempVal
  5231. let returnVal = blockUserUid(uid);
  5232. if (returnVal.state) {
  5233. return returnVal;
  5234. }
  5235. return blockByUidRange(uid)
  5236. };
  5237. const asyncBlockFollowedVideo = (following) => {
  5238. if (following && localMKData.isBlockFollowed()) {
  5239. return Promise.reject({state: true, type: '已关注'})
  5240. }
  5241. };
  5242. const asyncBlockChargeVideo = (isUpOwnerExclusive) => {
  5243. if (isUpOwnerExclusive && localMKData.isUpOwnerExclusive()) {
  5244. return Promise.reject({state: true, type: '充电专属视频'})
  5245. }
  5246. };
  5247. const blockTimeRangeMasking = (timestamp) => {
  5248. if (!timestamp || !localMKData.isTimeRangeMaskingStatus()) {
  5249. return returnTempVal
  5250. }
  5251. const timeRangeMaskingArr = localMKData.getTimeRangeMaskingArr();
  5252. if (timeRangeMaskingArr.length === 0) {
  5253. return returnTempVal;
  5254. }
  5255. for (let {status, r: [startTimestamp, endTimestamp]} of timeRangeMaskingArr) {
  5256. if (!status) continue
  5257. const startSecondsTimestamp = Math.floor(startTimestamp / 1000);
  5258. const endSecondsTimestamp = Math.floor(endTimestamp / 1000);
  5259. if (startSecondsTimestamp >= timestamp <= endSecondsTimestamp) {
  5260. const startToTime = new Date(startTimestamp).toLocaleString();
  5261. const endToTime = new Date(endTimestamp).toLocaleString();
  5262. const timestampToTime = new Date(timestamp * 1000).toLocaleString();
  5263. return {state: true, type: "时间范围屏蔽", matching: `${startToTime}=>${timestampToTime}<=${endToTime}`}
  5264. }
  5265. }
  5266. return returnTempVal
  5267. };
  5268. const asyncBlockTimeRangeMasking = async (timestamp) => {
  5269. const res = blockTimeRangeMasking(timestamp);
  5270. if (res.state) return Promise.reject(res);
  5271. };
  5272. const shieldingDynamic = (dynamicData) => {
  5273. const {
  5274. content = null,
  5275. el,
  5276. title = null,
  5277. tag = null
  5278. } = dynamicData;
  5279. let matching = null;
  5280. if (content !== null) {
  5281. matching = ruleMatchingUtil.fuzzyMatch(ruleKeyListData$1.getCommentOnArr(), content);
  5282. if (matching !== null) {
  5283. el?.remove();
  5284. return {state: true, type: "模糊评论内容", matching};
  5285. }
  5286. matching = ruleMatchingUtil.regexMatch(ruleKeyListData$1.getCommentOnCanonicalArr(), content);
  5287. if (matching !== null) {
  5288. el?.remove();
  5289. return {state: true, type: "正则评论内容", matching};
  5290. }
  5291. }
  5292. if (title !== null) {
  5293. matching = ruleMatchingUtil.fuzzyMatch(ruleKeyListData$1.getTitleArr(), title);
  5294. if (matching !== null) {
  5295. el?.remove();
  5296. return {state: true, type: "模糊标题", matching};
  5297. }
  5298. matching = ruleMatchingUtil.regexMatch(ruleKeyListData$1.getTitleCanonicalArr(), title);
  5299. if (matching !== null) {
  5300. el?.remove();
  5301. return {state: true, type: "正则标题", matching};
  5302. }
  5303. }
  5304. if (tag !== null) {
  5305. if (ruleMatchingUtil.exactMatch(ruleKeyListData$1.getPreciseTagArr(), tag)) {
  5306. el?.remove();
  5307. return {state: true, type: "精确话题tag"};
  5308. }
  5309. matching = ruleMatchingUtil.fuzzyMatch(ruleKeyListData$1.getTagArr(), tag);
  5310. if (matching !== null) {
  5311. el?.remove();
  5312. return {state: true, type: "模糊话题tag", matching};
  5313. }
  5314. matching = ruleMatchingUtil.regexMatch(ruleKeyListData$1.getTagCanonicalArr(), tag);
  5315. if (matching !== null) {
  5316. el?.remove();
  5317. return {state: true, type: "正则话题tag", matching};
  5318. }
  5319. }
  5320. return returnTempVal
  5321. };
  5322. const shieldingDynamicDecorated = (dynamicData) => {
  5323. const {state, type, matching} = shieldingDynamic(dynamicData);
  5324. if (state) {
  5325. const infoHtml = output_informationTab.getDynamicContentInfoHtml(type, matching, dynamicData);
  5326. eventEmitter.send('打印信息', infoHtml);
  5327. }
  5328. return state;
  5329. };
  5330. const intervalExecutionStartShieldingVideoInert = (func, name = '') => {
  5331. let i1 = -1;
  5332. const start = () => {
  5333. if (i1 !== -1) {
  5334. return
  5335. }
  5336. console.log('开始执行屏蔽' + name);
  5337. i1 = setInterval(() => {
  5338. func();
  5339. console.log(`执行屏蔽${name}列表-定时器正在执行`);
  5340. }, 800);
  5341. };
  5342. const stop = () => {
  5343. if (i1 === -1) {
  5344. return
  5345. }
  5346. clearInterval(i1);
  5347. console.log(`已停止执行屏蔽${name}列表`);
  5348. i1 = -1;
  5349. };
  5350. return {start, stop}
  5351. };
  5352. var shielding = {
  5353. shieldingDynamicDecorated,
  5354. addTopicDetailVideoBlockButton,
  5355. addTopicDetailContentsBlockButton,
  5356. intervalExecutionStartShieldingVideoInert,
  5357. addBlockButton: addBlockButton$1
  5358. };
  5359. class VideoInfoCache {
  5360. #caches = [];
  5361. getCaches() {
  5362. return this.#caches;
  5363. }
  5364. getCount() {
  5365. return this.#caches.length;
  5366. }
  5367. addData(data) {
  5368. this.#caches.push(data);
  5369. }
  5370. is(bv) {
  5371. return this.#caches.some(item => item.bv === bv);
  5372. }
  5373. find(bv) {
  5374. const find = this.#caches.find(item => item.bv === bv);
  5375. if (find) {
  5376. return find
  5377. }
  5378. return null
  5379. }
  5380. async update() {
  5381. this.#caches = await bvDexie.getVideoInfo();
  5382. return this.getCaches();
  5383. }
  5384. }
  5385. const videoInfoCache = new VideoInfoCache();
  5386. const asyncBlockVideoTagPreciseCombination = async (tags) => {
  5387. if (tags.length <= 0) return;
  5388. const mkArrTags = ruleKeyListData$1.getVideoTagPreciseCombination();
  5389. for (let mkTags of mkArrTags) {
  5390. if (arrUtil.arrayContains(tags, mkTags)) return Promise.reject({
  5391. state: true,
  5392. type: "多重tag屏蔽",
  5393. matching: mkTags
  5394. })
  5395. }
  5396. };
  5397. const blockVideoBV = (bv) => {
  5398. const bvs = ruleKeyListData$1.getPreciseVideoBV();
  5399. if (bvs.includes(bv)) return Promise.reject({
  5400. state: true,
  5401. type: "精确bv号屏蔽",
  5402. matching: bv
  5403. })
  5404. return returnTempVal
  5405. };
  5406. const shieldingVideo = (videoData) => {
  5407. const {
  5408. title, uid = -1,
  5409. name, nDuration = -1,
  5410. nBulletChat = -1, nPlayCount = -1,
  5411. bv = null
  5412. } = videoData;
  5413. let returnVal = blockUserUidAndName(uid, name);
  5414. if (returnVal.state) {
  5415. return returnVal;
  5416. }
  5417. returnVal = blockVideoOrOtherTitle(title);
  5418. if (returnVal.state) {
  5419. return returnVal
  5420. }
  5421. returnVal = blockVideoBV(bv);
  5422. if (returnVal.state) return returnVal;
  5423. if (nDuration !== -1) {
  5424. const min = gmUtil.getData('nMinimumDuration', -1);
  5425. if (min > nDuration && min !== -1) {
  5426. return {state: true, type: '最小时长', matching: min}
  5427. }
  5428. const max = gmUtil.getData('nMaximumDuration', -1);
  5429. if (max < nDuration && max !== -1) {
  5430. return {state: true, type: '最大时长', matching: max}
  5431. }
  5432. }
  5433. if (nBulletChat !== -1) {
  5434. const min = gmUtil.getData('nMinimumBarrage', -1);
  5435. if (min > nBulletChat && min !== -1) {
  5436. return {state: true, type: '最小弹幕数', matching: min}
  5437. }
  5438. const max = gmUtil.getData('nMaximumBarrage', -1);
  5439. if (max < nBulletChat && max !== -1) {
  5440. return {state: true, type: '最大弹幕数', matching: max}
  5441. }
  5442. }
  5443. if (nPlayCount !== -1) {
  5444. const min = gmUtil.getData('nMinimumPlay', -1);
  5445. if (min > nPlayCount && min !== -1) {
  5446. return {state: true, type: '最小播放量', matching: min}
  5447. }
  5448. const max = gmUtil.getData('nMaximumPlayback', -1);
  5449. if (max < nPlayCount && max !== -1) {
  5450. return {state: true, type: '最大播放量', matching: max}
  5451. }
  5452. }
  5453. return returnTempVal;
  5454. };
  5455. const shieldingVideoDecorated = (videoData, method = "remove") => {
  5456. const {el} = videoData;
  5457. if (el.style.display === "none") return true;
  5458. const {state, type, matching = null} = shieldingVideo(videoData);
  5459. if (state) {
  5460. eventEmitter.send('event-屏蔽视频元素', {res: {state, type, matching}, method, videoData});
  5461. return true;
  5462. }
  5463. if (localMKData.isDisableNetRequestsBvVideoInfo()) return state;
  5464. shieldingOtherVideoParameter(videoData, method);
  5465. return state;
  5466. };
  5467. eventEmitter.on('event-屏蔽视频元素', ({res, method = "remove", videoData}) => {
  5468. if (!res) return
  5469. const {type, matching} = res;
  5470. const {el} = videoData;
  5471. if (method === "remove") {
  5472. el?.remove();
  5473. } else {
  5474. el.style.display = "none";
  5475. }
  5476. eventEmitter.send('屏蔽视频信息', type, matching, videoData);
  5477. });
  5478. const shieldingOtherVideoParameter = async (videoData, method) => {
  5479. const {bv = '-1'} = videoData;
  5480. if (bv === '-1') return
  5481. if (videoInfoCache.getCount() === 0) {
  5482. await videoInfoCache.update();
  5483. }
  5484. const find = videoInfoCache.find(bv);
  5485. let result;
  5486. if (find === null) {
  5487. const {state, data, msg} = await requestIntervalQueue.add(() => bFetch.fetchGetVideoInfo(bv));
  5488. if (!state) {
  5489. console.warn('获取视频信息失败:' + msg);
  5490. return
  5491. }
  5492. result = data;
  5493. if (await bvDexie.addVideoData(bv, result)) {
  5494. await videoInfoCache.update();
  5495. console.log('mk-db-添加视频信息到数据库成功', result, videoData);
  5496. }
  5497. } else {
  5498. result = find;
  5499. }
  5500. const {tags = [], userInfo, videoInfo} = result;
  5501. asyncBlockUserUidAndName(userInfo.mid, userInfo.name)
  5502. .then(() => asyncBlockVideoTagPreciseCombination(tags))
  5503. .then(() => asyncBlockBasedVideoTag(tags))
  5504. .then(() => asyncBlockVerticalVideo(videoInfo.dimension))
  5505. .then(() => asyncBlockVideoCopyright(videoInfo.copyright))
  5506. .then(() => asyncBlockChargeVideo(videoInfo?.is_upower_exclusive))
  5507. .then(() => asyncBlockFollowedVideo(videoInfo?.following))
  5508. .then(() => asyncBlockSeniorMember(userInfo.is_senior_member))
  5509. .then(() => asyncBlockVideoTeamMember(userInfo.mid))
  5510. .then(() => asyncBlockVideoLikeRate(videoInfo.like, videoInfo.view))
  5511. .then(() => asyncBlockVideoInteractiveRate(videoInfo.danmaku, videoInfo.reply, videoInfo.view))
  5512. .then(() => asyncBlockVideoTripleRate(videoInfo.favorite, videoInfo.coin, videoInfo.share, videoInfo.view))
  5513. .then(() => asyncBlockVideoCoinLikesRatioRate(videoInfo.coin, videoInfo.like))
  5514. .then(() => asyncBlockTimeRangeMasking(videoInfo.pubdate))
  5515. .then(() => asyncBlockVideoDesc(videoInfo?.desc))
  5516. .then(() => asyncBlockSignature(videoInfo?.sign))
  5517. .then(() => asyncBlockAvatarPendant(userInfo?.pendant?.name))
  5518. .then(() => asyncBlockByLevel(userInfo?.current_level || -1))
  5519. .then(() => asyncBlockGender(userInfo?.sex))
  5520. .then(() => asyncBlockUserVip(userInfo.vip.type))
  5521. .catch((v) => {
  5522. const msg = v['msg'];
  5523. if (msg) {
  5524. console.warn(msg);
  5525. }
  5526. eventEmitter.send('event-屏蔽视频元素', {res: v, method, videoData});
  5527. });
  5528. };
  5529. eventEmitter.on('添加热门视频屏蔽按钮', (data) => {
  5530. shielding.addBlockButton(data, "gz_shielding_button", ["right", "bottom"]);
  5531. });
  5532. eventEmitter.on('视频添加屏蔽按钮-BewlyBewly', (data) => {
  5533. shielding.addBlockButton(data, "gz_shielding_button", ['right', 'bottom']);
  5534. });
  5535. eventEmitter.on('视频添加屏蔽按钮', (data) => {
  5536. shielding.addBlockButton(data, "gz_shielding_button", ["right"]);
  5537. });
  5538. var video_shielding = {
  5539. shieldingVideoDecorated
  5540. };
  5541. const isHome = (url, title) => {
  5542. if (title !== "哔哩哔哩 (゜-゜)つロ 干杯~-bilibili") {
  5543. return false
  5544. }
  5545. if (url === 'https://www.bilibili.com/') {
  5546. return true
  5547. }
  5548. return url.includes('https://www.bilibili.com/?spm_id_from=')
  5549. };
  5550. const deDesktopDownloadTipEl = async () => {
  5551. const el = await elUtil.findElementUntilFound(".desktop-download-tip");
  5552. el?.remove();
  5553. const log = "已删除下载提示";
  5554. console.log(log, el);
  5555. };
  5556. const getChangeTheVideoElList = async () => {
  5557. const elList = await elUtil.findElementsUntilFound(".container.is-version8>.feed-card");
  5558. const list = [];
  5559. for (let el of elList) {
  5560. try {
  5561. const tempData = getVideoData(el);
  5562. const {userUrl} = tempData;
  5563. const videoUrl = el.querySelector(".bili-video-card__info--tit>a")?.href || null;
  5564. if (!userUrl.includes("//space.bilibili.com/")) {
  5565. el?.remove();
  5566. const log = "遍历换一换视频列表中检测到异常内容,已将该元素移除";
  5567. console.log(log, el);
  5568. continue;
  5569. }
  5570. const items = {
  5571. ...tempData, ...{
  5572. videoUrl,
  5573. el,
  5574. insertionPositionEl: el.querySelector(".bili-video-card__info--bottom"),
  5575. explicitSubjectEl: el.querySelector(".bili-video-card__info")
  5576. }
  5577. };
  5578. if (videoUrl?.includes('www.bilibili.com/video')) {
  5579. items.bv = elUtil.getUrlBV(videoUrl);
  5580. }
  5581. list.push(items);
  5582. } catch (e) {
  5583. el.remove();
  5584. console.warn("获取视频信息失败");
  5585. }
  5586. }
  5587. return list
  5588. };
  5589. const getVideoData = (el) => {
  5590. const title = el.querySelector(".bili-video-card__info--tit").title;
  5591. const name = el.querySelector(".bili-video-card__info--author").textContent.trim();
  5592. let nPlayCount = el.querySelector('.bili-video-card__stats--text')?.textContent.trim();
  5593. nPlayCount = sFormatUtil.toPlayCountOrBulletChat(nPlayCount);
  5594. let nBulletChat = el.querySelector('.bili-video-card__stats--text')?.textContent.trim();
  5595. nBulletChat = sFormatUtil.toPlayCountOrBulletChat(nBulletChat);
  5596. let nDuration = el.querySelector('.bili-video-card__stats__duration')?.textContent.trim();
  5597. nDuration = sFormatUtil.timeStringToSeconds(nDuration);
  5598. const userUrl = el.querySelector(".bili-video-card__info--owner").getAttribute("href");
  5599. const uid = elUtil.getUrlUID(userUrl);
  5600. return {
  5601. title,
  5602. name,
  5603. uid,
  5604. nPlayCount,
  5605. nBulletChat,
  5606. nDuration,
  5607. userUrl
  5608. }
  5609. };
  5610. const getHomeVideoELList = async () => {
  5611. const elList = await elUtil.findElementsUntilFound(".container.is-version8>.bili-video-card");
  5612. let list = [];
  5613. for (let el of elList) {
  5614. try {
  5615. const tempData = getVideoData(el);
  5616. const {userUrl} = tempData;
  5617. if (!userUrl.includes("//space.bilibili.com/")) {
  5618. el?.remove();
  5619. const log = "遍历换一换视频列表下面列表时检测到异常内容,已将该元素移除";
  5620. eventEmitter.send('打印信息', log);
  5621. console.log(log, el);
  5622. continue;
  5623. }
  5624. const videoUrl = el.querySelector(".bili-video-card__info--tit>a")?.href;
  5625. const items = {
  5626. ...tempData, ...{
  5627. videoUrl,
  5628. el,
  5629. insertionPositionEl: el.querySelector(".bili-video-card__info--bottom"),
  5630. explicitSubjectEl: el.querySelector(".bili-video-card__info")
  5631. }
  5632. };
  5633. if (videoUrl?.includes('www.bilibili.com/video')) {
  5634. items.bv = elUtil.getUrlBV(videoUrl);
  5635. }
  5636. list.push(items);
  5637. } catch (e) {
  5638. el?.remove();
  5639. console.log("遍历视频列表中检测到异常内容,已将该元素移除;");
  5640. }
  5641. }
  5642. return list;
  5643. };
  5644. const startClearExcessContentList = () => {
  5645. if (globalValue.adaptationBAppCommerce) return;
  5646. setInterval(() => {
  5647. const otherElList = document.querySelectorAll(".floor-single-card");
  5648. const liveList = document.querySelectorAll(".bili-live-card");
  5649. const elList = [...otherElList, ...liveList];
  5650. let rightAdEl = document.querySelector('.adcard');
  5651. if (rightAdEl) {
  5652. elList.push(rightAdEl);
  5653. }
  5654. rightAdEl = document.querySelector('.fixed-card');
  5655. if (rightAdEl) {
  5656. elList.push(rightAdEl);
  5657. }
  5658. for (let el of elList) {
  5659. el?.remove();
  5660. console.log("已清理首页视频列表中多余的内容,直播选项卡,右侧大卡片广告,右侧小卡片广告等", el);
  5661. }
  5662. }, 1000);
  5663. console.log("已启动每秒清理首页视频列表中多余的内容");
  5664. };
  5665. const startShieldingChangeVideoList = async () => {
  5666. const list = await getChangeTheVideoElList();
  5667. for (let videoData of list) {
  5668. if (video_shielding.shieldingVideoDecorated(videoData)) {
  5669. continue;
  5670. }
  5671. eventEmitter.send('视频添加屏蔽按钮', {data: videoData, maskingFunc: startShieldingChangeVideoList});
  5672. }
  5673. };
  5674. const startDebounceShieldingChangeVideoList = defUtil.debounce(startShieldingChangeVideoList, 200);
  5675. const startShieldingHomeVideoList = async () => {
  5676. const homeVideoELList = await getHomeVideoELList();
  5677. for (const videoData of homeVideoELList) {
  5678. if (video_shielding.shieldingVideoDecorated(videoData)) {
  5679. continue;
  5680. }
  5681. eventEmitter.send('视频添加屏蔽按钮', {data: videoData, maskingFunc: startShieldingHomeVideoList});
  5682. }
  5683. };
  5684. const startDebounceShieldingHomeVideoList = defUtil.debounce(startShieldingHomeVideoList, 500);
  5685. const scrollMouseUpAndDown = async () => {
  5686. if (globalValue.adaptationBAppCommerce) return;
  5687. await defUtil.smoothScroll(false, 100);
  5688. return defUtil.smoothScroll(true, 600);
  5689. };
  5690. var bilibiliHome = {
  5691. isHome,
  5692. startClearExcessContentList,
  5693. startDebounceShieldingChangeVideoList,
  5694. startDebounceShieldingHomeVideoList,
  5695. scrollMouseUpAndDown,
  5696. deDesktopDownloadTipEl,
  5697. getVideoData
  5698. };
  5699. const isSearch = (url) => {
  5700. return url.includes("search.bilibili.com")
  5701. };
  5702. const isSearchVideoNetWorkUrl = (netUrl) => {
  5703. if (netUrl.includes('api.bilibili.com/x/web-interface/wbi/search/all/v2')) return true;
  5704. if (!netUrl.includes('api.bilibili.com/x/web-interface/wbi/search/type')) return false;
  5705. const parseUrl = defUtil.parseUrl(netUrl);
  5706. const search_type = parseUrl.queryParams['search_type'] || null;
  5707. return search_type === 'video';
  5708. };
  5709. const isSearchLiveRoomNetWorkUrl = (netUrl) => {
  5710. if (!netUrl.includes('api.bilibili.com/x/web-interface/wbi/search/type')) return false;
  5711. const parseUrl = defUtil.parseUrl(netUrl);
  5712. const search_type = parseUrl.queryParams['search_type'] || null;
  5713. return search_type === 'live';
  5714. };
  5715. const getVideoList$1 = async (css) => {
  5716. const elList = await elUtil.findElements(css, {interval: 200});
  5717. const list = [];
  5718. for (let el of elList) {
  5719. const title = el.querySelector(".bili-video-card__info--tit").title;
  5720. const userEl = el.querySelector(".bili-video-card__info--owner");
  5721. if (userEl === null) {
  5722. console.log("获取不到该视频卡片的用户地址,", el);
  5723. el?.remove();
  5724. continue
  5725. }
  5726. const userUrl = userEl.getAttribute("href");
  5727. if (!userUrl.includes("//space.bilibili.com/")) {
  5728. el?.remove();
  5729. console.log("移除了非视频内容", userUrl, el);
  5730. continue;
  5731. }
  5732. const videoUrl = el.querySelector(".bili-video-card__info--right>a")?.href;
  5733. if (videoUrl?.includes('live.bilibili.com/')) {
  5734. continue
  5735. }
  5736. const bv = elUtil.getUrlBV(videoUrl);
  5737. const uid = elUtil.getUrlUID(userUrl);
  5738. const name = userEl.querySelector(".bili-video-card__info--author").textContent.trim();
  5739. const bili_video_card__stats_item = el.querySelectorAll('.bili-video-card__stats--item');
  5740. let nPlayCount = bili_video_card__stats_item[0]?.textContent.trim();
  5741. nPlayCount = sFormatUtil.toPlayCountOrBulletChat(nPlayCount);
  5742. let nBulletChat = bili_video_card__stats_item[1]?.textContent.trim();
  5743. nBulletChat = sFormatUtil.toPlayCountOrBulletChat(nBulletChat);
  5744. let nDuration = el.querySelector('.bili-video-card__stats__duration')?.textContent.trim();
  5745. nDuration = sFormatUtil.timeStringToSeconds(nDuration);
  5746. list.push({
  5747. title,
  5748. userUrl,
  5749. name,
  5750. uid,
  5751. bv,
  5752. nPlayCount,
  5753. nBulletChat,
  5754. nDuration,
  5755. el,
  5756. videoUrl,
  5757. insertionPositionEl: el.querySelector(".bili-video-card__info--bottom"),
  5758. explicitSubjectEl: el.querySelector(".bili-video-card__info")
  5759. });
  5760. }
  5761. return list;
  5762. };
  5763. const getTabComprehensiveSortedVideoList = () => {
  5764. return getVideoList$1(".video.i_wrapper.search-all-list>.video-list>div");
  5765. };
  5766. const getOtherVideoList = () => {
  5767. return getVideoList$1(".search-page.search-page-video>.video-list.row>div:not(:empty)");
  5768. };
  5769. const startShieldingCSVideoList = async () => {
  5770. const list = await getTabComprehensiveSortedVideoList();
  5771. for (let videoData of list) {
  5772. if (video_shielding.shieldingVideoDecorated(videoData)) {
  5773. continue;
  5774. }
  5775. eventEmitter.send('视频添加屏蔽按钮', {data: videoData, maskingFunc: startShieldingCSVideoList});
  5776. }
  5777. };
  5778. const startShieldingOtherVideoList = async () => {
  5779. const list = await getOtherVideoList();
  5780. for (let videoData of list) {
  5781. if (video_shielding.shieldingVideoDecorated(videoData)) {
  5782. continue;
  5783. }
  5784. eventEmitter.send('视频添加屏蔽按钮', {data: videoData, maskingFunc: startShieldingOtherVideoList});
  5785. }
  5786. };
  5787. const getTwTabActiveItem = async () => {
  5788. const twoTabActiveItem = await elUtil.findElement('.vui_button.vui_button--tab.vui_button--active.mr_sm', {interval: 200});
  5789. const twoTabActiveItemLabel = twoTabActiveItem.textContent.trim();
  5790. return {el: twoTabActiveItemLabel, label: twoTabActiveItemLabel}
  5791. };
  5792. const startShieldingVideoList$6 = async () => {
  5793. const topTabActiveItem = await elUtil.findElement('.vui_tabs--nav-item.vui_tabs--nav-item-active', {interval: 200});
  5794. const topTabActiveItemLabel = topTabActiveItem.textContent.trim();
  5795. console.log(topTabActiveItemLabel);
  5796. if (topTabActiveItemLabel !== '综合') {
  5797. await startShieldingOtherVideoList();
  5798. return
  5799. }
  5800. const {label} = await getTwTabActiveItem();
  5801. if (label !== '综合排序') {
  5802. await startShieldingOtherVideoList();
  5803. return
  5804. }
  5805. const parseUrl = defUtil.parseUrl(window.location.href);
  5806. if (parseUrl.queryParams['page']) {
  5807. await startShieldingOtherVideoList();
  5808. } else {
  5809. await startShieldingCSVideoList();
  5810. await processingExactSearchVideoCardContent();
  5811. }
  5812. };
  5813. const processingExactSearchVideoCardContent = async () => {
  5814. let res;
  5815. try {
  5816. res = await elUtil.findElement('.user-list.search-all-list', {interval: 50, timeout: 4000});
  5817. } catch (e) {
  5818. return
  5819. }
  5820. let el;
  5821. if (!res.state) {
  5822. return
  5823. }
  5824. el = res.data;
  5825. const infoCardEl = el.querySelector('.info-card');
  5826. const userNameEl = infoCardEl.querySelector('.user-name');
  5827. const name = userNameEl.textContent.trim();
  5828. const userUrl = userNameEl.href;
  5829. const uid = elUtil.getUrlUID(userUrl);
  5830. if (ruleMatchingUtil.exactMatch(ruleKeyListData$1.getPreciseUidArr(), uid)) {
  5831. el.remove();
  5832. eventEmitter.send('打印信息', `根据精确uid匹配到用户${name}-【${uid}】`);
  5833. return
  5834. }
  5835. let fuzzyMatch = ruleMatchingUtil.fuzzyMatch(ruleKeyListData$1.getNameArr(), name);
  5836. if (fuzzyMatch) {
  5837. el.remove();
  5838. eventEmitter.send('打印信息', `根据模糊用户名【${fuzzyMatch}】匹配到用户${name}-【${uid}】`);
  5839. return
  5840. }
  5841. fuzzyMatch = ruleMatchingUtil.regexMatch(ruleKeyListData$1.getNameCanonical(), name);
  5842. if (fuzzyMatch) {
  5843. el.remove();
  5844. eventEmitter.send('打印信息', `根据正则用户名【${fuzzyMatch}】匹配到用户${name}-【${uid}】`);
  5845. return
  5846. }
  5847. const insertionPositionEl = el.querySelector('.info-card.flex_start');
  5848. shielding.addBlockButton({
  5849. data: {
  5850. name,
  5851. uid,
  5852. insertionPositionEl,
  5853. }
  5854. });
  5855. const videoElList = el.querySelectorAll('.video-list>.video-list-item');
  5856. const list = [];
  5857. for (let videoEl of videoElList) {
  5858. const titleEl = videoEl.querySelector('.bili-video-card__info--right>a');
  5859. const videoUrl = titleEl.href;
  5860. const bv = elUtil.getUrlBV(videoUrl);
  5861. const title = titleEl.textContent.trim();
  5862. let nDuration = videoEl.querySelector('.bili-video-card__stats__duration')?.textContent.trim();
  5863. nDuration = sFormatUtil.timeStringToSeconds(nDuration);
  5864. let nPlayCount = videoEl.querySelector('.bili-video-card__stats--item>span')?.textContent.trim();
  5865. nPlayCount = sFormatUtil.toPlayCountOrBulletChat(nPlayCount);
  5866. list.push({
  5867. title,
  5868. userUrl,
  5869. name,
  5870. uid,
  5871. bv,
  5872. nPlayCount,
  5873. nDuration,
  5874. el: videoEl,
  5875. videoUrl
  5876. });
  5877. }
  5878. for (let videoData of list) {
  5879. video_shielding.shieldingVideoDecorated(videoData);
  5880. }
  5881. };
  5882. const delFooterContent = () => {
  5883. if (!gmUtil.getData('isRemoveSearchBottomContent', false)) {
  5884. return
  5885. }
  5886. elUtil.findElement('#biliMainFooter').then(el => {
  5887. el.remove();
  5888. eventEmitter.send('打印信息', '已删除底部内容');
  5889. });
  5890. };
  5891. var searchModel = {
  5892. isSearch,
  5893. startShieldingVideoList: startShieldingVideoList$6,
  5894. delFooterContent,
  5895. isSearchVideoNetWorkUrl,
  5896. isSearchLiveRoomNetWorkUrl
  5897. };
  5898. const isVideoPlayPage = (url = window.location.href) => {
  5899. return url.includes("www.bilibili.com/video");
  5900. };
  5901. const selectUserBlocking = async () => {
  5902. const {state} = await elUtil.findElement('.header.can-pointer', {timeout: 1800});
  5903. if (state) {
  5904. const elList = document.querySelectorAll('.container>.membersinfo-upcard-wrap>.membersinfo-upcard');
  5905. const list = [];
  5906. for (const el of elList) {
  5907. const userUrl = el.querySelector('.avatar').href;
  5908. const uid = elUtil.getUrlUID(userUrl);
  5909. const name = el.querySelector('.staff-name').textContent.trim();
  5910. list.push({
  5911. label: `用户-name=${name}-uid=${uid}`,
  5912. uid
  5913. });
  5914. }
  5915. eventEmitter.send('sheet-dialog', {
  5916. title: '选择要屏蔽的用户(uid精确)',
  5917. list,
  5918. optionsClick: (item) => {
  5919. ruleUtil.addRulePreciseUid(item.uid);
  5920. return true
  5921. }
  5922. });
  5923. } else {
  5924. const el = document.querySelector('.up-info-container');
  5925. const nameEl = el.querySelector('.up-info--right a.up-name');
  5926. const name = nameEl.textContent.trim();
  5927. const userUrl = nameEl.href;
  5928. const uid = elUtil.getUrlUID(userUrl);
  5929. console.log('点击了屏蔽按钮', name, userUrl, uid);
  5930. eventEmitter.invoke('el-confirm', `用户uid=${uid}-name=${name}`, 'uid精确屏蔽方式').then(() => {
  5931. if (uid === -1) {
  5932. eventEmitter.send('el-msg', "该页面数据不存在uid字段");
  5933. return;
  5934. }
  5935. ruleUtil.addRulePreciseUid(uid);
  5936. });
  5937. }
  5938. };
  5939. const getGetTheVideoListOnTheRight$1 = async () => {
  5940. await elUtil.findElementUntilFound(".video-page-card-small .b-img img");
  5941. delAd();
  5942. delGameAd();
  5943. const elList = await elUtil.findElements(".rec-list>.video-page-card-small", {interval: 1000});
  5944. const nextPlayEl = document.querySelector('.next-play>.video-page-card-small');
  5945. if (nextPlayEl) {
  5946. elList.push(nextPlayEl);
  5947. }
  5948. const list = [];
  5949. for (let el of elList) {
  5950. try {
  5951. const elInfo = el.querySelector(".info");
  5952. const title = elInfo.querySelector(".title").title;
  5953. const name = elInfo.querySelector(".upname .name").textContent.trim();
  5954. const userUrl = elInfo.querySelector(".upname>a").href;
  5955. const uid = elUtil.getUrlUID(userUrl);
  5956. const playInfo = el.querySelector('.playinfo').innerHTML.trim();
  5957. const videoUrl = el.querySelector(".info>a").href;
  5958. const bv = elUtil.getUrlBV(videoUrl);
  5959. let nPlayCount = playInfo.match(/<\/svg>(.*)<svg/s)?.[1].trim();
  5960. nPlayCount = sFormatUtil.toPlayCountOrBulletChat(nPlayCount);
  5961. let nBulletChat = playInfo.match(/class="dm".+<\/svg>(.+)$/s)?.[1].trim();
  5962. nBulletChat = sFormatUtil.toPlayCountOrBulletChat(nBulletChat);
  5963. let nDuration = el.querySelector('.duration')?.textContent.trim();
  5964. nDuration = sFormatUtil.timeStringToSeconds(nDuration);
  5965. list.push({
  5966. title,
  5967. userUrl,
  5968. name,
  5969. uid,
  5970. bv,
  5971. nPlayCount,
  5972. nBulletChat,
  5973. nDuration,
  5974. el,
  5975. videoUrl,
  5976. insertionPositionEl: el.querySelector(".playinfo"),
  5977. explicitSubjectEl: elInfo
  5978. });
  5979. } catch (e) {
  5980. console.error("获取右侧视频列表失败:", e);
  5981. }
  5982. }
  5983. return list;
  5984. };
  5985. const startShieldingVideoList$5 = () => {
  5986. if (localMKData.isDelPlayerPageRightVideoList()) {
  5987. return
  5988. }
  5989. getGetTheVideoListOnTheRight$1().then((videoList) => {
  5990. for (let videoData of videoList) {
  5991. if (video_shielding.shieldingVideoDecorated(videoData)) {
  5992. continue;
  5993. }
  5994. eventEmitter.send('视频添加屏蔽按钮', {data: videoData, maskingFunc: startShieldingVideoList$5});
  5995. }
  5996. });
  5997. };
  5998. const findTheExpandButtonForTheListOnTheRightAndBindTheEvent$2 = () => {
  5999. setTimeout(() => {
  6000. elUtil.findElementUntilFound(".rec-footer", {interval: 2000}).then((el) => {
  6001. console.log("找到右侧视频列表的展开按钮", el);
  6002. el.addEventListener("click", () => {
  6003. startShieldingVideoList$5();
  6004. });
  6005. });
  6006. }, 3000);
  6007. };
  6008. const getPlayerVideoList = async () => {
  6009. const elList = await elUtil.findElements('.bpx-player-ending-related>.bpx-player-ending-related-item');
  6010. const data = {list: [], cancelEl: null};
  6011. for (const el of elList) {
  6012. const title = el.querySelector('.bpx-player-ending-related-item-title')?.textContent.trim();
  6013. const cancelEl = el.querySelector('.bpx-player-ending-related-item-cancel');
  6014. if (cancelEl) {
  6015. data.cancelEl = cancelEl;
  6016. }
  6017. data.list.push({
  6018. title,
  6019. el
  6020. });
  6021. }
  6022. return data
  6023. };
  6024. const getVideoPlayerEndingPanelEl = async () => {
  6025. return await elUtil.findElement('#bilibili-player .bpx-player-ending-wrap>.bpx-player-ending-panel',
  6026. {interval: 50})
  6027. };
  6028. const setVideoPlayerEnded = async () => {
  6029. const videoEl = await elUtil.findElement('#bilibili-player video');
  6030. const funcStart = async () => {
  6031. const res = await getPlayerVideoList();
  6032. for (let {el, title} of res.list) {
  6033. let matching = ruleMatchingUtil.fuzzyMatch(ruleKeyListData$1.getTitleArr(), title);
  6034. if (matching !== null) {
  6035. eventEmitter.send('打印信息', `根据-模糊标题-【${matching}】-屏蔽视频:${title}`);
  6036. el.remove();
  6037. continue
  6038. }
  6039. matching = ruleMatchingUtil.regexMatch(ruleKeyListData$1.getTitleCanonicalArr(), title);
  6040. if (matching !== null) {
  6041. eventEmitter.send('打印信息', `根据-正则标题-【${matching}】-屏蔽视频:${title}`);
  6042. el.remove();
  6043. }
  6044. }
  6045. };
  6046. videoEl.addEventListener('ended', () => {
  6047. console.log('视频播放结束');
  6048. funcStart();
  6049. if (localMKData.isDelPlayerEndingPanel()) {
  6050. getVideoPlayerEndingPanelEl().then(el => {
  6051. el.remove();
  6052. eventEmitter.send('打印信息', '已删除播放页播放器中推荐层');
  6053. });
  6054. }
  6055. });
  6056. };
  6057. const delAd = () => {
  6058. if (!gmUtil.getData('isDelPlayerPageAd', false)) {
  6059. return
  6060. }
  6061. elUtil.findElements('[class|=ad],#slide_ad').then(elList => {
  6062. for (const el of elList) {
  6063. el.style.display = 'none';
  6064. }
  6065. eventEmitter.send('打印信息', '隐藏了播放页的页面广告');
  6066. });
  6067. };
  6068. const delRightVideoList = () => {
  6069. if (!localMKData.isDelPlayerPageRightVideoList()) {
  6070. return
  6071. }
  6072. elUtil.findElement('.recommend-list-v1').then(el => {
  6073. el.style.visibility = "hidden";
  6074. eventEmitter.send('打印信息', '屏蔽了播放页的右侧推荐列表');
  6075. });
  6076. };
  6077. const delGameAd = () => {
  6078. if (!gmUtil.getData('isDelPlayerPageRightGameAd', false)) {
  6079. return
  6080. }
  6081. elUtil.findElement('.video-page-game-card-small', {timeout: 10000}).then(({state, data}) => {
  6082. if (!state) {
  6083. eventEmitter.send('打印信息', '没有找到播放页的右侧游戏推荐');
  6084. return
  6085. }
  6086. data?.remove();
  6087. eventEmitter.send('打印信息', '屏蔽了游戏推荐');
  6088. });
  6089. };
  6090. const delBottomCommentApp = () => {
  6091. if (!localMKData.isDelBottomComment()) {
  6092. return
  6093. }
  6094. elUtil.findElement('#commentapp').then(el => {
  6095. el?.remove();
  6096. eventEmitter.send('打印信息', '移除了页面底部的评论区');
  6097. });
  6098. };
  6099. const delElManagement = () => {
  6100. if (localMKData.isDelPlayerPageRightVideoList()) {
  6101. delAd();
  6102. }
  6103. delRightVideoList();
  6104. delBottomCommentApp();
  6105. };
  6106. var videoPlayModel = {
  6107. isVideoPlayPage,
  6108. startShieldingVideoList: startShieldingVideoList$5,
  6109. findTheExpandButtonForTheListOnTheRightAndBindTheEvent: findTheExpandButtonForTheListOnTheRightAndBindTheEvent$2,
  6110. selectUserBlocking,
  6111. setVideoPlayerEnded,
  6112. delElManagement
  6113. };
  6114. const getPlayCountAndBulletChatAndDuration = (el) => {
  6115. const playInfo = el.querySelector('.playinfo').innerHTML.trim();
  6116. let nPlayCount = playInfo.match(/<\/svg>(.*)<svg/s)?.[1].trim();
  6117. nPlayCount = sFormatUtil.toPlayCountOrBulletChat(nPlayCount);
  6118. let nBulletChat = playInfo.match(/class="dm-icon".+<\/svg>(.+)$/s)?.[1].trim();
  6119. nBulletChat = sFormatUtil.toPlayCountOrBulletChat(nBulletChat);
  6120. let nDuration = el.querySelector('.duration')?.textContent.trim();
  6121. nDuration = sFormatUtil.timeStringToSeconds(nDuration);
  6122. return {
  6123. nPlayCount, nBulletChat, nDuration
  6124. }
  6125. };
  6126. const getRightVideoDataList$1=(elList)=>{
  6127. const list = [];
  6128. for (let el of elList) {
  6129. const title = el.querySelector(".title").textContent.trim();
  6130. const userInfoEl = el.querySelector(".upname");
  6131. const name = userInfoEl.querySelector(".name").textContent.trim();
  6132. const userUrl = userInfoEl.href;
  6133. const uid = elUtil.getUrlUID(userUrl);
  6134. const videoUrl = el.querySelector(".info>a").href;
  6135. const bv = elUtil.getUrlBV(videoUrl);
  6136. list.push({
  6137. ...getPlayCountAndBulletChatAndDuration(el), ...{
  6138. title,
  6139. name,
  6140. userUrl,
  6141. videoUrl,
  6142. uid,
  6143. bv,
  6144. el,
  6145. insertionPositionEl: el.querySelector(".playinfo"),
  6146. explicitSubjectEl: el.querySelector(".info")
  6147. }
  6148. });
  6149. }
  6150. return list;
  6151. };
  6152. var generalFuc = {getRightVideoDataList: getRightVideoDataList$1};
  6153. const iscCollectionVideoPlayPage = (url) => {
  6154. return url.includes("www.bilibili.com/list/ml")
  6155. };
  6156. const getGetTheVideoListOnTheRight = async () => {
  6157. const elList = await elUtil.findElementsUntilFound(".recommend-list-container>.video-card");
  6158. return generalFuc.getRightVideoDataList(elList);
  6159. };
  6160. const startShieldingVideoList$4 = () => {
  6161. getGetTheVideoListOnTheRight().then((videoList) => {
  6162. const css = {right: "123px"};
  6163. for (let videoData of videoList) {
  6164. if (video_shielding.shieldingVideoDecorated(videoData)) continue;
  6165. videoData.css = css;
  6166. eventEmitter.send('视频添加屏蔽按钮', {data: videoData, maskingFunc: startShieldingVideoList$4});
  6167. }
  6168. });
  6169. };
  6170. const findTheExpandButtonForTheListOnTheRightAndBindTheEvent$1 = () => {
  6171. setTimeout(() => {
  6172. elUtil.findElementUntilFound(".rec-footer", {interval: 2000}).then((el) => {
  6173. el.addEventListener("click", () => {
  6174. startShieldingVideoList$4();
  6175. });
  6176. });
  6177. }, 3000);
  6178. };
  6179. var collectionVideoPlayPageModel = {
  6180. iscCollectionVideoPlayPage,
  6181. startShieldingVideoList: startShieldingVideoList$4,
  6182. findTheExpandButtonForTheListOnTheRightAndBindTheEvent: findTheExpandButtonForTheListOnTheRightAndBindTheEvent$1
  6183. };
  6184. const addEventListenerUrlChange = (callback) => {
  6185. let oldUrl = window.location.href;
  6186. setInterval(() => {
  6187. const newUrl = window.location.href;
  6188. if (oldUrl === newUrl) return;
  6189. oldUrl = newUrl;
  6190. const title = document.title;
  6191. callback(newUrl, oldUrl, title);
  6192. }, 1000);
  6193. };
  6194. const addEventListenerNetwork = (callback) => {
  6195. new PerformanceObserver(() => {
  6196. const entries = performance.getEntriesByType('resource');
  6197. const windowUrl = window.location.href;
  6198. const winTitle = document.title;
  6199. for (let entry of entries) {
  6200. const url = entry.name;
  6201. const initiatorType = entry.initiatorType;
  6202. if (initiatorType === "img" || initiatorType === "css" || initiatorType === "link" || initiatorType === "beacon") {
  6203. continue;
  6204. }
  6205. callback(url, windowUrl,winTitle, initiatorType);
  6206. }
  6207. performance.clearResourceTimings();//清除资源时间
  6208. }).observe({entryTypes: ['resource']});
  6209. };
  6210. function watchElementListLengthWithInterval(selector, callback, config={}) {
  6211. const defConfig = {};
  6212. config = {...defConfig, ...config};
  6213. let previousLength = -1;
  6214. const timer = setInterval(() => {
  6215. if (previousLength === -1) {
  6216. previousLength = document.querySelectorAll(selector).length;
  6217. return
  6218. }
  6219. const currentElements = document.querySelectorAll(selector);
  6220. const currentLength = currentElements.length;
  6221. if (currentLength !== previousLength) {
  6222. previousLength = currentLength;
  6223. callback({
  6224. action: currentLength > previousLength ? 'add' : 'del',
  6225. elements: currentElements,
  6226. length: currentLength
  6227. }
  6228. );
  6229. }
  6230. },
  6231. config.interval
  6232. );
  6233. return stop = () => {
  6234. clearInterval(timer);
  6235. };
  6236. }
  6237. var watch = {
  6238. addEventListenerUrlChange,
  6239. addEventListenerNetwork,
  6240. watchElementListLengthWithInterval
  6241. };
  6242. const blockCommentWordLimit = (content) => {
  6243. const commentWordLimit = localMKData.getCommentWordLimitVal();
  6244. if (commentWordLimit.length < 3) {
  6245. return returnTempVal
  6246. }
  6247. if (content.length > commentWordLimit) {
  6248. return {state: true, type: '屏蔽字数限制', matching: `字数限制为${commentWordLimit}`}
  6249. }
  6250. return returnTempVal;
  6251. };
  6252. const shieldingComment = (commentsData) => {
  6253. const {content, uid, name, level = -1} = commentsData;
  6254. let returnVal = blockUserUidAndName(uid, name);
  6255. if (returnVal.state) {
  6256. return returnVal
  6257. }
  6258. returnVal = blockComment(content);
  6259. if (returnVal.state) {
  6260. return returnVal
  6261. }
  6262. if (level !== -1) {
  6263. return blockByLevel(level);
  6264. }
  6265. return blockCommentWordLimit(content);
  6266. };
  6267. const shieldingComments = (commentsDataList) => {
  6268. for (let commentsData of commentsDataList) {
  6269. if (shieldingCommentDecorated(commentsData)) continue;
  6270. eventEmitter.send('评论添加屏蔽按钮', commentsData);
  6271. const {replies = []} = commentsData;
  6272. if (replies.length === 0) continue;
  6273. for (let reply of replies) {
  6274. if (shieldingCommentDecorated(reply)) continue;
  6275. eventEmitter.send('评论添加屏蔽按钮', reply);
  6276. }
  6277. }
  6278. };
  6279. const shieldingCommentDecorated = (commentsData) => {
  6280. const {state, type, matching} = shieldingComment(commentsData);
  6281. if (state) {
  6282. commentsData.el?.remove();
  6283. eventEmitter.send('屏蔽评论信息', type, matching, commentsData);
  6284. }
  6285. eventEmitter.send('event-评论通知替换关键词', commentsData);
  6286. return state;
  6287. };
  6288. var comments_shielding = {
  6289. shieldingComment,
  6290. shieldingComments,
  6291. shieldingCommentDecorated
  6292. };
  6293. const shieldingLiveRoomContentDecorated = (liveRoomContent) => {
  6294. let {state, type, matching} = comments_shielding.shieldingComment(liveRoomContent);
  6295. const {el, fansMedal} = liveRoomContent;
  6296. if (fansMedal !== null) {
  6297. if (ruleMatchingUtil.exactMatch(ruleKeyListData$1.getPreciseFanCardArr(), fansMedal)) {
  6298. el?.remove();
  6299. state = true;
  6300. type = "精确粉丝牌";
  6301. }
  6302. }
  6303. if (state) {
  6304. el?.remove();
  6305. }
  6306. if (type) {
  6307. const infoHtml = output_informationTab.getLiveRoomCommentInfoHtml(type, matching, liveRoomContent);
  6308. eventEmitter.send('打印信息', infoHtml);
  6309. }
  6310. return state;
  6311. };
  6312. const shieldingLiveRoom = (liveRoomData) => {
  6313. const {name, title, partition, uid = -1} = liveRoomData;
  6314. let returnVal;
  6315. if (uid !== -1) {
  6316. if (blockCheckWhiteUserUid(uid)) {
  6317. return returnTempVal;
  6318. }
  6319. returnVal = blockUserUidAndName(uid, name);
  6320. if (returnVal.state) {
  6321. return returnVal
  6322. }
  6323. }
  6324. returnVal = blockVideoOrOtherTitle(title);
  6325. if (returnVal.state) {
  6326. return returnVal
  6327. }
  6328. if (partition) {
  6329. if (ruleMatchingUtil.exactMatch(ruleKeyListData$1.getPrecisePartitionArr(), partition)) {
  6330. return {state: true, type: "精确直播分区"};
  6331. }
  6332. }
  6333. return returnTempVal;
  6334. };
  6335. const shieldingLiveRoomDecorated = (liveRoomData) => {
  6336. const {state, type, matching = null} = shieldingLiveRoom(liveRoomData);
  6337. if (state) {
  6338. liveRoomData.el?.remove();
  6339. const infoHtml = output_informationTab.getLiveRoomInfoHtml(type, matching, liveRoomData);
  6340. eventEmitter.send('打印信息', infoHtml);
  6341. }
  6342. return state;
  6343. };
  6344. const addLiveContentBlockButton = (commentsData) => {
  6345. shielding.addBlockButton(commentsData, "gz_shielding_live_danmaku_button");
  6346. };
  6347. var live_shielding = {
  6348. shieldingLiveRoomDecorated,
  6349. addLiveContentBlockButton
  6350. };
  6351. const isLiveRoom = (url) => {
  6352. return url.search('/live.bilibili.com/\\d+') !== -1;
  6353. };
  6354. const getChatItems = async () => {
  6355. const elList = await elUtil.findElementsUntilFound("#chat-items>div");
  6356. if (elList.length >= 200) {
  6357. for (let i = 0; i < 100; i++) {
  6358. elList[i]?.remove();
  6359. }
  6360. console.log("弹幕列表超过200,已删除前100条");
  6361. }
  6362. const list = [];
  6363. for (let el of elList) {
  6364. if (el.className === "chat-item convention-msg border-box") {
  6365. continue;
  6366. }
  6367. if (el.className === "chat-item misc-msg guard-buy") {
  6368. continue;
  6369. }
  6370. const name = el.getAttribute("data-uname");
  6371. if (name === null) {
  6372. continue;
  6373. }
  6374. const uid = el.getAttribute("data-uid");
  6375. const content = el.getAttribute("data-danmaku");
  6376. const timeStamp = el.getAttribute("data-timestamp");
  6377. const fansMedalEl = el.querySelector(".fans-medal-content");
  6378. const fansMedal = fansMedalEl === null ? null : fansMedalEl.textContent.trim();
  6379. list.push({
  6380. name,
  6381. uid,
  6382. content,
  6383. timeStamp,
  6384. fansMedal,
  6385. el,
  6386. insertionPositionEl: el,
  6387. explicitSubjectEl: el
  6388. });
  6389. }
  6390. return list;
  6391. };
  6392. const startShieldingLiveChatContents = async () => {
  6393. const commentsDataList = await getChatItems();
  6394. for (let commentsData of commentsDataList) {
  6395. if (shieldingLiveRoomContentDecorated(commentsData)) {
  6396. continue;
  6397. }
  6398. live_shielding.addLiveContentBlockButton({data: commentsData, maskingFunc: startShieldingLiveChatContents});
  6399. }
  6400. };
  6401. const addWatchLiveRoomChatItemsListener = () => {
  6402. const throttle = defUtil.throttle(startShieldingLiveChatContents, 1000);
  6403. watch.watchElementListLengthWithInterval("#chat-items>div", throttle);
  6404. };
  6405. var liveRoomModel = {
  6406. isLiveRoom,
  6407. addWatchLiveRoomChatItemsListener
  6408. };
  6409. const isVideoPlayWatchLaterPage = (url) => {
  6410. return url.startsWith("https://www.bilibili.com/list/watchlater")
  6411. };
  6412. const getRightVideoDataList = async () => {
  6413. const elList = await elUtil.findElementsUntilFound(".recommend-video-card.video-card");
  6414. return generalFuc.getRightVideoDataList(elList);
  6415. };
  6416. const startShieldingVideoList$3 = async () => {
  6417. const videoList = await getRightVideoDataList();
  6418. const css = {right: "123px"};
  6419. for (let videoData of videoList) {
  6420. videoData.css = css;
  6421. if (video_shielding.shieldingVideoDecorated(videoData)) continue;
  6422. eventEmitter.send('视频添加屏蔽按钮', {data: videoData, maskingFunc: startShieldingVideoList$3});
  6423. }
  6424. };
  6425. const startDebounceShieldingVideoList = defUtil.debounce(startShieldingVideoList$3, 1000);
  6426. const findTheExpandButtonForTheListOnTheRightAndBindTheEvent = () => {
  6427. elUtil.findElementsAndBindEvents(".rec-footer", startDebounceShieldingVideoList);
  6428. };
  6429. var videoPlayWatchLater = {
  6430. isVideoPlayWatchLaterPage,
  6431. startDebounceShieldingVideoList,
  6432. findTheExpandButtonForTheListOnTheRightAndBindTheEvent
  6433. };
  6434. const getBewlyEl = async () => {
  6435. let el = await elUtil.findElementUntilFound('#bewly', {interval: 500});
  6436. return el.shadowRoot;
  6437. };
  6438. const isBEWLYPage = (url) => {
  6439. return url.includes('www.bilibili.com/?page=') ||
  6440. url === 'https://www.bilibili.com/'
  6441. || url.startsWith('https://www.bilibili.com/?spm_id_from=')
  6442. };
  6443. const check_BEWLYPage_compatibility = async () => {
  6444. const {state} = await elUtil.findElement('#bewly', {interval: 200, timeout: 5000});
  6445. if (state) {
  6446. if (!globalValue.compatibleBEWLYBEWLY) {
  6447. eventEmitter.send('el-alert', '检测到使用BewlyBewly插件但未开启兼容选项,需要启用相关兼容选项才可正常使用');
  6448. }
  6449. } else {
  6450. if (globalValue.compatibleBEWLYBEWLY) {
  6451. eventEmitter.send('el-alert', '检测到未使用BewlyBewly插件却开启了兼容选项,请关闭兼容选项或启用bilibili_gate脚本后再启用相关兼容选项');
  6452. }
  6453. }
  6454. };
  6455. const getVideoList = async () => {
  6456. const beEl = await getBewlyEl();
  6457. const elList = await elUtil.findElementsUntilFound('.video-card.group', {doc: beEl});
  6458. const list = [];
  6459. for (let el of elList) {
  6460. const parentElement = el.parentElement.parentElement;
  6461. const title = el.querySelector('.keep-two-lines>a[title]').textContent.trim();
  6462. const userUrlEl = el.querySelector('.channel-name');
  6463. const userUrl = userUrlEl.href;
  6464. const uid = elUtil.getUrlUID(userUrl);
  6465. const name = userUrlEl.textContent.trim();
  6466. const playInfoEl = el.querySelector('[flex="~ items-center gap-1 wrap"]>div');
  6467. let playCount = playInfoEl.querySelector('span:first-child')?.textContent.trim() || null;
  6468. playCount = sFormatUtil.toPlayCountOrBulletChat(playCount);
  6469. let bulletChat = playInfoEl.querySelector('span:last-of-type')?.textContent.trim() || null;
  6470. if (playInfoEl.querySelectorAll('span').length < 2) {
  6471. bulletChat = -1;
  6472. } else {
  6473. bulletChat = sFormatUtil.toPlayCountOrBulletChat(bulletChat);
  6474. }
  6475. let nDuration = el.querySelector('[class*="group-hover:opacity-0"]')?.textContent.trim() || null;
  6476. nDuration = sFormatUtil.timeStringToSeconds(nDuration);
  6477. const videoUrl = el.querySelector('[href*="https://www.bilibili.com/video"]')?.href;
  6478. const bv = elUtil.getUrlBV(videoUrl);
  6479. const insertionPositionEl = el.querySelector('[class="group/desc"]');
  6480. list.push({
  6481. title,
  6482. name,
  6483. uid,
  6484. bv,
  6485. userUrl,
  6486. videoUrl,
  6487. playCount,
  6488. bulletChat,
  6489. nDuration,
  6490. el: parentElement,
  6491. insertionPositionEl,
  6492. explicitSubjectEl: parentElement
  6493. });
  6494. }
  6495. return list
  6496. };
  6497. const getRightTabs = async () => {
  6498. const beEl = await getBewlyEl();
  6499. const els = await elUtil.findElementsUntilFound(".dock-content-inner>.b-tooltip-wrapper", {doc: beEl});
  6500. const list = [];
  6501. for (let el of els) {
  6502. const label = el.querySelector('.b-tooltip').textContent.trim();
  6503. const active = !!el.querySelector('.dock-item.group.active');
  6504. list.push({label, active, el});
  6505. }
  6506. return list;
  6507. };
  6508. const getHistoryVideoDataList = async () => {
  6509. const beEL = await getBewlyEl();
  6510. const elList = await elUtil.findElementsUntilFound("a.group[flex][cursor-pointer]", {doc: beEL});
  6511. const list = [];
  6512. for (let el of elList) {
  6513. const titleEl = el.querySelector('h3.keep-two-lines');
  6514. const videoUrlEl = titleEl.parentElement;
  6515. const userEl = videoUrlEl.nextElementSibling;
  6516. const videoUrl = videoUrlEl.href;
  6517. const bv = elUtil.getUrlBV(videoUrl);
  6518. const userUrl = userEl.href;
  6519. const uid = elUtil.getUrlUID(userUrl);
  6520. const name = userEl.textContent.trim();
  6521. const title = titleEl?.textContent.trim();
  6522. const tempTime = el.querySelector('div[pos][rounded-8]')?.textContent.trim().split(/[\t\r\f\n\s]*/g).join("");
  6523. const match = tempTime?.match(/\/(.*)/);
  6524. let nDuration = match?.[1];
  6525. nDuration = sFormatUtil.timeStringToSeconds(nDuration);
  6526. list.push({
  6527. title,
  6528. userUrl,
  6529. name,
  6530. uid,
  6531. videoUrl,
  6532. nDuration,
  6533. bv,
  6534. el,
  6535. insertionPositionEl: videoUrlEl.parentElement,
  6536. explicitSubjectEl: el
  6537. });
  6538. }
  6539. return list
  6540. };
  6541. const startShieldingHistoryVideoList = async () => {
  6542. const list = await getHistoryVideoDataList();
  6543. for (let videoData of list) {
  6544. if (video_shielding.shieldingVideoDecorated(videoData)) {
  6545. continue
  6546. }
  6547. eventEmitter.send('视频添加屏蔽按钮', {data: videoData, maskingFunc: startShieldingHistoryVideoList});
  6548. }
  6549. };
  6550. const startShieldingVideoList$2 = async () => {
  6551. const list = await getVideoList();
  6552. for (let videoData of list) {
  6553. if (video_shielding.shieldingVideoDecorated(videoData)) {
  6554. continue
  6555. }
  6556. eventEmitter.send('视频添加屏蔽按钮-BewlyBewly', {data: videoData, maskingFunc: startShieldingVideoList$2});
  6557. }
  6558. };
  6559. const intervalExecutionStartShieldingVideo$2 = () => {
  6560. const res = shielding.intervalExecutionStartShieldingVideoInert(startShieldingVideoList$2, '视频');
  6561. return () => {
  6562. return res
  6563. }
  6564. };
  6565. const intervalExecutionStartShieldingHistoryVideo = () => {
  6566. const res = shielding.intervalExecutionStartShieldingVideoInert(startShieldingHistoryVideoList, '历史记录');
  6567. return () => {
  6568. return res
  6569. }
  6570. };
  6571. const startShieldingVideo$1 = intervalExecutionStartShieldingVideo$2();
  6572. const startShieldingHistoryVideo = intervalExecutionStartShieldingHistoryVideo();
  6573. const rightTabsInsertListener = () => {
  6574. getRightTabs().then(list => {
  6575. for (let {el, label, active} of list) {
  6576. el.addEventListener('click', () => {
  6577. console.log('右侧选项卡栏点击了' + label, active);
  6578. if (label === '首页') {
  6579. homeTopTabsInsertListener();
  6580. startShieldingVideo$1().start();
  6581. } else {
  6582. startShieldingVideo$1().stop();
  6583. }
  6584. if (label === '观看历史') {
  6585. startShieldingHistoryVideo().start();
  6586. } else {
  6587. startShieldingHistoryVideo().stop();
  6588. }
  6589. }
  6590. );
  6591. }
  6592. }
  6593. );
  6594. };
  6595. const getHomeTopTabs = async () => {
  6596. const beEl = await getBewlyEl();
  6597. const els = beEl.querySelectorAll('.home-tabs-inside>[data-overlayscrollbars-contents]>button');
  6598. const list = [];
  6599. for (let el of els) {
  6600. const label = el.textContent.trim();
  6601. const active = el.classList.contains('tab-activated');
  6602. list.push({label, active, el});
  6603. }
  6604. if (list.some(tab => tab.active === true)) {
  6605. return list
  6606. }
  6607. return await getHomeTopTabs()
  6608. };
  6609. const excludeTabNames = ['正在关注', '订阅剧集', '直播'];
  6610. const excludeRankingLeftTabNames = ['番剧', '综艺', '电视剧', '纪录片', '中国动画'];
  6611. const homeTopTabsInsertListener = () => {
  6612. getHomeTopTabs().then(list => {
  6613. for (let {el, label} of list) {
  6614. el.addEventListener('click', () => {
  6615. console.log('点击了' + label);
  6616. if (excludeTabNames.includes(label)) {
  6617. startShieldingVideo$1().stop();
  6618. return
  6619. }
  6620. if (label === '排行') {
  6621. rankingLeftTabsInsertListener();
  6622. }
  6623. startShieldingVideo$1().start();
  6624. });
  6625. }
  6626. });
  6627. };
  6628. const getRankingLeftTabs = async () => {
  6629. const beEl = await getBewlyEl();
  6630. const elList = await elUtil.findElementsUntilFound('ul[flex="~ col gap-2"]>li', {doc: beEl});
  6631. const list = [];
  6632. for (let el of elList) {
  6633. const label = el.textContent.trim();
  6634. list.push({label, el});
  6635. }
  6636. return list
  6637. };
  6638. const rankingLeftTabsInsertListener = () => {
  6639. getRankingLeftTabs().then(list => {
  6640. for (let {el, label} of list) {
  6641. el.addEventListener('click', () => {
  6642. console.log('点击了' + label);
  6643. if (excludeRankingLeftTabNames.includes(label)) {
  6644. startShieldingVideo$1().stop();
  6645. return
  6646. }
  6647. startShieldingVideo$1().start();
  6648. });
  6649. }
  6650. });
  6651. };
  6652. const installBEWLStyle = () => {
  6653. getBewlyEl().then(el => {
  6654. gz_ui_css.addStyle(el, el);
  6655. });
  6656. };
  6657. const searchBoxInsertListener = async () => {
  6658. const beEl = await getBewlyEl();
  6659. const input = await elUtil.findElementUntilFound('[placeholder="搜索观看历史"]', {doc: beEl});
  6660. input.addEventListener('keydown', (event) => {
  6661. if (event.key === 'Enter' || event.keyCode === 13) {
  6662. console.log('回车键被按下');
  6663. if (input['value'].length === 0) return
  6664. setTimeout(startShieldingHistoryVideoList, 1500);
  6665. }
  6666. });
  6667. };
  6668. const startRun$1 = async (url) => {
  6669. const parseUrl = defUtil.parseUrl(url);
  6670. const {page} = parseUrl.queryParams;
  6671. installBEWLStyle();
  6672. if (page === 'Home' ||
  6673. url.startsWith('https://www.bilibili.com/?spm_id_from=') ||
  6674. url === 'https://www.bilibili.com/'
  6675. ) {
  6676. startShieldingVideo$1().start();
  6677. homeTopTabsInsertListener();
  6678. }
  6679. if (page === 'History') {
  6680. startShieldingHistoryVideo().start();
  6681. searchBoxInsertListener();
  6682. }
  6683. rightTabsInsertListener();
  6684. };
  6685. var compatibleBewlyBewly = {
  6686. startRun: startRun$1,
  6687. isBEWLYPage,
  6688. check_BEWLYPage_compatibility
  6689. };
  6690. const isNewHistoryPage = (url) => {
  6691. return url.includes('://www.bilibili.com/history')
  6692. };
  6693. const getDuration = (str) => {
  6694. if (str === null) {
  6695. return -1
  6696. }
  6697. if (str.includes('已看完') || str === '') {
  6698. return -1
  6699. } else {
  6700. const match = str?.match(/\/(.*)/);
  6701. if (match) {
  6702. return sFormatUtil.timeStringToSeconds(match[1]);
  6703. }
  6704. }
  6705. return -1
  6706. };
  6707. const getVideoDataList$2 = async () => {
  6708. const elList = await elUtil.findElementsUntilFound('.section-cards.grid-mode>div');
  6709. const list = [];
  6710. for (let el of elList) {
  6711. const titleEl = el.querySelector('.bili-video-card__title');
  6712. const title = titleEl.textContent.trim();
  6713. const videoUrl = titleEl.firstElementChild.href||null;
  6714. if (videoUrl?.includes('live.bilibili.com')) {
  6715. continue
  6716. }
  6717. const bv=elUtil.getUrlBV(videoUrl);
  6718. const userEl = el.querySelector('.bili-video-card__author');
  6719. const cardTag = el.querySelector('.bili-cover-card__tag')?.textContent.trim() || null;
  6720. const name = userEl.textContent.trim();
  6721. const userUrl = userEl.href;
  6722. const uid = elUtil.getUrlUID(userUrl);
  6723. let nDuration = -1;
  6724. if (cardTag !== '专栏') {
  6725. nDuration = el.querySelector('.bili-cover-card__stat')?.textContent.trim() || null;
  6726. nDuration = getDuration(nDuration);
  6727. }
  6728. const tempEL = el.querySelector('.bili-video-card__details');
  6729. list.push({
  6730. title,
  6731. videoUrl,
  6732. name,
  6733. userUrl,
  6734. nDuration,
  6735. uid,
  6736. el,
  6737. bv,
  6738. insertionPositionEl: tempEL,
  6739. explicitSubjectEl: tempEL
  6740. });
  6741. }
  6742. return list
  6743. };
  6744. const startShieldingVideoList$1 = async () => {
  6745. const list = await getVideoDataList$2();
  6746. for (let videoData of list) {
  6747. if (video_shielding.shieldingVideoDecorated(videoData)) {
  6748. continue;
  6749. }
  6750. shielding.addBlockButton({data: videoData, maskingFunc: startShieldingVideoList$1}, "gz_shielding_button");
  6751. }
  6752. };
  6753. const intervalExecutionStartShieldingVideo$1 = () => {
  6754. const res = shielding.intervalExecutionStartShieldingVideoInert(startShieldingVideoList$1, '历史记录项');
  6755. return () => {
  6756. return res
  6757. }
  6758. };
  6759. const executionStartShieldingVideo = intervalExecutionStartShieldingVideo$1();
  6760. const getTopFilterLabel = async () => {
  6761. const el = await elUtil.findElementUntilFound('.radio-filter>.radio-filter__item--active');
  6762. return el.textContent?.trim()
  6763. };
  6764. const topFilterInsertListener = () => {
  6765. elUtil.findElementUntilFound('.radio-filter').then((el => {
  6766. el.addEventListener('click', (e) => {
  6767. const target = e.target;
  6768. const label = target.textContent?.trim();
  6769. console.log(`点击了${label}`);
  6770. if (label === '直播') {
  6771. executionStartShieldingVideo().stop();
  6772. return
  6773. }
  6774. executionStartShieldingVideo().start();
  6775. });
  6776. }));
  6777. };
  6778. const startRun = () => {
  6779. getTopFilterLabel().then(label => {
  6780. if (label === '直播') {
  6781. return
  6782. }
  6783. executionStartShieldingVideo().start();
  6784. });
  6785. topFilterInsertListener();
  6786. };
  6787. var newHistory = {
  6788. isNewHistoryPage,
  6789. intervalExecutionStartShieldingVideo: intervalExecutionStartShieldingVideo$1,
  6790. startRun
  6791. };
  6792. var css = `.to_hide_xl {
  6793. display: block !important;
  6794. }
  6795. `;
  6796. const isSearchLivePage = (url = window.location.href) => {
  6797. return url.includes('search.bilibili.com/live')
  6798. };
  6799. const installStyle = () => {
  6800. const styleElement = document.createElement('style');
  6801. styleElement.textContent = css;
  6802. document.head.appendChild(styleElement);
  6803. };
  6804. const getLiveRoomList = async () => {
  6805. const elList = await elUtil.findElements('.live-room-cards>.video-list-item');
  6806. const list = [];
  6807. for (let el of elList) {
  6808. const titleAEl = el.querySelector('.bili-live-card__info--tit>a');
  6809. const titleEl = el.querySelector('.bili-live-card__info--tit>a>span');
  6810. const userEl = el.querySelector('.bili-live-card__info--uname');
  6811. const liveUrl = titleAEl.href;
  6812. const title = titleEl.textContent.trim();
  6813. const userUrl = userEl.href;
  6814. const uid = elUtil.getUrlUID(userUrl);
  6815. const name = userEl.textContent.trim();
  6816. list.push({
  6817. title,
  6818. liveUrl,
  6819. name,
  6820. userUrl,
  6821. uid,
  6822. el,
  6823. explicitSubjectEl: el.querySelector('.bili-live-card__info'),
  6824. insertionPositionEl: userEl
  6825. });
  6826. }
  6827. return list
  6828. };
  6829. const addBlockButton = (data) => {
  6830. shielding.addBlockButton(data, '', ['right']);
  6831. };
  6832. const startShieldingLiveRoomList = async () => {
  6833. const list = await getLiveRoomList();
  6834. for (let liveData of list) {
  6835. if (live_shielding.shieldingLiveRoomDecorated(liveData)) {
  6836. continue
  6837. }
  6838. addBlockButton({data: liveData, maskingFunc: startShieldingLiveRoomList});
  6839. }
  6840. };
  6841. var searchLive = {
  6842. installStyle,
  6843. startShieldingLiveRoomList,
  6844. isSearchLivePage
  6845. };
  6846. const startShieldingHotList = async () => {
  6847. const elList = await elUtil.findElements(".trendings-col>.trending-item",
  6848. {interval: 2000});
  6849. console.log("检查热搜关键词中...");
  6850. const hotSearchKeyArr = ruleKeyListData$1.getHotSearchKeyArr();
  6851. const hotSearchKeyCanonicalArr = ruleKeyListData$1.getHotSearchKeyCanonicalArr();
  6852. for (let el of elList) {
  6853. const label = el.textContent.trim();
  6854. let match = ruleMatchingUtil.fuzzyMatch(hotSearchKeyArr, label);
  6855. if (match) {
  6856. el.remove();
  6857. eventEmitter.send('打印信息', `根据模糊热搜关键词-【${match}】-屏蔽-${label}`);
  6858. continue;
  6859. }
  6860. match = ruleMatchingUtil.regexMatch(hotSearchKeyCanonicalArr, label);
  6861. if (match) {
  6862. eventEmitter.send('打印信息', `根据正则热搜关键词-【${match}】-屏蔽-${label}`);
  6863. el.remove();
  6864. }
  6865. }
  6866. };
  6867. var hotSearch = {
  6868. startShieldingHotList
  6869. };
  6870. const isMessagePage = (url = window.location.href) => {
  6871. return url.includes("message.bilibili.com");
  6872. };
  6873. const modifyTopItemsZIndex = () => {
  6874. elUtil.findElement('#home_nav').then(el => {
  6875. el.style.zIndex = 1000;
  6876. eventEmitter.send('打印信息', '已修改顶部的z-index值为1');
  6877. });
  6878. };
  6879. var messagePage = {
  6880. isMessagePage,
  6881. modifyTopItemsZIndex,
  6882. };
  6883. const isSpacePage = (url = window.location.href) => {
  6884. return url.startsWith('https://space.bilibili.com/')
  6885. };
  6886. const isPersonalHomepage = async () => {
  6887. const keyStr = 'isPersonalHomepage';
  6888. const cache = valueCache.get(keyStr);
  6889. if (cache) {
  6890. return cache
  6891. }
  6892. const {
  6893. state: newState,
  6894. data: newData
  6895. } = await elUtil.findElements('.nav-tab__item .nav-tab__item-text', {timeout: 2500});
  6896. if (newState) {
  6897. const bool = newData.some(el => el.textContent.trim() === '设置');
  6898. valueCache.set('space_version', 'new');
  6899. return valueCache.set(keyStr, bool);
  6900. }
  6901. let {state} = await elUtil.findElement('.n-tab-links>.n-btn.n-setting>.n-text', {timeout: 1500});
  6902. valueCache.set('space_version', 'old');
  6903. return valueCache.set(keyStr, state);
  6904. };
  6905. const getUserInfo = async () => {
  6906. const spaceUserInfo = valueCache.get('space_userInfo');
  6907. if (spaceUserInfo) {
  6908. return spaceUserInfo
  6909. }
  6910. await isPersonalHomepage();
  6911. const nameData = {};
  6912. nameData.uid = elUtil.getUrlUID(window.location.href);
  6913. if (valueCache.get('space_version', 'new') === 'new') {
  6914. nameData.name = await elUtil.findElement('.nickname').then(el => el.textContent.trim());
  6915. } else {
  6916. nameData.name = await elUtil.findElement('#h-name').then(el => el.textContent.trim());
  6917. }
  6918. if (!nameData.name) {
  6919. const title = document.title;
  6920. nameData.name = title.match(/(.+)的个人空间/)[1];
  6921. }
  6922. valueCache.set('space_userInfo', nameData);
  6923. return nameData
  6924. };
  6925. var space = {
  6926. isPersonalHomepage,
  6927. isSpacePage,
  6928. getUserInfo
  6929. };
  6930. const getGateActivatedTab = async () => {
  6931. const el = await elUtil.findElementUntilFound(".ant-radio-group>.ant-radio-button-wrapper-checked .css-1k4kcw8");
  6932. return el?.textContent.trim();
  6933. };
  6934. const check_bilibili_gate_compatibility = async () => {
  6935. const {state} = await elUtil.findElement('.bilibili-gate-root', {interval: 300, timeout: 5000});
  6936. if (state) {
  6937. if (!globalValue.adaptationBAppCommerce) {
  6938. eventEmitter.send('el-alert', "检测到使用bilibili_gate脚本但未开启兼容选项,需要启用相关兼容选项才可正常使用");
  6939. } else {
  6940. eventEmitter.send('el-notify', {
  6941. title: "tip",
  6942. message: '启用兼容bilibili-gate脚本',
  6943. position: 'bottom-right',
  6944. });
  6945. }
  6946. return
  6947. }
  6948. if (globalValue.adaptationBAppCommerce) {
  6949. eventEmitter.send('el-alert', "检测到未使用bilibili_gate脚本却开启了兼容选项,请关闭兼容选项或启用bilibili_gate脚本后再启用相关兼容选项");
  6950. }
  6951. };
  6952. const getGateDataList = async () => {
  6953. const elList = await elUtil.findElementsUntilFound(".bilibili-gate-video-grid>[data-bvid].bili-video-card");
  6954. const list = [];
  6955. for (let el of elList) {
  6956. const tempData = bilibiliHome.getVideoData(el);
  6957. const videoUrl = el.querySelector("a.css-feo88y")?.href;
  6958. const bv = elUtil.getUrlBV(videoUrl);
  6959. const insertionPositionEl = el.querySelector(".bili-video-card__info--owner");
  6960. list.push({
  6961. ...tempData, ...{
  6962. videoUrl,
  6963. el,
  6964. bv,
  6965. insertionPositionEl,
  6966. explicitSubjectEl: el
  6967. }
  6968. });
  6969. }
  6970. return list;
  6971. };
  6972. const startShieldingGateVideoList = async () => {
  6973. const list = await getGateDataList();
  6974. for (let videoData of list) {
  6975. if (video_shielding.shieldingVideoDecorated(videoData, "hide")) {
  6976. continue;
  6977. }
  6978. eventEmitter.send('视频添加屏蔽按钮', {data: videoData, maskingFunc: startShieldingGateVideoList});
  6979. }
  6980. };
  6981. const startIntervalShieldingGateVideoList = () => {
  6982. const throttle = defUtil.throttle(startShieldingGateVideoList, 2000);
  6983. setInterval(async () => {
  6984. await getGateActivatedTab();
  6985. throttle();
  6986. }, 1500);
  6987. };
  6988. var BLBLGate = {
  6989. check_bilibili_gate_compatibility,
  6990. startIntervalShieldingGateVideoList
  6991. };
  6992. const homeStaticRoute = (title, url) => {
  6993. if (compatibleBewlyBewly.isBEWLYPage(url) && globalValue.compatibleBEWLYBEWLY) {
  6994. compatibleBewlyBewly.startRun(url);
  6995. }
  6996. if (bilibiliHome.isHome(url, title)) {
  6997. BLBLGate.check_bilibili_gate_compatibility();
  6998. compatibleBewlyBewly.check_BEWLYPage_compatibility();
  6999. eventEmitter.send('通知屏蔽');
  7000. if (globalValue.compatibleBEWLYBEWLY) return;
  7001. bilibiliHome.scrollMouseUpAndDown().then(() => bilibiliHome.startDebounceShieldingChangeVideoList());
  7002. bilibiliHome.startClearExcessContentList();
  7003. bilibiliHome.deDesktopDownloadTipEl();
  7004. }
  7005. };
  7006. const staticRoute = (title, url) => {
  7007. console.log("静态路由", title, url);
  7008. homeStaticRoute(title, url);
  7009. if (globalValue.bOnlyTheHomepageIsBlocked) return;
  7010. topInput.processTopInputContent();
  7011. hotSearch.startShieldingHotList();
  7012. eventEmitter.send('通知屏蔽');
  7013. if (searchModel.isSearch(url)) {
  7014. searchLive.installStyle();
  7015. searchModel.delFooterContent();
  7016. }
  7017. if (videoPlayModel.isVideoPlayPage(url)) {
  7018. elUtil.updateCssVModal();
  7019. videoPlayModel.findTheExpandButtonForTheListOnTheRightAndBindTheEvent();
  7020. videoPlayModel.setVideoPlayerEnded();
  7021. videoPlayModel.delElManagement();
  7022. }
  7023. if (collectionVideoPlayPageModel.iscCollectionVideoPlayPage(url)) {
  7024. collectionVideoPlayPageModel.findTheExpandButtonForTheListOnTheRightAndBindTheEvent();
  7025. }
  7026. if (liveRoomModel.isLiveRoom(url)) {
  7027. liveRoomModel.addWatchLiveRoomChatItemsListener();
  7028. }
  7029. if (videoPlayWatchLater.isVideoPlayWatchLaterPage(url)) {
  7030. elUtil.updateCssVModal();
  7031. videoPlayWatchLater.findTheExpandButtonForTheListOnTheRightAndBindTheEvent();
  7032. }
  7033. if (newHistory.isNewHistoryPage(url)) {
  7034. newHistory.startRun();
  7035. }
  7036. if (messagePage.isMessagePage(url)) {
  7037. messagePage.modifyTopItemsZIndex();
  7038. }
  7039. if (space.isSpacePage()) {
  7040. space.getUserInfo().then(userInfo => {
  7041. console.info('userInfo', userInfo);
  7042. });
  7043. }
  7044. };
  7045. const dynamicRouting = (title, url) => {
  7046. console.log("动态路由", title, url);
  7047. if (globalValue.bOnlyTheHomepageIsBlocked) return;
  7048. eventEmitter.send('通知屏蔽');
  7049. };
  7050. var router = {
  7051. staticRoute,
  7052. dynamicRouting
  7053. };
  7054. const isTopicDetailPage = (url) => {
  7055. return url.includes("//www.bilibili.com/v/topic/detail/")
  7056. };
  7057. const getDataList$1 = async () => {
  7058. const elList = await elUtil.findElementsUntilFound(".list__topic-card");
  7059. const list = [];
  7060. for (let el of elList) {
  7061. const name = el.querySelector(".bili-dyn-title").textContent.trim();
  7062. const uidEl = el.querySelector(".bili-dyn-item__following");
  7063. const uid = parseInt(uidEl.getAttribute("data-mid"));
  7064. const judgmentEl = el.querySelector(".bili-dyn-card-video__title");
  7065. const data = {name, uid, el, judgmentVideo: judgmentEl !== null};
  7066. if (judgmentEl !== null) {
  7067. data.title = judgmentEl.textContent.trim();
  7068. const videoUrl = el.querySelector(".bili-dyn-card-video").href;
  7069. data.videoUrl = videoUrl;
  7070. data.bv = elUtil.getUrlBV(videoUrl);
  7071. data.insertionPositionEl = el.querySelector(".bili-dyn-content__orig");
  7072. data.explicitSubjectEl = data.insertionPositionEl;
  7073. } else {
  7074. const dynTitle = el.querySelector(".dyn-card-opus__title");
  7075. const contentTitle = dynTitle === null ? "" : dynTitle.textContent.trim();
  7076. const contentBody = el.querySelector(".bili-rich-text>div").textContent.trim();
  7077. data.insertionPositionEl = el.querySelector(".dyn-card-opus");
  7078. data.explicitSubjectEl = data.insertionPositionEl;
  7079. data.content = contentTitle + contentBody;
  7080. }
  7081. list.push(data);
  7082. }
  7083. return list;
  7084. };
  7085. const __shieldingVideo = (videoData) => {
  7086. if (video_shielding.shieldingVideoDecorated(videoData)) {
  7087. return;
  7088. }
  7089. shielding.addTopicDetailVideoBlockButton({data: videoData, maskingFunc: startShielding});
  7090. };
  7091. const __shieldingDynamic = (dynamicData) => {
  7092. if (comments_shielding.shieldingCommentDecorated(dynamicData)) {
  7093. return;
  7094. }
  7095. shielding.addTopicDetailContentsBlockButton({data: dynamicData, maskingFunc: startShielding});
  7096. };
  7097. const startShielding = async () => {
  7098. const list = await getDataList$1();
  7099. const css = {width: "100%"};
  7100. for (let data of list) {
  7101. data.css = css;
  7102. if (data.judgmentVideo) {
  7103. __shieldingVideo(data);
  7104. } else {
  7105. __shieldingDynamic(data);
  7106. }
  7107. }
  7108. };
  7109. var topicDetail = {
  7110. isTopicDetailPage,
  7111. startShielding
  7112. };
  7113. eventEmitter.on('评论添加屏蔽按钮', (commentsData) => {
  7114. shielding.addBlockButton({
  7115. data: commentsData,
  7116. maskingFunc: startShieldingComments
  7117. }, "gz_shielding_comment_button");
  7118. });
  7119. const getUrlUserLevel = (src) => {
  7120. const levelMath = src?.match(/level_(.+)\.svg/) || null;
  7121. let level = -1;
  7122. if (levelMath !== null) {
  7123. const levelRow = levelMath[1];
  7124. if (levelRow === 'h') {
  7125. level = 7;
  7126. } else {
  7127. level = parseInt(levelRow);
  7128. }
  7129. }
  7130. return level;
  7131. };
  7132. const getOldUserLevel = (iEl) => {
  7133. let level;
  7134. const levelCLassName = iEl.classList[1];
  7135. if (levelCLassName === 'level-hardcore') {
  7136. level = 7;
  7137. } else {
  7138. const levelMatch = levelCLassName.match(/level-(.+)/)?.[1] || '';
  7139. level = parseInt(levelMatch);
  7140. }
  7141. return level
  7142. };
  7143. const getCommentSectionList = async () => {
  7144. const commentApp = await elUtil.findElementUntilFound("bili-comments",
  7145. {interval: 500});
  7146. const comments = await elUtil.findElementsUntilFound("#feed>bili-comment-thread-renderer",
  7147. {doc: commentApp.shadowRoot, interval: 500});
  7148. const commentsData = [];
  7149. let isLoaded = false;
  7150. for (let el of comments) {
  7151. const theOPEl = el.shadowRoot.getElementById("comment").shadowRoot;
  7152. const theOPUserInfo = theOPEl.querySelector("bili-comment-user-info")
  7153. .shadowRoot.getElementById("info");
  7154. const userNameEl = theOPUserInfo.querySelector("#user-name>a");
  7155. const userLevelSrc = theOPUserInfo.querySelector('#user-level>img')?.src || null;
  7156. const level = getUrlUserLevel(userLevelSrc);
  7157. isLoaded = theOPEl.querySelector("#content>bili-rich-text")
  7158. .shadowRoot.querySelector("#contents>*") !== null;
  7159. if (!isLoaded) {
  7160. break;
  7161. }
  7162. const theOPContentEl = theOPEl.querySelector("#content>bili-rich-text")
  7163. .shadowRoot.querySelector("#contents");
  7164. const theOPContent = theOPContentEl.textContent.trim();
  7165. const userName = userNameEl.textContent.trim();
  7166. const userUrl = userNameEl.href;
  7167. const uid = elUtil.getUrlUID(userUrl);
  7168. const replies = [];
  7169. commentsData.push({
  7170. name: userName,
  7171. userUrl,
  7172. uid,
  7173. level,
  7174. content: theOPContent,
  7175. replies,
  7176. el,
  7177. insertionPositionEl: theOPUserInfo,
  7178. explicitSubjectEl: theOPEl.querySelector("#body"),
  7179. contentsEl: theOPContentEl
  7180. });
  7181. const inTheBuildingEls = el.shadowRoot.querySelector("bili-comment-replies-renderer")
  7182. .shadowRoot.querySelectorAll("bili-comment-reply-renderer");
  7183. for (let inTheBuildingEl of inTheBuildingEls) {
  7184. const inTheContentEl = inTheBuildingEl.shadowRoot;
  7185. const biliCommentUserInfo = inTheContentEl.querySelector("bili-comment-user-info");
  7186. biliCommentUserInfo.style.display = 'block';
  7187. const inTheBuildingUserInfo = biliCommentUserInfo.shadowRoot.getElementById("info");
  7188. const inTheBuildingUserNameEl = inTheBuildingUserInfo.querySelector("#user-name>a");
  7189. const inTheBuildingUserName = inTheBuildingUserNameEl.textContent.trim();
  7190. const inTheBuildingUserUrl = inTheBuildingUserNameEl.href;
  7191. const inTheBuildingUid = elUtil.getUrlUID(inTheBuildingUserUrl);
  7192. const biliRichTextEL = inTheContentEl.querySelector("bili-rich-text");
  7193. const contentsEl = biliRichTextEL.shadowRoot.querySelector("#contents");
  7194. const inTheBuildingContent = contentsEl.textContent.trim();
  7195. const userLevelSrc = inTheBuildingUserInfo.querySelector('#user-level>img')?.src || null;
  7196. const level = getUrlUserLevel(userLevelSrc);
  7197. replies.push({
  7198. name: inTheBuildingUserName,
  7199. userUrl: inTheBuildingUserUrl,
  7200. uid: inTheBuildingUid,
  7201. level,
  7202. content: inTheBuildingContent,
  7203. el: inTheBuildingEl,
  7204. insertionPositionEl: inTheBuildingUserInfo,
  7205. explicitSubjectEl: inTheBuildingEl,
  7206. contentsEl
  7207. });
  7208. }
  7209. }
  7210. if (!isLoaded) {
  7211. await defUtil.wait(500);
  7212. return getCommentSectionList()
  7213. }
  7214. return commentsData;
  7215. };
  7216. const getOldCommentSectionList = async () => {
  7217. let results;
  7218. try {
  7219. results = await elUtil.findElementsUntilFound(".reply-list>.reply-item", {timeout: 5000});
  7220. } catch (e) {
  7221. return []
  7222. }
  7223. const commentsData = [];
  7224. for (let el of results) {
  7225. const theOPEl = el.querySelector(".root-reply-container");
  7226. const theOPUserInfoEl = theOPEl.querySelector(".user-name");
  7227. const userName = theOPUserInfoEl.textContent.trim();
  7228. const uid = parseInt(theOPUserInfoEl.getAttribute("data-user-id"));
  7229. const userUrl = `https://space.bilibili.com/${uid}`;
  7230. const theOPContent = theOPEl.querySelector(".reply-content").textContent.trim();
  7231. const userInfoEl = el.querySelector(".user-info");
  7232. const iEl = userInfoEl.querySelector('i');
  7233. const level = getOldUserLevel(iEl);
  7234. const replies = [];
  7235. commentsData.push({
  7236. name: userName,
  7237. userUrl,
  7238. uid,
  7239. content: theOPContent,
  7240. level,
  7241. replies,
  7242. el,
  7243. insertionPositionEl: userInfoEl,
  7244. explicitSubjectEl: el.querySelector(".content-warp")
  7245. });
  7246. const inTheBuildingEls = el.querySelectorAll(".sub-reply-container>.sub-reply-list>.sub-reply-item");
  7247. for (let inTheBuildingEl of inTheBuildingEls) {
  7248. const subUserNameEl = inTheBuildingEl.querySelector(".sub-user-name");
  7249. const uid = parseInt(subUserNameEl.getAttribute("data-user-id"));
  7250. const userName = subUserNameEl.textContent.trim();
  7251. const userUrl = `https://space.bilibili.com/${uid}`;
  7252. const subContent = inTheBuildingEl.querySelector(".reply-content").textContent.trim();
  7253. const subUserInfoEl = inTheBuildingEl.querySelector(".sub-user-info");
  7254. const iEl = subUserInfoEl.querySelector('i');
  7255. const level = getOldUserLevel(iEl);
  7256. const replyContentContainerEl = inTheBuildingEl.querySelector('span.reply-content-container');
  7257. replyContentContainerEl.style.display = 'block';
  7258. replies.push({
  7259. name: userName,
  7260. userUrl,
  7261. uid,
  7262. level,
  7263. content: subContent,
  7264. el: inTheBuildingEl,
  7265. insertionPositionEl: subUserInfoEl,
  7266. explicitSubjectEl: inTheBuildingEl
  7267. });
  7268. }
  7269. }
  7270. return commentsData;
  7271. };
  7272. const startShieldingComments = async () => {
  7273. if (videoPlayModel.isVideoPlayPage() && localMKData.isDelBottomComment()) {
  7274. return
  7275. }
  7276. let list;
  7277. const href = window.location.href;
  7278. if (localMKData.isDiscardOldCommentAreas()) {
  7279. list = await getCommentSectionList();
  7280. } else if (href.includes("https://space.bilibili.com/") || topicDetail.isTopicDetailPage(href)) {
  7281. list = await getOldCommentSectionList();
  7282. } else {
  7283. list = await getCommentSectionList();
  7284. }
  7285. comments_shielding.shieldingComments(list);
  7286. };
  7287. var commentSectionModel = {
  7288. startShieldingComments
  7289. };
  7290. const getVideDataList = async (isWeekly = false) => {
  7291. const css = isWeekly ? ".video-list>.video-card" : ".card-list>.video-card";
  7292. const elList = await elUtil.findElementsUntilFound(css);
  7293. const list = [];
  7294. for (let el of elList) {
  7295. const videoCardInfoEl = el.querySelector(".video-card__info");
  7296. const title = videoCardInfoEl.querySelector(".video-name").title.trim();
  7297. const name = videoCardInfoEl.querySelector(".up-name__text").title;
  7298. const videoUrl = el.querySelector('.video-card__content>a')?.href || null;
  7299. const bv = elUtil.getUrlBV(videoUrl);
  7300. let nPlayCount = el.querySelector('.play-text').textContent.trim();
  7301. nPlayCount = sFormatUtil.toPlayCountOrBulletChat(nPlayCount);
  7302. let nBulletChat = el.querySelector('.like-text').textContent.trim();
  7303. nBulletChat = sFormatUtil.toPlayCountOrBulletChat(nBulletChat);
  7304. list.push({
  7305. el,
  7306. title,
  7307. name,
  7308. videoUrl,
  7309. bv,
  7310. nPlayCount,
  7311. nBulletChat,
  7312. insertionPositionEl: videoCardInfoEl.querySelector("div"),
  7313. explicitSubjectEl: videoCardInfoEl
  7314. });
  7315. }
  7316. return list;
  7317. };
  7318. const startShieldingVideoList = async (isWeekly = false) => {
  7319. const list = await getVideDataList(isWeekly);
  7320. for (let videoData of list) {
  7321. if (video_shielding.shieldingVideoDecorated(videoData)) {
  7322. continue;
  7323. }
  7324. eventEmitter.send('添加热门视频屏蔽按钮', {data: videoData, maskingFunc: startShieldingVideoList});
  7325. }
  7326. };
  7327. var popularAll = {
  7328. startShieldingVideoList
  7329. };
  7330. const isDynamicPage = (url) => {
  7331. return url.search("space.bilibili.com/\\d+/dynamic") !== -1;
  7332. };
  7333. const getDataList = async () => {
  7334. const elList = await elUtil.findElementsUntilFound(".bili-dyn-list__items>.bili-dyn-list__item");
  7335. const list = [];
  7336. for (let el of elList) {
  7337. const videoCardEl = el.querySelector(".bili-dyn-card-video__title");
  7338. const name = el.querySelector(".bili-dyn-title").textContent.trim();
  7339. const tagEl = el.querySelector(".bili-dyn-topic__text");
  7340. const data = {el, name};
  7341. if (tagEl !== null) {
  7342. data.tag = tagEl.textContent.trim();
  7343. }
  7344. data.judgmentVideo = videoCardEl !== null;
  7345. if (data.judgmentVideo) {
  7346. data.title = videoCardEl.textContent.trim();
  7347. } else {
  7348. const contentTitleEL = el.querySelector(".dyn-card-opus>.dyn-card-opus__title");
  7349. const contentTitle = contentTitleEL === null ? "" : contentTitleEL.textContent.trim();
  7350. const contentElBody = el.querySelector(".bili-rich-text").textContent.trim();
  7351. data.content = contentTitle + contentElBody;
  7352. }
  7353. list.push(data);
  7354. }
  7355. return list;
  7356. };
  7357. const startShieldingDynamicContent = async () => {
  7358. const personalHomepage = await space.isPersonalHomepage();
  7359. if (personalHomepage) return;
  7360. const list = await getDataList();
  7361. for (let dynamicContent of list) {
  7362. shielding.shieldingDynamicDecorated(dynamicContent);
  7363. }
  7364. };
  7365. const startThrottleShieldingDynamicContent = defUtil.throttle(startShieldingDynamicContent, 2000);
  7366. var dynamic = {
  7367. isDynamicPage,
  7368. startThrottleShieldingDynamicContent
  7369. };
  7370. const isLiveSection = (url) => {
  7371. return url.includes("live.bilibili.com/p/eden/area-tags")
  7372. };
  7373. const getRoomCardDataList = async () => {
  7374. const elList = await elUtil.findElementsUntilFound("#room-card-list>div");
  7375. const list = [];
  7376. for (let el of elList) {
  7377. const liveUrl = el.querySelector("#card").href;
  7378. const name = el.querySelector(".Item_nickName_KO2QE").textContent.trim();
  7379. const title = el.querySelector(".Item_roomTitle_ax3eD").textContent.trim();
  7380. const partition = el.querySelector(".Item_area-name_PXDG4")?.textContent.trim() || null;
  7381. const popularity = el.querySelector(".Item_onlineCount_FmOW6").textContent.trim();
  7382. list.push({liveUrl, name, title, partition, popularity, el});
  7383. }
  7384. return list;
  7385. };
  7386. const startShieldingLiveRoom$1 = async () => {
  7387. const liveList = await getRoomCardDataList();
  7388. for (let liveData of liveList) {
  7389. live_shielding.shieldingLiveRoomDecorated(liveData);
  7390. }
  7391. };
  7392. var liveSectionModel = {
  7393. isLiveSection,
  7394. startShieldingLiveRoom: startShieldingLiveRoom$1
  7395. };
  7396. const isLiveHomePage = (url) => {
  7397. return url.includes("https://live.bilibili.com/?spm_id_from=333.1007.0.0") ||
  7398. url === "https://live.bilibili.com/"
  7399. };
  7400. const getTopLiveRoomDataList = async () => {
  7401. const verification = await elUtil.findElementUntilFound(".v-top>.aside-item .t-left.aside-item-tips.p-absolute.w-100.border-box");
  7402. if (verification.textContent.trim() === "--") {
  7403. return await getTopLiveRoomDataList();
  7404. }
  7405. const elList = await elUtil.findElementsUntilFound(".v-top>.aside-item", {interval: 2000});
  7406. const list = [];
  7407. for (let el of elList) {
  7408. const classList = el.classList;
  7409. const active = classList.contains("active");
  7410. const title = el.getAttribute("title");
  7411. const {up_id: uid, room_id} = JSON.parse(el.getAttribute("data-report"));
  7412. const liveUrl = `https://live.bilibili.com/${room_id}`;
  7413. list.push({title, uid, active, liveUrl, el});
  7414. }
  7415. return list;
  7416. };
  7417. const getLiveRoomDataList = async () => {
  7418. const elList = await elUtil.findElementsUntilFound(".room-card-wrapper.p-relative.dp-i-block");
  7419. const list = [];
  7420. for (let el of elList) {
  7421. const cardEl = el.querySelector(".room-card-ctnr.p-relative.w-100");
  7422. const cardData = JSON.parse(cardEl.getAttribute("data-bl-report-click") || "");
  7423. const {up_id: uid, room_id} = cardData.msg;
  7424. const liveUrl = `https://live.bilibili.com/${room_id}`;
  7425. const name = el.querySelector(".room-anchor>span").textContent.trim();
  7426. const title = el.querySelector(".room-title.card-text").textContent.trim();
  7427. const partition = el.querySelector(".area-name").textContent.trim();
  7428. const popularity = el.querySelector(".room-anchor .v-middle").textContent.trim();
  7429. list.push({name, title, partition, popularity, liveUrl, uid, el});
  7430. }
  7431. return list;
  7432. };
  7433. const startShieldingLiveRoom = async () => {
  7434. const list = await getLiveRoomDataList();
  7435. for (let liveData of list) {
  7436. live_shielding.shieldingLiveRoomDecorated(liveData);
  7437. }
  7438. };
  7439. const startShieldingTopLiveRoom = async () => {
  7440. const list = await getTopLiveRoomDataList();
  7441. for (let liveData of list) {
  7442. live_shielding.shieldingLiveRoomDecorated(liveData);
  7443. }
  7444. };
  7445. var liveHome = {
  7446. isLiveHomePage,
  7447. startShieldingLiveRoom,
  7448. startShieldingTopLiveRoom
  7449. };
  7450. const isPartition = (url = window.location.href) => {
  7451. return url.includes('www.bilibili.com/v/');
  7452. };
  7453. const isNewPartition = (url = window.location.href) => {
  7454. return url.includes('www.bilibili.com/c/')
  7455. };
  7456. const getHotVideoDayList = async () => {
  7457. const elList = await elUtil.findElementsUntilFound('.bili-rank-list-video__item');
  7458. const list = [];
  7459. for (let el of elList) {
  7460. let videoUrlEl = el.querySelector('a.rank-video-card');
  7461. const titleEl = el.querySelector('.rank-video-card__info--tit');
  7462. const videoUrl = videoUrlEl.href;
  7463. const title = titleEl.textContent.trim();
  7464. const bv = elUtil.getUrlBV(videoUrl);
  7465. list.push({
  7466. title, videoUrl, bv, el
  7467. });
  7468. }
  7469. return list
  7470. };
  7471. const getVVideoDataList = async () => {
  7472. const elList = await elUtil.findElementsUntilFound('.bili-video-card');
  7473. const list = [];
  7474. const oneTitleEl = elList[0].querySelector('.bili-video-card__info--tit>a');
  7475. if (oneTitleEl === null) {
  7476. await defUtil.wait();
  7477. return await getVVideoDataList()
  7478. }
  7479. for (let el of elList) {
  7480. const titleEl = el.querySelector('.bili-video-card__info--tit>a');
  7481. if (titleEl === null) {
  7482. continue
  7483. }
  7484. const userEl = el.querySelector('a.bili-video-card__info--owner');
  7485. const playAndDmu = el.querySelectorAll('.bili-video-card__stats--item>span');
  7486. let nDuration = el.querySelector('.bili-video-card__stats__duration')?.textContent.trim();
  7487. let nPlayCount = playAndDmu[0]?.textContent.trim();
  7488. nPlayCount = sFormatUtil.toPlayCountOrBulletChat(nPlayCount);
  7489. let nBulletChat = playAndDmu[1]?.textContent.trim();
  7490. nBulletChat = sFormatUtil.toPlayCountOrBulletChat(nBulletChat);
  7491. nDuration = sFormatUtil.toPlayCountOrBulletChat(nDuration);
  7492. const title = titleEl.textContent.trim();
  7493. const videoUrl = titleEl.href;
  7494. const userUrl = userEl.href;
  7495. const name = userEl
  7496. .querySelector('.bili-video-card__info--author')
  7497. ?.textContent.trim() || null;
  7498. const uid = elUtil.getUrlUID(userUrl);
  7499. const bv = elUtil.getUrlBV(videoUrl);
  7500. list.push({
  7501. name, title, uid, bv, userUrl, videoUrl, el,
  7502. nPlayCount, nBulletChat, nDuration,
  7503. explicitSubjectEl: el.querySelector('.bili-video-card__info'),
  7504. insertionPositionEl: el.querySelector('.bili-video-card__info--bottom')
  7505. });
  7506. }
  7507. return list
  7508. };
  7509. const getCVideoDataList = async () => {
  7510. const elList = await elUtil.findElementsUntilFound('.bili-video-card');
  7511. const list = [];
  7512. for (let el of elList) {
  7513. const titleEl = el.querySelector('.bili-video-card__title');
  7514. const title = titleEl.textContent.trim();
  7515. const videoUrl = titleEl.querySelector('a').href;
  7516. const bv = elUtil.getUrlBV(videoUrl);
  7517. const userEl = el.querySelector('.bili-video-card__author');
  7518. const userUrl = userEl.href;
  7519. const uid = elUtil.getUrlUID(userUrl);
  7520. const name = userEl.querySelector('[title]').textContent.trim().split('·')[0].trim();
  7521. const statEls = el.querySelectorAll('.bili-cover-card__stats span');
  7522. const nPlayCount = sFormatUtil.toPlayCountOrBulletChat(statEls[0].textContent.trim());
  7523. const nBulletChat = sFormatUtil.toPlayCountOrBulletChat(statEls[1].textContent.trim());
  7524. const nDuration = sFormatUtil.timeStringToSeconds(statEls[2].textContent.trim());
  7525. const insertionPositionEl = el.querySelector('.bili-video-card__subtitle');
  7526. const explicitSubjectEl = el.querySelector('.bili-video-card__details');
  7527. list.push({
  7528. title,
  7529. userUrl,
  7530. uid,
  7531. name,
  7532. videoUrl,
  7533. bv,
  7534. nPlayCount,
  7535. nBulletChat,
  7536. nDuration,
  7537. el,
  7538. insertionPositionEl,
  7539. explicitSubjectEl
  7540. });
  7541. }
  7542. return list
  7543. };
  7544. const shieldingVideoList = async () => {
  7545. let list;
  7546. if (isPartition()) {
  7547. list = await getVVideoDataList();
  7548. } else {
  7549. list = await getCVideoDataList();
  7550. }
  7551. for (let videoData of list) {
  7552. if (video_shielding.shieldingVideoDecorated(videoData)) {
  7553. continue
  7554. }
  7555. eventEmitter.send('视频添加屏蔽按钮', {data: videoData, maskingFunc: shieldingVideoList});
  7556. }
  7557. };
  7558. const startShieldingHotVideoDayList = async () => {
  7559. let list = await getHotVideoDayList();
  7560. for (let videoData of list) {
  7561. video_shielding.shieldingVideoDecorated(videoData);
  7562. }
  7563. };
  7564. const startIntervalShieldingVideoList = () => {
  7565. setInterval(async () => {
  7566. await shieldingVideoList();
  7567. for (let el of document.querySelectorAll('.feed-card:empty')) {
  7568. el?.remove();
  7569. console.log('已移除页面空白视频选项元素');
  7570. }
  7571. }, 1500);
  7572. };
  7573. var partition = {
  7574. isPartition,
  7575. isNewPartition,
  7576. startIntervalShieldingVideoList,
  7577. startShieldingHotVideoDayList
  7578. };
  7579. const observeNetwork = (url, windowUrl, winTitle, initiatorType) => {
  7580. if (!url.includes('api')) return;
  7581. if (globalValue.bOnlyTheHomepageIsBlocked) {
  7582. if (!bilibiliHome.isHome(windowUrl, winTitle)) return;
  7583. }
  7584. if (url.startsWith("https://api.bilibili.com/x/web-interface/wbi/index/top/feed/rcmd?web_location=")) {
  7585. if (globalValue.compatibleBEWLYBEWLY) return;
  7586. bilibiliHome.startDebounceShieldingChangeVideoList();
  7587. bilibiliHome.startDebounceShieldingHomeVideoList();
  7588. console.log("检测到首页加载了换一换视频列表和其下面的视频列表");
  7589. return;
  7590. }
  7591. if (url.startsWith("https://api.bilibili.com/x/v2/reply/wbi/main?oid=")) {
  7592. console.log("检测到评论区楼主评论加载了");
  7593. commentSectionModel.startShieldingComments();
  7594. return;
  7595. }
  7596. if (url.startsWith("https://api.bilibili.com/x/v2/reply/reply?oid=")) {
  7597. console.log("检测到评论区楼主层中的子层评论列表加载了");
  7598. commentSectionModel.startShieldingComments();
  7599. }
  7600. if (url.startsWith("https://api.bilibili.com/x/web-interface/popular?ps=")) {
  7601. popularAll.startShieldingVideoList();
  7602. }
  7603. if (url.startsWith("https://api.bilibili.com/x/web-interface/popular/series/one?number=")) {
  7604. popularAll.startShieldingVideoList(true);
  7605. }
  7606. if (url.startsWith("https://api.bilibili.com/x/polymer/web-dynamic/v1/feed/space?offset=")) {
  7607. console.log("检测到用户动态加载了");
  7608. dynamic.startThrottleShieldingDynamicContent();
  7609. }
  7610. if (url.startsWith("https://api.live.bilibili.com/xlive/web-interface/v1/second/getList?platform=web&parent_area_id=")) {
  7611. console.log("检测到直播间加载了分区下的房间列表");
  7612. liveSectionModel.startShieldingLiveRoom();
  7613. }
  7614. if (url.startsWith("https://api.live.bilibili.com/xlive/web-interface/v1/index/getList?platform=web")) {
  7615. console.log("检测到直播间加载了推荐房间列表");
  7616. liveHome.startShieldingLiveRoom();
  7617. }
  7618. if (url.startsWith('https://api.bilibili.com/x/web-interface/ranking/region?day=')) {
  7619. console.log("检测到专区热门排行榜加载了");
  7620. partition.startShieldingHotVideoDayList();
  7621. }
  7622. if (searchModel.isSearchVideoNetWorkUrl(url) || searchModel.isSearchLiveRoomNetWorkUrl(url)) {
  7623. eventEmitter.send('通知屏蔽');
  7624. }
  7625. };
  7626. var observeNetwork$1 = {
  7627. observeNetwork
  7628. };
  7629. const shielding_user_vue = {
  7630. template: `
  7631. <div>
  7632. <el-dropdown v-if="shieldingModelShow"
  7633. @command="dropdownEvent">
  7634. <el-button round>
  7635. 屏蔽操作<i class="el-icon-arrow-down el-icon--right"></i>
  7636. </el-button>
  7637. <el-dropdown-menu v-slot="dropdown">
  7638. <el-dropdown-item command="屏蔽uid"
  7639. v-if="shieldingUseUIDrButShow">屏蔽(uid)
  7640. </el-dropdown-item>
  7641. <el-dropdown-item command="移除屏蔽uid"
  7642. v-if="removedShieldingUIDrButShow">移除屏蔽(uid)
  7643. </el-dropdown-item>
  7644. <el-dropdown-item command="选择用户屏蔽" v-if="selectUserBlockingButShow">选择用户屏蔽</el-dropdown-item>
  7645. </el-dropdown-menu>
  7646. </el-dropdown>
  7647. </div>`,
  7648. data() {
  7649. return {
  7650. shieldingModelShow: true,
  7651. shieldingUseUIDrButShow: false,
  7652. removedShieldingUIDrButShow: false,
  7653. selectUserBlockingButShow: false,
  7654. uid: -1
  7655. }
  7656. },
  7657. methods: {
  7658. async dropdownEvent(item) {
  7659. if (item === '屏蔽uid') {
  7660. const {name, uid} = await space.getUserInfo();
  7661. this.$confirm(`是否屏蔽当前用户【${name}】uid=【${uid}】`, '提示', {
  7662. confirmButtonText: '确定',
  7663. cancelButtonText: '取消',
  7664. type: 'warning'
  7665. }).then(() => {
  7666. const {status, res} = ruleUtil.addRulePreciseUid(uid);
  7667. this.$alert(res);
  7668. if (status) {
  7669. this.shieldingUseUIDrButShow = false;
  7670. this.removedShieldingUIDrButShow = true;
  7671. }
  7672. });
  7673. return
  7674. }
  7675. if (item === '移除屏蔽uid') {
  7676. const {uid} = await space.getUserInfo();
  7677. ruleUtil.delRUlePreciseUid(uid);
  7678. return
  7679. }
  7680. if (item === '选择用户屏蔽') {
  7681. await videoPlayModel.selectUserBlocking();
  7682. return
  7683. }
  7684. this.$message('未知选项');
  7685. }
  7686. },
  7687. async created() {
  7688. if (videoPlayModel.isVideoPlayPage()) {
  7689. this.selectUserBlockingButShow = true;
  7690. }
  7691. if (space.isSpacePage()) {
  7692. this.urlUID = elUtil.getUrlUID(window.location.href);
  7693. if (ruleKeyListData$1.getPreciseUidArr().includes(this.urlUID)) {
  7694. this.shieldingModelShow = true;
  7695. this.removedShieldingUIDrButShow = true;
  7696. await this.$alert('当前用户为已标记uid黑名单', '提示');
  7697. return;
  7698. }
  7699. if (await space.isPersonalHomepage()) {
  7700. this.shieldingModelShow = false;
  7701. return;
  7702. }
  7703. this.shieldingModelShow = true;
  7704. this.shieldingUseUIDrButShow = true;
  7705. }
  7706. }
  7707. };
  7708. const addLayout = () => {
  7709. const div = document.createElement('div');
  7710. const divStyle = div.style;
  7711. divStyle.position = 'fixed';
  7712. divStyle.zIndex = '9000';
  7713. divStyle.right = "0";
  7714. divStyle.top = '13%';
  7715. divStyle.transition = 'transform 0.5s';
  7716. if (!localMKData.isFirstFullDisplay()) {
  7717. divStyle.transform = 'translateX(80%)';
  7718. } else {
  7719. if (localMKData.isHalfHiddenIntervalAfterInitialDisplay()) {
  7720. setTimeout(() => {
  7721. divStyle.transform = 'translateX(80%)';
  7722. eventEmitter.send('el-msg', '自动隐藏外部主面板显隐按钮');
  7723. }, 8000);
  7724. }
  7725. }
  7726. const vueDiv = document.createElement('div');
  7727. div.appendChild(vueDiv);
  7728. document.body.appendChild(div);
  7729. const config = {
  7730. components: {
  7731. shielding_user_vue,
  7732. },
  7733. el: vueDiv,
  7734. template: `
  7735. <div v-show="panelShow" @mouseenter="handleMouseEnter" @mouseleave="handleMouseLeave">
  7736. <div>
  7737. <el-button round @click="showBut">主面板</el-button>
  7738. </div>
  7739. <shielding_user_vue/>
  7740. </div>`,
  7741. data() {
  7742. return {
  7743. panelShow: localMKData.isShowRightTopMainButSwitch(),
  7744. }
  7745. },
  7746. methods: {
  7747. showBut() {
  7748. eventEmitter.send('主面板开关');
  7749. },
  7750. handleMouseEnter() {
  7751. divStyle.transform = "translateX(0)";
  7752. },
  7753. handleMouseLeave() {
  7754. divStyle.transform = 'translateX(80%)';
  7755. }
  7756. },
  7757. created() {
  7758. eventEmitter.on('显隐主面板开关', (bool) => {
  7759. this.panelShow = bool;
  7760. });
  7761. }
  7762. };
  7763. new Vue(config);
  7764. };
  7765. var rightFloatingLayoutVue = {
  7766. addLayout
  7767. };
  7768. const generalUrl=[
  7769. "popular/rank/all",
  7770. "popular/rank/douga",
  7771. "popular/rank/music",
  7772. "popular/rank/dance",
  7773. "popular/rank/game",
  7774. "popular/rank/knowledge",
  7775. "popular/rank/tech",
  7776. "popular/rank/sports",
  7777. "popular/rank/car",
  7778. "popular/rank/life",
  7779. "popular/rank/food",
  7780. "popular/rank/animal",
  7781. "popular/rank/kichiku",
  7782. "popular/rank/fashion",
  7783. "popular/rank/ent",
  7784. "popular/rank/cinephile",
  7785. "popular/rank/origin",
  7786. "popular/rank/rookie"
  7787. ];
  7788. const isPopularHistory = (url) => {
  7789. return url.includes("popular/history")
  7790. };
  7791. const isPopularAllPage = (url) => {
  7792. return url.includes("www.bilibili.com/v/popular/all");
  7793. };
  7794. const isPopularWeeklyPage = (url) => {
  7795. return url.includes("www.bilibili.com/v/popular/weekly");
  7796. };
  7797. const isGeneralPopularRank=(url)=>{
  7798. return generalUrl.some(itemUrl => url.includes(itemUrl));
  7799. };
  7800. const getVideoDataList$1 = async () => {
  7801. const elList = await elUtil.findElementsUntilFound(".rank-list>li");
  7802. const list = [];
  7803. for (let el of elList) {
  7804. const title = el.querySelector(".title").textContent.trim();
  7805. const userUrl = el.querySelector(".detail>a").href;
  7806. const uid = elUtil.getUrlUID(userUrl);
  7807. const name = el.querySelector(".up-name").textContent.trim();
  7808. const detailStateEls = el.querySelectorAll('.detail-state>.data-box');
  7809. let nPlayCount = detailStateEls[0].textContent.trim();
  7810. nPlayCount = sFormatUtil.toPlayCountOrBulletChat(nPlayCount);
  7811. let nBulletChat = detailStateEls[1].textContent.trim();
  7812. nBulletChat = sFormatUtil.toPlayCountOrBulletChat(nBulletChat);
  7813. const videoUrl = el.querySelector('.img>a')?.href || null;
  7814. const bv = elUtil.getUrlBV(videoUrl);
  7815. list.push({
  7816. title,
  7817. userUrl,
  7818. uid,
  7819. name,
  7820. videoUrl,
  7821. bv,
  7822. nPlayCount,
  7823. nBulletChat,
  7824. nDuration: -1,
  7825. el,
  7826. insertionPositionEl: el.querySelector(".detail-state"),
  7827. explicitSubjectEl: el.querySelector(".info")
  7828. });
  7829. }
  7830. return list;
  7831. };
  7832. const startShieldingRankVideoList = async () => {
  7833. const list = await getVideoDataList$1();
  7834. for (let videoData of list) {
  7835. if (video_shielding.shieldingVideoDecorated(videoData)) {
  7836. continue;
  7837. }
  7838. eventEmitter.send('添加热门视频屏蔽按钮', {data: videoData, maskingFunc: startShieldingRankVideoList});
  7839. }
  7840. };
  7841. var popular = {
  7842. isPopularHistory,
  7843. isPopularAllPage,
  7844. isGeneralPopularRank,
  7845. isPopularWeeklyPage,
  7846. startShieldingRankVideoList,
  7847. };
  7848. const isOldHistory = (url) => {
  7849. return url.includes('https://www.bilibili.com/account/history')
  7850. };
  7851. const getVideoDataList = async () => {
  7852. const elList = await elUtil.findElementsUntilFound('#history_list>.history-record');
  7853. const list = [];
  7854. for (let el of elList) {
  7855. const labelEL = el.querySelector('.cover-contain>.label');
  7856. if (labelEL !== null) {
  7857. const label = labelEL.textContent.trim();
  7858. console.log(`排除${label}`);
  7859. continue
  7860. }
  7861. const titleEl = el.querySelector('.title');
  7862. const userEl = el.querySelector('.w-info>span>a');
  7863. const title = titleEl.textContent.trim();
  7864. const videoUrl = titleEl.href;
  7865. const bv = elUtil.getUrlBV(videoUrl);
  7866. const name = userEl.textContent.trim();
  7867. const userUrl = userEl.href;
  7868. const uid = elUtil.getUrlUID(userUrl);
  7869. list.push({
  7870. title,
  7871. videoUrl,
  7872. name,
  7873. userUrl,
  7874. uid,
  7875. el,
  7876. bv,
  7877. explicitSubjectEl: el.querySelector('.r-txt'),
  7878. insertionPositionEl: el.querySelector('.subtitle')
  7879. });
  7880. }
  7881. return list
  7882. };
  7883. const startShieldingVideo = async () => {
  7884. console.log('开始屏蔽旧版历史记录视频列表');
  7885. const list = await getVideoDataList();
  7886. const css = {right: "45px"};
  7887. for (let videoData of list) {
  7888. if (video_shielding.shieldingVideoDecorated(videoData)) {
  7889. continue;
  7890. }
  7891. videoData.css = css;
  7892. eventEmitter.send('视频添加屏蔽按钮', {data: videoData, maskingFunc: startShieldingVideo});
  7893. }
  7894. console.log('屏蔽旧版历史记录视频列表完成');
  7895. };
  7896. const intervalExecutionStartShieldingVideo = () => {
  7897. setInterval(startShieldingVideo, 2000);
  7898. };
  7899. var oldHistory = {
  7900. isOldHistory,
  7901. intervalExecutionStartShieldingVideo
  7902. };
  7903. eventEmitter.on('通知屏蔽', () => {
  7904. const url = window.location.href;
  7905. const title = document.title;
  7906. if (globalValue.bOnlyTheHomepageIsBlocked) return;
  7907. if (searchLive.isSearchLivePage(url)) {
  7908. searchLive.startShieldingLiveRoomList();
  7909. }
  7910. if (searchModel.isSearch(url)) {
  7911. searchModel.startShieldingVideoList();
  7912. }
  7913. if (bilibiliHome.isHome(url, title)) {
  7914. if (globalValue.compatibleBEWLYBEWLY) return;
  7915. if (globalValue.adaptationBAppCommerce) {
  7916. BLBLGate.startIntervalShieldingGateVideoList();
  7917. }
  7918. bilibiliHome.startDebounceShieldingHomeVideoList();
  7919. }
  7920. if (videoPlayModel.isVideoPlayPage(url)) {
  7921. videoPlayModel.startShieldingVideoList();
  7922. }
  7923. if (collectionVideoPlayPageModel.iscCollectionVideoPlayPage(url)) {
  7924. collectionVideoPlayPageModel.startShieldingVideoList();
  7925. }
  7926. if (popular.isPopularAllPage(url) || popular.isPopularHistory(url)) {
  7927. popularAll.startShieldingVideoList();
  7928. }
  7929. if (popular.isPopularWeeklyPage(url)) {
  7930. popularAll.startShieldingVideoList(true);
  7931. }
  7932. if (popular.isGeneralPopularRank(url)) {
  7933. popular.startShieldingRankVideoList();
  7934. }
  7935. if (topicDetail.isTopicDetailPage(url)) {
  7936. topicDetail.startShielding();
  7937. }
  7938. if (dynamic.isDynamicPage(url)) {
  7939. dynamic.startThrottleShieldingDynamicContent();
  7940. }
  7941. if (videoPlayWatchLater.isVideoPlayWatchLaterPage(url)) {
  7942. videoPlayWatchLater.startDebounceShieldingVideoList();
  7943. }
  7944. if (liveSectionModel.isLiveSection(url)) {
  7945. liveSectionModel.startShieldingLiveRoom();
  7946. }
  7947. if (liveHome.isLiveHomePage(url)) {
  7948. liveHome.startShieldingLiveRoom();
  7949. liveHome.startShieldingTopLiveRoom();
  7950. }
  7951. if (oldHistory.isOldHistory(url)) {
  7952. oldHistory.intervalExecutionStartShieldingVideo();
  7953. }
  7954. if (partition.isPartition(url) || partition.isNewPartition(url)) {
  7955. partition.startIntervalShieldingVideoList();
  7956. }
  7957. });
  7958. const replaceKeywords = (arr, actionScope, content) => {
  7959. if (arr.length === 0 || !enableReplacementProcessing()) return returnTempVal;
  7960. for (const v of arr) {
  7961. if (!content.includes(v.findVal)) continue;
  7962. if (!v.actionScopes.some(aItem => aItem === actionScope)) continue;
  7963. return {
  7964. state: true,
  7965. content: content.replaceAll(v.findVal, v.replaceVal)
  7966. }
  7967. }
  7968. return returnTempVal
  7969. };
  7970. const replaceEmoticons = (arr, el, alt) => {
  7971. if (arr.length === 0 || !enableReplacementProcessing()) return returnTempVal;
  7972. for (const v of arr) {
  7973. if (!v.actionScopes.some(aItem => aItem === '评论表情')) continue;
  7974. if (v.findVal !== alt) continue;
  7975. if (v.replaceVal === '') {
  7976. el?.remove();
  7977. return {state: true, model: 'del', content: alt};
  7978. }
  7979. return {
  7980. state: true,
  7981. model: 'subStr',
  7982. content: v.replaceVal
  7983. }
  7984. }
  7985. return returnTempVal
  7986. };
  7987. eventEmitter.on('event-评论通知替换关键词', (commentsData) => {
  7988. const {contentsEl, name, uid} = commentsData;
  7989. if (!contentsEl) return;
  7990. const spanEls = contentsEl.querySelectorAll('span');
  7991. const imgEls = contentsEl.querySelectorAll('img');
  7992. const aEls = contentsEl.querySelectorAll('a');
  7993. const substituteWordsArr = getSubstituteWordsArr();
  7994. if (isClearCommentEmoticons()) {
  7995. for (let imgEl of imgEls) {
  7996. imgEl?.remove();
  7997. eventEmitter.send('打印信息', `已清除${name}的评论中的表情`);
  7998. }
  7999. } else {
  8000. for (let imgEl of imgEls) {
  8001. if (imgEl.getAttribute('replace') !== null) continue;
  8002. const alt = imgEl.getAttribute('alt');
  8003. imgEl.setAttribute('replace', '');
  8004. if (alt === null) continue;
  8005. imgEl.setAttribute('title', alt);
  8006. const {state, model, content} = replaceEmoticons(substituteWordsArr, imgEl, alt);
  8007. if (!state) continue;
  8008. if (model === 'del') {
  8009. eventEmitter.send('打印信息', `已清除用户${name}的评论中的表情`);
  8010. continue;
  8011. }
  8012. if (model === 'subStr') {
  8013. imgEl.outerHTML = `<span replace>${content}</span>`;
  8014. eventEmitter.send('打印信息', `已替换用户${name}的评论中的表情:`);
  8015. }
  8016. }
  8017. }
  8018. if (isReplaceCommentSearchTerms()) {
  8019. for (let aEl of aEls) {
  8020. const text = aEl.textContent;
  8021. aEl.outerHTML = `<span replace>${text}</span>`;
  8022. eventEmitter.send('打印信息', `已替换用户${name}的评论中的搜索跳转关键词:`);
  8023. }
  8024. }
  8025. for (let spanEl of spanEls) {
  8026. if (spanEl.getAttribute('replace') !== null) continue;
  8027. const elContent = spanEl.textContent;
  8028. const {state, content} = replaceKeywords(substituteWordsArr, '评论内容', elContent);
  8029. if (!state) continue;
  8030. spanEl.textContent = content;
  8031. spanEl.setAttribute('replace', '');
  8032. eventEmitter.send('打印信息', `已替换用户${name}的评论内容:原\n${elContent}现\n${content}`);
  8033. }
  8034. });
  8035. window.addEventListener('load', () => {
  8036. console.log('页面加载完成');
  8037. rightFloatingLayoutVue.addLayout();
  8038. router.staticRoute(document.title, window.location.href);
  8039. watch.addEventListenerUrlChange((newUrl, oldUrl, title) => {
  8040. router.dynamicRouting(title, newUrl);
  8041. });
  8042. });
  8043. watch.addEventListenerNetwork((url, windowUrl, winTitle, initiatorType) => {
  8044. observeNetwork$1.observeNetwork(url, windowUrl, winTitle, initiatorType);
  8045. });
  8046. document.addEventListener('keydown', function (event) {
  8047. if (event.key === "`") {
  8048. eventEmitter.send('主面板开关');
  8049. }
  8050. });
  8051. })(Vue, Dexie);

QingJ © 2025

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