barrage-keywords-stop

抖音、斗鱼、虎牙、bilibili弹幕关键字屏蔽,按下 ctrl+alt+k 即可激活🧨

  1. // ==UserScript==
  2. // @name barrage-keywords-stop
  3. // @namespace https://github.com/wuxin0011/tampermonkey-script/barrage-keywords-stop
  4. // @version 0.0.4
  5. // @author wuxin0011
  6. // @description 抖音、斗鱼、虎牙、bilibili弹幕关键字屏蔽,按下 ctrl+alt+k 即可激活🧨
  7. // @license MIT
  8. // @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAABIFJREFUWEe1l11oHFUUx//nzm6tgQa/GpudrSE0YgI+VAR9EE0LftGgxA9adDNbS7UPsTuJPkSwARVaQaE2mU0jpJbUmSw+iKVIG6lGaqQPGqiCPrSR2pBmZ1ut1opWa7J7j9zZ3Xaz2c1uku3A7jzMvef/u+eec+49hAU8HfbU/RL0CIGaQGgCvN9vAC54b8aIIDm2bPryiXe3Nv5VjmkqNWj7YKJRaNwO4AkQVpUan/5OvzBkTGMx1BMOnJxvzrwApuO+CUCJr/DMEg+D6ZhkTAqfb3K5zz85jctacsbXIJgbmPlOgBsY9CgB1QCSYI5a4eDrxSCKApiOOwLggawwS22/Fa4dLscDnbFf16bkzBsEPJ6dL5OyK/rCHT/nzy8IYNrxOIhuVfsqQJEeI3CoHOH8MRE7YQrCTgb7AZwUTKH8LZkDYDruKQB1YP4Bmmi3QoETixHPnWPabgyEpxWEJpLP7gnVncl+nwVwbSAfnhGpbe+H6v6omDjjYBqCP7WM4KY5AJmAew3ghGTfw33hVROVFLfCeijixC8R6AZm3hUNB3d68aH+vFTz8dfpaKeIZQQ+qLS4p+MkWgX4I09YUGtvKHDUAzBt1wLhJQCHLEN/7nqIZ22aTvwAQJvA2GeFddMDiNjuWSKsJJbresOrv10KwNU4YhxUbs+3paopk/gKoIRlBNZQRyzxEEs+CrBrGcGG6yme44XTAOmSuYUidrybiHagCHG5QKVWXjAtmXdTTo62W4Y+WK5gYYOF3Z5v03TcLQD61aLJdNzv1KlGgh7rDQVUJizoWcjKs4az286M7xXAv5m0WDDAYsSV1rW4wyUFMAXgtoV6YLHiuQDMuLCoLViK+CwA8DEF8DmAB8G8zQoHnVIBsFTxdOGLGyAaANCnsqALhLcADFmGrqph0acS4h6A4+4D0MbgTup0ptZJiM8ATFqG3lhMvVLiGQDvyBestVDX/lMrriyr/hHg21mI5miodmxO3mbP8yUWK6/sx87dR1KOAhizDL05fRY48V0EepWZh6Ph4DNLKTIlY8hx1WnYCokua7Me9QA67USTJFYr9xFxd29bcHc6WDI3mQqsPO36xIsAR0G4mJzx39u/peb81RuRacffBtErYMxYYb260uLb7fP1gpIjAAWYMBBt0zu8Apjrso6h+CfMtEHVaO/6VKGVdw5O3JTS/ANEpHqL48ll/qf6N9b8PQcgcuDsGtK0j72Op1LisfjaVIr6iXAPgAkN/if3GDWnswufcyvOxEMs03ZNE3Goty14uFRwFfqurmAascWMlQCmoaHFel4/nju2YF/wcixRp0nuVsUi7Sb6MCnle3s3B38qB8S0z20gkdrqbScAJoxqLKI9Ru2R/Pnzt2bpkrnD6xOA38EYBXiCmc4wY3z6PxqvqrkxmbryT70mqT6VQlAI2ZwVBjAOyD7LWF30kluyOVXeECkZEUTNDNxdjgcY/A0YX1Ytr4q+s/GWP+ebUxJgVpY47l1MtB7M6wHcPGsvCReZ5UhKaF/sDQUmywFVY/4HmMRRtLE+F8gAAAAASUVORK5CYII=
  9. // @source https://github.com/wuxin0011/tampermonkey-script/barrage-keywords-stop
  10. // @supportURL https://github.com/wuxin0011/tampermonkey-script/issues
  11. // @match https://www.huya.com/*
  12. // @match https://live.douyin.com/*
  13. // @match https://live.bilibili.com/*
  14. // @match https://www.douyu.com/*
  15. // ==/UserScript==
  16. const selectKeywordsLocal = "selectKeywordsLocal";
  17.  
  18. const isNoShowTipKey = "tip_isNoShowTipKey";
  19.  
  20. const isFisrtInstallKey = "isFisrtInstallKey";
  21.  
  22. const selectOnlyThieRoom = "selectOnlyThieRoom";
  23.  
  24. const isAnimationKey = "m_isAnimationKey";
  25.  
  26. const AnimationTimeKey = "m_time_isAnimationKey";
  27.  
  28. const defaultKeywords = [ "送出", "6666", "直播间" ];
  29.  
  30. const localLink = window.location.href;
  31.  
  32. const isDouYinLive = /https?:\/\/live\.douyin.*/.test(localLink);
  33.  
  34. const isHyLive = /https?:\/\/www\.huya\.com.+/.test(localLink);
  35.  
  36. const isDouyuLive = /https?:\/\/.*douyu.*(\/((.*rid=\d+)|(\d+)).*)$/.test(localLink);
  37.  
  38. const isBiliBiliLive = /https?:\/\/live\.bilibili.*/.test(localLink);
  39.  
  40. const isLocalHost = /127\..*/.test(localLink);
  41.  
  42. const MAX_ANIMATION_TIME = 2;
  43.  
  44. const DEFAULT_ANIMATION_TIME = .5;
  45.  
  46. const setItem = (k, v, isParse = false) => window.localStorage.setItem(k, isParse ? JSON.stringify(v) : v);
  47.  
  48. const getItem = (k, isParse = false) => isParse ? JSON.parse(window.localStorage.getItem(k)) : window.localStorage.getItem(k);
  49.  
  50. const isFisrtInstall = () => getItem(isFisrtInstallKey) == null || getItem(isFisrtInstallKey) !== isFisrtInstallKey;
  51.  
  52. const isNoShowTip = () => getItem(isNoShowTipKey) == null || getItem(isNoShowTipKey) !== isNoShowTipKey;
  53.  
  54. const getAnimationTime = () => getItem(AnimationTimeKey) == null ? DEFAULT_ANIMATION_TIME : isNaN(getItem(AnimationTimeKey)) ? DEFAULT_ANIMATION_TIME : getItem(AnimationTimeKey) > MAX_ANIMATION_TIME ? DEFAULT_ANIMATION_TIME : getItem(AnimationTimeKey);
  55.  
  56. const isOpenTranisition = () => getItem(isAnimationKey) == null || getItem(isAnimationKey) === isAnimationKey;
  57.  
  58. const selectKeywords = () => isFisrtInstall() || getItem(selectKeywordsLocal) == null ? defaultKeywords : getItem(selectKeywordsLocal, true);
  59.  
  60. const createRoomId = id => id ? `${selectOnlyThieRoom}_${id}` : `${selectOnlyThieRoom}_${localLink}`;
  61.  
  62. const getRoomId = () => {
  63. let match = null;
  64. try {
  65. if (!localLink) {
  66. return "";
  67. }
  68. if (isBiliBiliLive) {
  69. match = localLink.match(/https:\/\/live\.bilibili\..*\/(\d+).*/);
  70. } else if (isDouYinLive) {
  71. match = localLink.match(/https:\/\/live\.douyin\..*\/(\d+).*/);
  72. } else if (isHyLive) {
  73. match = localLink.match(/https:\/\/www\.huya\.com\/(.*)/);
  74. } else if (isDouyuLive) {
  75. if (/.*rid=(\d+).*/.test(localLink)) {
  76. match = localLink.match(/rid=(\d+)/);
  77. } else if (localLink.match(/https:\/\/www\.douyu\.com\/(\d+).*/)) {
  78. match = localLink.match(/https:\/\/www\.douyu\.com\/(\d+).*/);
  79. }
  80. }
  81. } catch (error) {}
  82. if (match !== null && match.length >= 1) {
  83. return match[1];
  84. }
  85. return localLink;
  86. };
  87.  
  88. const isFull = () => {
  89. if ("fullscreenElement" in document) {
  90. return !!document["fullscreenElement"];
  91. }
  92. if ("webkitFullscreenElement" in document) {
  93. return !!document["webkitFullscreenElement"];
  94. }
  95. if ("mozFullScreenElement" in document) {
  96. return !!document["mozFullScreenElement"];
  97. }
  98. if ("msFullscreenElement" in document) {
  99. return !!document["msFullscreenElement"];
  100. }
  101. };
  102.  
  103. const roomId = () => createRoomId(getRoomId());
  104.  
  105. const selectOnlyThisRoomsKeywords = () => getItem(roomId()) == null ? defaultKeywords : getItem(roomId(), true);
  106.  
  107. const MARK = "dm-mark-version";
  108.  
  109. const MARK_TAG = (t = 0) => `mark-version-${t}`;
  110.  
  111. const removeDom = (dom, r = false) => {
  112. try {
  113. dom.style.display = "none";
  114. if (r) {
  115. dom.remove();
  116. }
  117. } catch (ignore) {}
  118. };
  119.  
  120. const SUPPORT = {
  121. HY: "HY_LIVE",
  122. DOUYIN: "DOUYIN_LIVE",
  123. DOUYU: "DOUYU_LIVE",
  124. BILIBILI: "BILIBILI_LIVE",
  125. LOCALHOST: "LOCALHOST_LIVE"
  126. };
  127.  
  128. const TAG_TYPE = {
  129. [SUPPORT.DOUYIN]: [ ".xgplayer-danmu>div[data-line-index]", ".webcast-chatroom___list .webcast-chatroom___item", ".xgplayer-danmu div" ],
  130. [SUPPORT.HY]: [ "#player-video #danmuwrap #danmudiv .danmu-item", "#player-video #danmuwrap #danmudiv #danmudiv2", "#player-marquee-wrap .player-marquee-noble-item", "#player-marquee-wrap .player-banner-enter", "#chat-room__list>div[data-cmid]" ],
  131. [SUPPORT.BILIBILI]: [ ".web-player-danmaku .danmaku-item-container .bili-dm", "#chat-items .chat-item" ],
  132. [SUPPORT.DOUYU]: [ "#douyu_room_normal_player_danmuDom .ani-broadcast", "#js-barrage-container #js-barrage-list li" ],
  133. [SUPPORT.LOCALHOST]: [ "video" ]
  134. };
  135.  
  136. const style = `\n \n .m-dm-container {\n --dm-container-width: 500px;\n --dm-container-height: 300px;\n --dm-input-add-keywords-width: 120px;\n --dm-input-time-width: 20px;\n --dm-container-background-color: 30, 23, 37;\n --dm-font-color: #fff;\n --dm-font-color-hover: #000;\n --dm-background-color: 0, 0, 0;\n --dm-background-color-hover: #fff;\n --dm-border-color: #fff;\n --dm-border-color-hover: #000;\n }\n\n\n\n\n .m-dm-container {\n width: var(--dm-container-width) ;\n height: var(--dm-container-height) ;\n background-color: rgba(var(--dm-container-background-color), 1) ;\n position: fixed ;\n display: flex ;\n flex-direction: column ;\n box-sizing: border-box ;\n box-shadow: 2px 2px 10px rgba(var(--dm-background-color), 0.7) ;\n border-radius: 10px ;\n position: fixed ;\n right: 0 ;\n top: 100px ;\n border: none ;\n transition: transform ease-in-out 0.5s ;\n z-index: 999999 ;\n box-sizing: border-box ;\n padding: 10px ;\n }\n\n .m-dm-input-animation-time,\n .m-dy-input-add-keywords {\n width: var(--dm-input-add-keywords-width) ;\n padding: 8px 12px ;\n border: none ;\n outline: none ;\n margin-left: 10px ;\n margin-top: 10px ;\n border-radius: 10px ;\n }\n\n .m-dm-input-animation-time,\n .m-dy-input-add-keywords:focus {\n border: none ;\n outline: none ;\n }\n\n .m-dm-input-animation-time {\n width: var(--dm-input-time-width) ;\n }\n\n .m-dm-install-link {\n display:inline-block ;\n float:right ;\n right:5px ;\n color: var(--dm-font-color) ;\n }\n\n\n\n .m-dm-container-header,\n .m-dm-container-footer {\n height: 44px ;\n position: relative ;\n }\n\n .m-dm-container-header #m-dm-close-btn {\n float:right ;\n right: 3px ;\n color: var(--dm-font-color) ;\n font-size: 30px ;\n cursor: pointer ;\n position: absolute ;\n }\n\n\n .m-dm-container-body {\n flex: 1 ;\n overflow: auto ;\n }\n\n .m-dm-keywords-tag {\n display: inline-block ;\n padding: 5px ;\n background-color: var(--dm-background-color) ;\n border: none ;\n margin: 5px ;\n cursor: pointer ;\n color: var(--dm-font-color) ;\n font-size: 12px ;\n outline: 1px solid var(--dm-border-color) ;\n border-radius: 10px ;\n }\n\n .m-dm-keywords-tag:hover {\n background-color:var(--dm-font-color);\n color:var(--dm-font-color-hover);\n }\n\n\n .m-dm-time-button,\n .m-dm-all-keywords-button,\n .m-dm-delete-keywords-button,\n .m-dm-add-keywords-button {\n display: inline-block ;\n padding: 4px 8px ;\n text-align: center ;\n border: none ;\n outline: none ;\n background-color: var(--dm-background-color-hover) ;\n color: var(--dm-font-color-hover) ;\n cursor: pointer ;\n border: 1px solid var(--dm-border-color) ;\n border-radius: 10px ;\n }\n\n \n .m-dm-time-button:hover,\n .m-dm-all-keywords-button:hover,\n .m-dm-delete-keywords-button:hover,\n .m-dm-add-keywords-button:hover {\n background-color: rgb(var(--dm-background-color)) ;\n color: var(--dm-font-color) ;\n border: 1px solid var(--dm-border-color) ;\n\n }\n\n .m-dm-container-footer {\n box-sizing: border-box ;\n padding: 10px ;\n }\n\n .m-dm-container-footer .message-tip{\n color: var(--dm-font-color) ;\n opacity:1;\n display:inline-block;\n transition:opacity 0.5s ease-out;\n }\n\n\n .m-dm-ani-close {\n transform: translateX(var(--dm-container-width)) ;\n }\n\n .m-dm-container-body {\n overflow: auto ;\n -webkit-overflow-scrolling: touch ;\n scrollbar-width: thin ;\n scrollbar-color: #888888 #f0f0f0 ;\n -webkit-overflow-scrolling: touch ;\n scrollbar-width: none ;\n -ms-overflow-style: none ;\n }\n\n\n\n .m-dm-container-body::-webkit-scrollbar {\n width: 4px ;\n }\n\n .m-dm-container-body::-webkit-scrollbar-track {\n background-color: rgb(22, 24, 35) ;\n }\n\n .m-dm-container-body::-webkit-scrollbar-thumb {\n background-color: #333 ;\n border-radius: 4px ;\n }\n\n\n \n `;
  137.  
  138. const containerDOMStr = ` \n <div class="m-dm-container-header">\n <input type="text" class="m-dy-input-add-keywords" placeholder="请输入关键字">\n <div class="m-dm-add-keywords-button">确认</div>\n <div class="m-dm-all-keywords-button" title="当前弹幕仅在房间内生效,点击切换到全房间">房间</div>\n <div class="m-dm-delete-keywords-button">清空</div>\n <input type="checkbox" class="m-dm-animation-checkbox" id="m-dm-animation-checkbox" title="如果弹幕区出现抖动,添加一个过渡可能好点">\n <input type="text" class="m-dm-input-animation-time" id="m-dm-input-animation-time" title="自定义输出一个过渡时间,默认为0.5s,建议数字大小在0-1之间" placeholder="请输入弹幕过渡时间">\n <div class="m-dm-time-button">确认</div>\n <span title="收起 使用 ctrl+alt+k可唤醒 我哦" class="m-dm-close-btn" id="m-dm-close-btn"> &times </span>\n </div>\n <div class="m-dm-container-body"></div>\n <div class="m-dm-container-footer">\n <p class="message-tip"></p>\n <a href="https://greatest.deepsurf.us/zh-CN/scripts/475878-barrage-keywords-stop" target="_blank" title="更新" class="m-dm-install-link">反馈</a>\n </div>\n`;
  139.  
  140. class BarrageKeywordsStop extends HTMLElement {
  141. constructor() {
  142. super();
  143. this.attachShadow({
  144. mode: "open"
  145. });
  146. const css = document.createElement("style");
  147. css.innerHTML = style;
  148. this.shadowRoot.appendChild(css);
  149. }
  150. createContainer(tagName, isShow, isBefore = false) {
  151. var _a;
  152. if (!tagName) {
  153. return null;
  154. }
  155. const c = document.querySelector(tagName);
  156. if (!c) {
  157. console.log("当前容器不存在!请检查", tagName);
  158. return null;
  159. }
  160. const plugin = document.createElement("barrage-keywords-stop");
  161. const shadowRoot = plugin.shadowRoot;
  162. const dmContainer = document.createElement("div");
  163. dmContainer.className = `m-dm-container ${isFisrtInstall() || isShow ? "" : "m-dm-ani-close"} `;
  164. dmContainer.innerHTML = containerDOMStr;
  165. const tip = dmContainer.querySelector(".m-dm-container-footer .message-tip");
  166. tip.textContent = isNoShowTip() ? "使用ctrl+alt+k可唤醒或者关闭哦!" : "";
  167. shadowRoot.appendChild(dmContainer);
  168. if (isBefore) {
  169. (_a = c === null || c === void 0 ? void 0 : c.parentNode) === null || _a === void 0 ? void 0 : _a.insertBefore(plugin, c.nextSibling);
  170. } else {
  171. c.append(plugin);
  172. }
  173. return dmContainer;
  174. }
  175. }
  176.  
  177. (function() {
  178. if (typeof window === undefined) {
  179. return;
  180. }
  181. const tipTimeout = 2e3;
  182. let isAnimation = false;
  183. let animationTime = DEFAULT_ANIMATION_TIME;
  184. let nodeVersion = 0;
  185. let beforeTag = null;
  186. let keywordsCache = [];
  187. let tipMessageElement = null;
  188. let isStart = false;
  189. let tagInitSuccess = true;
  190. let isAllRooms = false;
  191. let isSupport = true;
  192. let currentContainer = null;
  193. let requestAnimationFrameTimer = 0;
  194. let BARRAGE_CONTAINER = [];
  195. const contains = text => {
  196. if (!text) {
  197. return false;
  198. }
  199. for (let index = 0; index < keywordsCache.length; index++) {
  200. if (keywordsCache[index] && text.indexOf(keywordsCache[index]) !== -1) {
  201. return true;
  202. }
  203. }
  204. return false;
  205. };
  206. let findBarrages = () => {
  207. const findTargetText = selector => {
  208. if (!selector) {
  209. return;
  210. }
  211. const nodes = document.querySelectorAll(`${selector} :not([${MARK}="${MARK_TAG(nodeVersion)}"])`);
  212. for (let index = 0; index < nodes.length; index++) {
  213. const node = nodes[index];
  214. if (node instanceof HTMLElement) {
  215. if (contains(node === null || node === void 0 ? void 0 : node.textContent)) {
  216. if (isAnimation) {
  217. node.style.opacity = "0";
  218. node.style.transition = `opacity ${animationTime}s ease-out`;
  219. node.addEventListener("transitionend", (() => {
  220. removeDom(node, true);
  221. }));
  222. } else {
  223. removeDom(node, true);
  224. }
  225. }
  226. node.setAttribute(MARK, MARK_TAG(nodeVersion));
  227. }
  228. }
  229. };
  230. for (let i = 0; i < BARRAGE_CONTAINER.length; i++) {
  231. findTargetText(BARRAGE_CONTAINER[i]);
  232. }
  233. requestAnimationFrameTimer = window.requestAnimationFrame(findBarrages);
  234. };
  235. const installBeforeInfo = () => {
  236. console.log("欢迎使用弹幕屏蔽插件...");
  237. console.log("是否是首次安装", isFisrtInstall() ? "是" : "否");
  238. console.log("是否不需要快捷键提示", isNoShowTip() ? "需要" : "不需要");
  239. };
  240. const keywordsUpdate = array => {
  241. if (!Array.isArray(array)) {
  242. array = [];
  243. }
  244. isAllRooms ? setItem(selectKeywordsLocal, array, true) : setItem(roomId(), array, true);
  245. notify();
  246. };
  247. const removeKeywords = text => {
  248. if (!Array.isArray(keywordsCache)) {
  249. return;
  250. }
  251. const index = keywordsCache.findIndex((t => t == text));
  252. if (index >= 0) {
  253. addTipMessageText(`关键词 ${text} 已移除`);
  254. keywordsCache.splice(index, 1);
  255. keywordsUpdate([ ...keywordsCache ]);
  256. }
  257. };
  258. const createKeywords = text => {
  259. if (!Array.isArray(keywordsCache)) {
  260. keywordsCache = [];
  261. }
  262. const index = keywordsCache.findIndex((t => t == text));
  263. if (index === -1) {
  264. addTipMessageText(`关键词 ${text} 已添加`);
  265. keywordsCache = [ text, ...keywordsCache ];
  266. keywordsUpdate(keywordsCache);
  267. }
  268. };
  269. customElements.define("barrage-keywords-stop", BarrageKeywordsStop);
  270. const initInfo = () => {
  271. keywordsCache = [];
  272. if (Array.isArray(selectOnlyThisRoomsKeywords())) {
  273. keywordsCache = [ ...new Set(selectOnlyThisRoomsKeywords()) ];
  274. }
  275. if (Array.isArray(selectKeywords())) {
  276. keywordsCache = [ ...new Set([ ...keywordsCache, ...selectKeywords() ]) ];
  277. }
  278. isAnimation = isOpenTranisition();
  279. animationTime = getAnimationTime();
  280. console.log("是否开启动画过渡效果🕢:", isAnimation ? "开启了弹幕过渡效果" : "关闭了弹幕过渡效果");
  281. console.log("弹幕过渡时长🕑:", animationTime, "s");
  282. console.log("重新扫描中...当前关键词🧹:", keywordsCache);
  283. };
  284. const notify = () => {
  285. try {
  286. window.cancelAnimationFrame(requestAnimationFrameTimer);
  287. initInfo();
  288. if (Array.isArray(keywordsCache) && keywordsCache.length > 0) {
  289. nodeVersion = nodeVersion + 2;
  290. findBarrages();
  291. setTimeout((() => {
  292. addTipMessageText("弹幕重新扫描中...🚀");
  293. }), tipTimeout);
  294. } else {
  295. addTipMessageText("当前标签为空!停止扫描!🧹");
  296. }
  297. } catch (error) {
  298. addTipMessageText("弹幕插件出现异常了😭");
  299. }
  300. };
  301. const addOperationEvent = () => {
  302. let dmContainer = currentContainer;
  303. if (!dmContainer) {
  304. console.error("获取不到弹幕容器");
  305. return;
  306. }
  307. dmContainer = dmContainer;
  308. const dmInput = dmContainer.querySelector(".m-dy-input-add-keywords");
  309. const dmAnimationCheckbox = dmContainer.querySelector("#m-dm-animation-checkbox");
  310. const dmAniTimeInput = dmContainer.querySelector("#m-dm-input-animation-time");
  311. const dmTimeButton = dmContainer.querySelector(".m-dm-time-button");
  312. const dmBody = dmContainer.querySelector(".m-dm-container-body");
  313. const dmAddButton = dmContainer.querySelector(".m-dm-add-keywords-button");
  314. const dmChangeButton = dmContainer.querySelector(".m-dm-all-keywords-button");
  315. const dmCloseButton = dmContainer.querySelector("#m-dm-close-btn");
  316. const dmDeleteButton = dmContainer.querySelector(".m-dm-delete-keywords-button");
  317. if (!dmInput || !dmAddButton || !dmBody) {
  318. console.log("element has null");
  319. return;
  320. }
  321. tipMessageElement = dmContainer.querySelector(".m-dm-container-footer .message-tip");
  322. const find = text => keywordsCache.find((t => t == text));
  323. const add = () => {
  324. const text = dmInput.value;
  325. if (!text) {
  326. addTipMessageText("请输入关键字!");
  327. return;
  328. }
  329. if (find(text)) {
  330. addTipMessageText(`添加失败,关键词${text}已存在!😭`);
  331. dmInput.value = "";
  332. return;
  333. }
  334. createTag(dmBody, text);
  335. createKeywords(text);
  336. setItem(isFisrtInstallKey, isFisrtInstallKey);
  337. dmInput.value = "";
  338. notify();
  339. };
  340. dmInput.addEventListener("keydown", (event => {
  341. if (event.key === "Enter") {
  342. add();
  343. }
  344. }));
  345. dmAddButton.addEventListener("click", (() => {
  346. add();
  347. }));
  348. dmCloseButton.addEventListener("click", (() => {
  349. if (dmContainer.classList.contains("m-dm-ani-close")) {
  350. dmContainer.classList.remove("m-dm-ani-close");
  351. } else {
  352. dmContainer.classList.add("m-dm-ani-close");
  353. }
  354. }));
  355. dmChangeButton.addEventListener("click", (() => {
  356. isAllRooms = !isAllRooms;
  357. createTags();
  358. dmChangeButton.textContent = isAllRooms ? "全房间" : "房间";
  359. dmChangeButton.title = isAllRooms ? "当前弹幕在所有直播间生效,点击切换房间" : "当前弹幕仅在该房间生效,点击切换到全房间";
  360. addTipMessageText(`切换成功 ${isAllRooms ? "当前弹幕在所有直播间生效🧱" : "当前弹幕仅在该房间生效🚀"}`);
  361. }));
  362. dmAnimationCheckbox.checked = isOpenTranisition();
  363. dmAnimationCheckbox.addEventListener("change", (() => {
  364. setItem(isAnimationKey, dmAnimationCheckbox.checked ? isAnimationKey : `NO_${isAnimationKey}`);
  365. addTipMessageText(`弹幕过渡效果${dmAnimationCheckbox.checked ? `已开启,过渡时间${dmAniTimeInput.value}s` : "已关闭"}`);
  366. notify();
  367. }));
  368. dmAniTimeInput.value = getAnimationTime();
  369. const addTime = () => {
  370. if (isNaN(Number(dmAniTimeInput.value)) || (Number(dmAniTimeInput.value) < 0 || Number(dmAniTimeInput.value) > MAX_ANIMATION_TIME)) {
  371. addTipMessageText(`请输入0-${MAX_ANIMATION_TIME}的数字`);
  372. dmAniTimeInput.value = String(animationTime);
  373. return;
  374. }
  375. setItem(AnimationTimeKey, dmAniTimeInput.value);
  376. addTipMessageText(`弹幕过渡效果${isOpenTranisition() ? `已开启,过渡时间${dmAniTimeInput.value}s` : "已关闭,需要开启才能生效哦!"}`);
  377. notify();
  378. };
  379. dmAniTimeInput.addEventListener("keydown", (event => {
  380. if (event.key === "Enter") {
  381. addTime();
  382. }
  383. }));
  384. dmTimeButton.addEventListener("click", (event => {
  385. addTime();
  386. }));
  387. dmDeleteButton.addEventListener("click", (() => {
  388. if (confirm("确认清空?")) {
  389. removeTags();
  390. keywordsCache = [];
  391. setItem(isAllRooms ? selectKeywordsLocal : roomId(), keywordsCache, true);
  392. addTipMessageText(`${isAllRooms ? "全房间" : "该房间"}关键词标签已清空!`);
  393. notify();
  394. }
  395. }));
  396. console.log("响应事件监听完毕...");
  397. };
  398. const addTipMessageText = (text, wait = tipTimeout) => {
  399. if (!tipMessageElement) {
  400. return;
  401. }
  402. tipMessageElement.style.opacity = "1";
  403. tipMessageElement.textContent = text;
  404. setTimeout((() => {
  405. tipMessageElement.style.opacity = "0";
  406. }), wait);
  407. };
  408. const handleFullScreenChange = () => {
  409. removeDom(currentContainer, true);
  410. currentContainer = null;
  411. console.log("容器重新生成中....");
  412. if (isFull()) {
  413. createContainer("video", false, true);
  414. } else {
  415. createContainer("body", false);
  416. }
  417. };
  418. const addFullScreenEvent = () => {
  419. document.addEventListener("fullscreenchange", handleFullScreenChange);
  420. document.addEventListener("webkitfullscreenchange", handleFullScreenChange);
  421. document.addEventListener("mozfullscreenchange", handleFullScreenChange);
  422. document.addEventListener("MSFullscreenChange", handleFullScreenChange);
  423. };
  424. const addCtrlAltKEvent = () => {
  425. document.addEventListener("keydown", (function(event) {
  426. if (event.ctrlKey && event.altKey && event.key === "k") {
  427. const dmContainer = currentContainer;
  428. if (!dmContainer) {
  429. return;
  430. }
  431. if (dmContainer.classList.contains("m-dm-ani-close")) {
  432. dmContainer.classList.remove("m-dm-ani-close");
  433. setItem(isFisrtInstallKey, isFisrtInstallKey);
  434. } else {
  435. dmContainer.classList.add("m-dm-ani-close");
  436. }
  437. }
  438. }));
  439. };
  440. const createTag = (dmBody, text) => {
  441. if (!currentContainer) {
  442. return;
  443. }
  444. if (!dmBody) {
  445. dmBody = currentContainer.querySelector(".m-dm-container-body");
  446. }
  447. if (!dmBody) {
  448. return;
  449. }
  450. if (!text) {
  451. console.log("关键词内容不能为空! ");
  452. return;
  453. }
  454. const dmTag = document.createElement("span");
  455. dmTag.className = "m-dm-keywords-tag";
  456. dmTag.textContent = `${text}`;
  457. dmTag.title = `点击移除关键字: ${text}`;
  458. dmTag.addEventListener("click", (() => {
  459. removeKeywords(text);
  460. dmTag.remove();
  461. }));
  462. !!beforeTag ? dmBody.appendChild(dmTag) : dmBody.insertBefore(dmTag, beforeTag);
  463. beforeTag = dmTag;
  464. };
  465. const removeTags = () => {
  466. if (!currentContainer) {
  467. return;
  468. }
  469. const allTags = currentContainer.querySelectorAll(".m-dm-container-body .m-dm-keywords-tag");
  470. if (allTags && allTags.length > 0) {
  471. for (let i = 0; i < allTags.length; i++) {
  472. removeDom(allTags[i], true);
  473. }
  474. }
  475. };
  476. const createTags = () => {
  477. if (!currentContainer) {
  478. return;
  479. }
  480. removeTags();
  481. const dmBody = currentContainer.querySelector(".m-dm-container .m-dm-container-body");
  482. if (!dmBody) {
  483. return;
  484. }
  485. const keys = isAllRooms ? selectKeywords() : [ ...selectOnlyThisRoomsKeywords() ];
  486. if (!Array.isArray(keys)) {
  487. return;
  488. }
  489. for (let i = 0; i < keys.length; i++) {
  490. createTag(dmBody, keys[i]);
  491. }
  492. console.log("标签创建完毕....");
  493. };
  494. const createContainer = (tagName = "body", isShow = true, isBefore = false) => {
  495. currentContainer = (new BarrageKeywordsStop).createContainer(tagName, isShow, isBefore);
  496. if (!currentContainer) {
  497. isSupport = false;
  498. console.log("当前容器不存在!请检查", tagName);
  499. return;
  500. }
  501. console.log("弹幕容器创建完毕....");
  502. addOperation();
  503. };
  504. const addOperation = () => {
  505. if (!isSupport) {
  506. console.warn("不支持哦初始化失败");
  507. return;
  508. }
  509. if (!currentContainer) {
  510. console.log("未找到弹幕容器... ");
  511. return;
  512. }
  513. createTags();
  514. addOperationEvent();
  515. console.log("一切准备就绪!");
  516. notify();
  517. };
  518. const initDom = () => {
  519. addCtrlAltKEvent();
  520. addFullScreenEvent();
  521. if (isFisrtInstall()) {
  522. setTimeout((() => {
  523. createContainer("body", false);
  524. }), 5e3);
  525. } else {
  526. createContainer("body", false);
  527. }
  528. };
  529. const initTag = type => {
  530. if (!TAG_TYPE[type]) {
  531. tagInitSuccess = false;
  532. return;
  533. }
  534. BARRAGE_CONTAINER = TAG_TYPE[type];
  535. tagInitSuccess = !!BARRAGE_CONTAINER && Array.isArray(BARRAGE_CONTAINER) && BARRAGE_CONTAINER.length > 0;
  536. };
  537. const start = () => {
  538. if (isStart) {
  539. return;
  540. }
  541. console.log("弹幕插件执行中...");
  542. installBeforeInfo();
  543. if (isDouYinLive) {
  544. initTag(SUPPORT.DOUYIN);
  545. } else if (isHyLive) {
  546. initTag(SUPPORT.HY);
  547. } else if (isBiliBiliLive) {
  548. initTag(SUPPORT.BILIBILI);
  549. } else if (isDouyuLive) {
  550. initTag(SUPPORT.DOUYU);
  551. } else if (isLocalHost) {
  552. initTag(SUPPORT.LOCALHOST);
  553. isSupport = true;
  554. } else {
  555. isSupport = false;
  556. }
  557. if (!tagInitSuccess) {
  558. console.log("标签初始化失败!");
  559. return;
  560. }
  561. if (isSupport) {
  562. initDom();
  563. } else {
  564. console.log("对不起不支持当前网址!", localLink);
  565. }
  566. isStart = true;
  567. };
  568. start();
  569. })();