Greasy Fork镜像 支持简体中文。

抖音优化

视频过滤,包括广告、直播或自定义规则,伪装登录(不可用)、屏蔽登录(不可用)弹窗、自定义清晰度选择、未登录(不可用)解锁画质选择、禁止自动播放、自动进入全屏、双击进入全屏、屏蔽弹幕和礼物特效、手机模式、修复进度条拖拽、自定义视频和评论区背景色等

  1. // ==UserScript==
  2. // @name 抖音优化
  3. // @namespace https://github.com/WhiteSevs/TamperMonkeyScript
  4. // @version 2025.4.1
  5. // @author WhiteSevs
  6. // @description 视频过滤,包括广告、直播或自定义规则,伪装登录(不可用)、屏蔽登录(不可用)弹窗、自定义清晰度选择、未登录(不可用)解锁画质选择、禁止自动播放、自动进入全屏、双击进入全屏、屏蔽弹幕和礼物特效、手机模式、修复进度条拖拽、自定义视频和评论区背景色等
  7. // @license GPL-3.0-only
  8. // @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAAAXNSR0IArs4c6QAAF19JREFUeF7tnQl8U1X2x38vabqlLWOFrpStUMrWsrdUEARkEREQCoiAghswHQdQEFREGXfhD24MjOiMIqKD4IyICCObFCgt1JaC0ELpmq0L3ZLuyft/bkpL8pI07yUvaVre/Xz6KTT3nnvOud93381dzqXAU+riGzJarMMoGogGqD4A3RXAPQC8eKribhSjBVBJA0UUkAfgGgXq90YRzpZUybL4cAhlj5BAn9DxoOkFAGYB8LdHllCWsweuAfgvDWpvkUaWzrn07QI2ARDkExpPg14FGqNsrVgox58HaFCHAd2HRRrFEa5SOQEQ7B08VEdR7wCYxLUiIb8TPEDjB0qke1WpVv7BtjbWAARJQ9bQwPtsBQv52tID9CqVRrGNjQZsAKACpSG7ATzORqCQxzU8QIHerdQoFlvTplUAuvp19W/Q0gcAeqw1QcLnrugB+qRELJ5dWFl4y5J2FgFoanztYYAa6YqmCTqx9QCdLBGLp1qCwCIAgdLgEwA1jm01Qj5X9gB1SqWRPQCAZmppFoAgafBXNKhFrmySoBtnD+xRaeQLrQIQKA1eCVBbOYsXCri8ByhgrVIj/8BQUaMeIMgnqD9Ni664vCWCgjZ7QETTwxTVitRmAUYABHqHHACln9YVUsf1wFGVRj7ZBIAAafBkCtQvHddu85aJ40bYbbL2bIrdMpwpgKKouUq1bB+ps6UHCJCG/kyBnupMRVyhLj/VZbvUqN/5FWpfa2cTpBTOqdTyuBYAAqSh0RToNLs80U4L2wtAw76DqElY3/6sp6gJKrXsuL4HCJSGvAvgpfZnhf0a2wtA45GTqF6cYL8izpfwuUojf7oZgKsAIp2vQ9vXyARg2NVsTkr1u5aPtJWvmpQ5W1vBSU4bZL6l0sjvpTr7hkaIdXRmGyjgElUyARh+LRvfDBvOWrfClEvotGQTfChRS5knVVdxpNri9Dtr2Y7OqBNhDBUkDX2SBv1PR1fmqvKZAIy4lo09HAAgdmWMegwD1WT3VlNKKM7CfnWxq5rcoheZGCJLvR8CeN7ltXWQgkwARl7LxtccAUhauh4xKXdeHa+U3sQXlQoHacyr2D0EALKN6K7d4cMEIObaTeweNoyTl7P2/4I+r3/RUmZzWT62lBdwktE2malkKkAaco0C+raNAm1fKxOA2Myb+GooNwCIFckPLMaIklq9QadqyjFf2S5m1OWkByCjFbJ9+65MTABGZd7ElzYAcGnHNxj06X/0PizWNiAqP7k9+LOGANAIQNwetHWEjnwBQHS7GLcAQ6uIO4EJsjT8Ua9xhMq8yiQAmGwS4LUGFxfGBCAuMwf/GjrUJq1vHDyGLuu3w48S441budhRIbNJjjMLCQAw1gLuy8rBP4fYBgBpuLPLNmDUmUxcqKvCdPklZ7alTXUJADAAGJ2Vgy/sAMDwVRCvvIzEGteeERQAYAAwJisHn9sJQPOr4LCmFCuLr9v0ZDqrkAAAA4D7s3Kxa8gQu/2f/vFX6LLjAN4szcUBF54VFABgADD2ei4+G2w/AISggp9PomrtVswtSEeJtsFuqBwhQADAgQCQBqu/no+f5iZgxfWLjmg/u2UKADAAGHc9D/8YPNhuxxoJKK/CW7OfxsfnE/mVy4M0AQAGAA9cz8NOvgEgDUXTmLNtO85s/gR0RSUPTcePCAEABgDjb+RhRzTPPYBBWy0+cRKnjv+G+m8OgFa1/ZKxAAADgAk38vB3BwJAWFiReAb/69UV2sRkNJ5OQsOPR0GXts0GEgEABgATb+Rje3Q0P/1rK1I+/y0R2yN6oMpXqs/VeDYFdKECOpkCdHEp6JJS6IpvwdFbzgUAGAA8mJ2PT6McDwBp9CtZ2fiopAgnos1vx9TMWiIA4OhHkbkY5EwAmm376XwK9rhRuBgZbmSuAICjWx8AE4BJ2fn4xEk9ANO8vYnncBxapPcIQ/k9fhAAaAMAJmcX4OOoKCfUbLkKlaoY57NvomrLl0g5ew5FjQ0o0tajSNv0m88kjAEYY4ApNwvw0aC2BaC5gS8Pj8eAOuMQDsE5Z/hsfwgAMACYerMAHwoA8AqZSwtjjgEeulmAbQIALt1mvCpnCkAhtg0axGsdtgoTXgG2eo5DOSYA03IKsXUgewAq8mTo1D2UQ43sswoAsPeVzTmZADycU4j/4wDA5S/2oTpXhpGbVtqsg6WCAgC8u9RUIBOA6TkybBk4kHXNBIABW/chuYsX7n1+IXrPfJB1WWsZBQCseYiHz5kAPJIjw2YbACCqqGktMgb3xJCPNsDLv5Pd2gkAMFxIBQdCPKgfRIFdQJGfoC76f8PDHaiuAa2pBq2uBtQaNBw7zWoenQnAjFwZPhjAvQcwVPWGWIvioX3Q7Zm56DrK9i3mAgDkyFLcCLiNHw23cXH6xmeb2E6jOgKAVSXXscm/FzwoEdI7e0E8JQ7D1i1jq3pLvrsWAFG3UEgemwXJzKkQ9erO2XGkgK0AzMyV4/0BA1jX2TwGMCwwW3EZJEJIQqeueMW/Sf8ckRaKAd3h98AIRC6cBYmXp9U67joARJG94U4a/rFZoDr5WXVQaxlsBWBWrhzv8QQA0a+HxFMPwuO+gS3qVtJa3PDzQF1UOAKmjUPv6RPMmnLXAEBJveHx+ov6xodEwrnhO5eUIbi0DKHqanTT0ejr44OeL3yEvIIClOkaUU1rUa3T4d0ycu+ScWK+Ah7Nk+Pd/vz0AIY1TfS+B3N9AjBd2tlEBx1o1NA0akGjjiI/FBrETT8dfi1A1LMbPLe8Drf72Eeld6+tw8jLWZgAEaYOGgB/f9PT7edHxmNkjfWFFCYAs/PkeMcBADS3+jAPX8z1DUC8TwC8DOIKsaW+Qy0GiWOGwvubv4PyadoW1VoKkasQU6BErMQdU4YPgZdX67fRHY+NxwMa7gDMyVPg7f79ralzZ6B2ex7A3BigNSG9JF76HmGStz/6uXuzrq/DACB5ZDK8Ptti1XC/SjWeysrF8rFjrOY1zHBoVDweUnMHID5fgbf6OR4AQ10Dxe6I8vBBlLsU0eS3hw/I38ylDgGA53sb4P7kPKsNOj31Cp4L7YqIXj2s5mVm2B8Xj0er2gcA5ozzpkS4Vyxp+eksavr3dp5jDjh9P4DXzs2QzJzSaoP2y87HMzX1eDjG9kDO394Xj3mV3AGYm6/Am07uATjTzWMBpwLgvmQ+PN81jappaM/ylAwsj42Bp6eHXWbaCsC8fCX+1o/9hFNr8wB2GeCkwk4DgHzH9znVFETJUlqeeBGrJk/kxXRbAZhfoMSmSAEAXhrBUIjPyQMQ9YuwKPe53T/ghWVLeavXVgAeK1DiDQEA3tpBL8jzg41wXxxvUeizu77Di3/lPlfempa2ArCgQInXBQD4A0Aydwa8Pn7LosBlPx3H6nmz+avwtiTbAVDh9Uj2gdOFMUArTUf5+kB6aA9EfY1PvDQXWZ5yGavGcft+z5YUWwF4vFCFjX0FANj6udV8Hi+ugMeaFWbzRJ1Pw7f33w83NztjVNbWAeVVQFkV0NAIRPYA3CWwFYCFhSq8JgBgf/uTp548/aQXYCZdbgH+cUuN8WP019ZwT9dy8cdn38LvdDpCa+6EaW8WVCemIBPR6MUIy2NuFo25FrCosAgb+rIPnSy8Aiw0n+db6+H+tPkLxxd8/zNef4L7ZeSl+/8HzdbdCKtoCsrMNbEBYHFhEV4VAODqWtP8Pkk/g6z0MVOPU0n4NjbW7AqexVq1OmQl/A19Eu2LwM0GgCdkRXglQugB7CLAbcIY/SqfufT++QzMHH8/e/nZhchZ8gp6lNWwL2MhpwCAqWMcMhNoqfufkXIJH4wby74hswshm78GIbWm73mmkNS6Kn1AxtQ6Ncp1DSjTNqJc14ieEk/0dPPS//7czC0ezDHAk7JivBxhecKKWa8wBjDTnD6//dfsV79d1wtw/2CWJ2+ra5E/bgnCzAzyDKskjb67SomkWtsibzEBWCIrxnoBAPYPKTMnGf0TAJgpXF6Ew33Yv1szlr6CgSmtx9ndVl6A98rybVfWTICIpfJirOsj9AA2O1WycDa8trxhUn5ZRhZWx8awkluRdhV+iza2mndYwQXIG+tYyWstE7MHeEpejJcEAGz3q9cX2yCZZrqit09WiuiI3qwEX0zYhKGnLN/pO7QgBYpGfiJlMAF4Wl6CtX36sNKTZBLGAAxXSY9+B3G08a5art3/leFz0d/Cw9285551C1nJKADA85Ux5r7/L0+9glVjRrNqs8zTyYhYsdls3rdu5eGTikJWcthmYgLwjKIEa3oLPQBb/5nk8716GhRjm/aeHAVGDGS30fL7v7yG2SevmchNq1NjuuISGml+rzhiAvCsogQvCgDY3P7wk6UDjAWeQ0WV6NOT3RGvg8texsNnbpgo8FxRJn7UlNiumIWSTACeU5Tihd7sxirCGMCMU81dx36mRocune9l1XhHn3oJDybnGOW92VCD+wpTWZXnmomp7zJlKVaHCwBw9WNLfnMA/OHhCzc3N1YyTy56EWPTjL/bkyef9AB8J3Lc3DftmJHY5cpSrBIAsN3VTACkmmr8HsA+hs7/lqzFxAu5Rgq8VpqDzyrltitloSQ5ei79wfji9BXKW1gZbn4Dizkx5zduw8gDZ40+4vubCu+GGwjkfS2ACUCwshinwtnPrKUc+hXD1/3DyOZ5yiv4raacdz+Ym7TiCsCJmcsxLrtUAKDZA74ZJ0EF3DkBG5mdjx85xt49FjkF4yV3joc7agDo+c4rcF/6mFHjPS8vQQKHiaCk4XMQUycykjFNfglkcao9JN57AO+9O/QRPZrTkMyb+I7jZcy7Jy3GQsWdTR/rSrPxZaWSV3+S+AM+p/+rDzVjmF7OLsSTUezDxGX3fwS9xMbBHkYXpiK7wf7la14NtiCMdwA8N6yGe8Kd/f3heXIc5nDcmuh5YOeXGLJ1vz64AknkXP+H5fxOAEkWPAqvrZtM3PJOZi5mD2V3bVz66fOIWmF6wHVgfjJKXfSaOKbBvAPAdGyXkjKc6d6LM8xfT3kCj8uaniJHfAvw3rMdbhNNN6Z8kpmHSUPZ3Rl0eNM2TNlnPAAk+oblnuV9woqzA1kW4B0A8ZBBkP6yt6V6j7p6ZPjfCY/CUi+kpaajZv46xHr4oqCxDiMLLrAtajWfJ4lGsvxJs/m+zCrAqCHs9izsm5uAOVeLjOSotPUYnJ9iVQdXycA7AMQw7+93wW1MbIuNp9QNCA4M4GzzD39+FQ+dugZ3SoSxhb8jq6GaswxmAfHwaP1uZXMpLCkVxyaYj9djLv+h4bPwUJ1xSJvk2krMUGTYraezBDgEADKyJiPs5vThlWxMHTncJpu+W7kRsUd+xy+aW9h4y3iGkKtAMuDzvXTCYrFlv57F6hnTWIk9tWsvYrbt14eCM0z71EV43sUvjDbU1yEAUMEB8Ek82BL65dmUDLw4jsNGUEYT/Oulv6HfgTN6AMiikC3JbdI4eO/+xGJRt0I5jnh3QlhYV1biD49fhCnFpmvWm8vysaW8gJUMV8jkEACIYWSETQaEJI1Pv4odcTYeArntpezMG8h4Yj2WXTYddLXmSFFYCCTzZlo8odRc9pnTF7BmCrs4v1eOn0FAwmZ0FptGNBtTmIob7eQrILHdYQC4jR4J7/1f6P3bqaIKR7074Z4/2R8/9+W3P8Hu3Ew07DsINFi+kVs8MBKSeTP0jU918m31YXsoKxfbOFwZ/+ucBEzINB78kQou1akxWZ7uCg82ax0cBgDRwHBS6L30LMyKY7cn0Jr2Pfd+CxJkqvG3c6DLKpp+yisgCugMqkcYyFMvCmO//pAlNQ0zZ0mH0nwZqqb+Gd1FpkGc3i7Lw8c8z1dY84W9nzsUAMmch+H16bt6Haf9fgVbR7PbFWTNqAvJFzEnKQnuTy2wlrXVzwedPo/9U1qPV8QUkLj8NdyXaLphheSbJEtHRr1tYxS7DLGjsEMB0PcC+z+H2+gY/WvgkMQbAV1MI2Xaon9VZRWe27ELydMfBIktzCk1NmJRSgY2TBzPqVjZzXyUzlqFcJ3piWYy90/WANpbcjgA5Hu39/efg/LyxKoLlznH+7Pm0P8cPIyk2loc6x6MigGtnzugi0oQn5WL+d26Iaof+zMKzTokTVqKGIX5J3xNSTa+ruJ3vcKa7Xx87nAAiJLuyxbD84216FGgwMGe4fDwsC8CmCXDz6VdwpnyclRSFKpEFKokboCORoSmBj0oCpEBXRAVxf4uAMN6kl54GzFH08xW3R4Hf82GOAUAUhmJCkoGbq+lXsVCW+MC8IG8DTKyfzqOe9Z9inso88Es2uvTT1zhNABIZT6//4rQ+kZ87eePMA6jdBvajNciabHzEa3Rdbin3+kAkAp9s87ikQuXseURdlOuvLakDcLOLFiNuAzLS9Ht+elvEwBIpdKj/8Y7VbWYO5n9wosNbWd3kbOPJmDUddMJn2bB/9GUYLkDNqvarTgHAU59BRjqFfTdZ9gV1g3DbRyUcbDRpqznpj2L2HzL+xBzGmoRV3jRJtmuVKjNACBOGPb+RvxzzqMIMNhD6ArOSXpwKWKUrU/oTJCl4Y96jSuoa48OWgIAWWRv/fYFe6qwUvbhZUuwK2EF0CPEgbWwF508bjFGlLYehOovxVn4Xl3MXqjr5iwjAMgAtKn3n+kXjUXjJyDivRfazFUX3vwU0gMnENlgvL7PVGhDaQ52OeCMQlsYTgOZVKA09DxAs7+wx0GaksuU1vbsD9Gf5yN80QwH1WIq9ureg9Bs/w7Dyq3HG5guv4QL7WS7N0sHHiU9wNcAuAftY1kDl2wEgvc7hyOrTzBCVj+BsPsdx+WVf30P9e5DGFlk/T1OtnmtLLkOMvDrYOkjKkgasoYG3ncVw8gFSiv/FIZHpJ2R4Qmoo8IROGsiwh+2/yujWlWC63t+hPbAMQyrsLyXwNAXhzSlWF1yA5W6RldxEW96UKCWUF18Q0aLdDjNm1SeBJGLFgkIXd2a1g1kaERBty4QR/eBf+xg9Jw8FmIPy3cM1mlqoEzNQFHaVVQnpsLvphwRNTTIXTxsUka9BjsrZNjfMQZ7Zk3Wiqi++kt1AqUh5HCbPxvHODNPkNgdS/2CscQvGD4i03n4PKoRanfjv4u1NPwbdAig2J1GZtpDDnTsrJRjZ4Uc9bT56V9n+sCBdV1TaeT9mgHYBeApB1Zml+jeEq8WEOwS1ErhKp0W+9VF+sbP7XjvenOWv6fSyNc1AeATOh40bXxQ3lGetkNuhMQb06X36q9f7cvhssXWqjxWXYYj1bfwc3VpuznOZYcLW4rSoAYXaWTpLfeqBfqEnAWNUXwId4aMkZ5+eNDbHz3cPPVnCMlvc68JQ11qaZ0+vJxCW4eDmlIc1JTcVY3e7Asa1OEijewh8v8WAIJ8QuNpmv63MxrPUXWQbdrNQHR384RSW3+7wcnvOn3sYCEBNOgpRRrFESMAbg8GyR8nCU7qwB6g8YOqWt50YMOwByD/CfYOHqqjqPa/xNWB289e0yhKN0CpVv5hFgDyR1ebGLLXYKG8oQfoVSqNYpvhX4wv1739iStNDwsNyI8HKNC7lRrFYqY0swA07RUMPQHQHG534EdRQYojPECfVGkUD5iTbAkAdPXr6t+g1R4GKMetyDjCVkEmwwN0skQsnlpYWXiLEwAk820I9gPUOMGv7dED1CmJmHrUUuObfAuwZGKQNPgrGtSi9uiCu1jnPSqNnLRZq9G1Lb4CmI4LlAavBKitd7FD243pFLBWqZF/wEZh1gDovyL6BPWndaI3QWEWG+FCHqd74KiIptcrqhWsI2tzAqDZnABp8GRA9FcK9FSnmyhUaOoBCucoUFuVatk+ru6xCYA7IIRGU6BJrFWyiY/9ldtctRTym/MAGdX/AIr6RqWWHbfVRXYBYFhpZ9/QCDcd4mjQJMxmJA10pwASG44E/bXzinBbzesQ5Ui0zDKAKgTo6xSQrhXhXHGVPJEP6/4fAayH1yFvEgIAAAAASUVORK5CYII=
  9. // @supportURL https://github.com/WhiteSevs/TamperMonkeyScript/issues
  10. // @match *://*.douyin.com/*
  11. // @match *://*.iesdouyin.com/*
  12. // @require https://fastly.jsdelivr.net/gh/WhiteSevs/TamperMonkeyScript@86be74b83fca4fa47521cded28377b35e1d7d2ac/lib/CoverUMD/index.js
  13. // @require https://fastly.jsdelivr.net/npm/@whitesev/utils@2.6.4/dist/index.umd.js
  14. // @require https://fastly.jsdelivr.net/npm/@whitesev/domutils@1.5.1/dist/index.umd.js
  15. // @require https://fastly.jsdelivr.net/npm/@whitesev/pops@2.0.2/dist/index.umd.js
  16. // @require https://fastly.jsdelivr.net/npm/qmsg@1.3.0/dist/index.umd.js
  17. // @connect *
  18. // @grant GM_deleteValue
  19. // @grant GM_getResourceText
  20. // @grant GM_getValue
  21. // @grant GM_info
  22. // @grant GM_registerMenuCommand
  23. // @grant GM_setValue
  24. // @grant GM_unregisterMenuCommand
  25. // @grant GM_xmlhttpRequest
  26. // @grant unsafeWindow
  27. // @run-at document-start
  28. // ==/UserScript==
  29.  
  30. (function (Qmsg, Utils, DOMUtils, pops) {
  31. 'use strict';
  32.  
  33. var __defProp = Object.defineProperty;
  34. var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
  35. var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
  36. var _a;
  37. var _GM_deleteValue = /* @__PURE__ */ (() => typeof GM_deleteValue != "undefined" ? GM_deleteValue : void 0)();
  38. var _GM_getResourceText = /* @__PURE__ */ (() => typeof GM_getResourceText != "undefined" ? GM_getResourceText : void 0)();
  39. var _GM_getValue = /* @__PURE__ */ (() => typeof GM_getValue != "undefined" ? GM_getValue : void 0)();
  40. var _GM_info = /* @__PURE__ */ (() => typeof GM_info != "undefined" ? GM_info : void 0)();
  41. var _GM_registerMenuCommand = /* @__PURE__ */ (() => typeof GM_registerMenuCommand != "undefined" ? GM_registerMenuCommand : void 0)();
  42. var _GM_setValue = /* @__PURE__ */ (() => typeof GM_setValue != "undefined" ? GM_setValue : void 0)();
  43. var _GM_unregisterMenuCommand = /* @__PURE__ */ (() => typeof GM_unregisterMenuCommand != "undefined" ? GM_unregisterMenuCommand : void 0)();
  44. var _GM_xmlhttpRequest = /* @__PURE__ */ (() => typeof GM_xmlhttpRequest != "undefined" ? GM_xmlhttpRequest : void 0)();
  45. var _unsafeWindow = /* @__PURE__ */ (() => typeof unsafeWindow != "undefined" ? unsafeWindow : void 0)();
  46. var _monkeyWindow = /* @__PURE__ */ (() => window)();
  47. const KEY = "GM_Panel";
  48. const ATTRIBUTE_INIT = "data-init";
  49. const ATTRIBUTE_KEY = "data-key";
  50. const ATTRIBUTE_DEFAULT_VALUE = "data-default-value";
  51. const ATTRIBUTE_INIT_MORE_VALUE = "data-init-more-value";
  52. const PROPS_STORAGE_API = "data-storage-api";
  53. const UISelect = function(text, key, defaultValue, data, callback, description) {
  54. let selectData = [];
  55. if (typeof data === "function") {
  56. selectData = data();
  57. } else {
  58. selectData = data;
  59. }
  60. let result = {
  61. text,
  62. type: "select",
  63. description,
  64. attributes: {},
  65. props: {},
  66. getValue() {
  67. return this.props[PROPS_STORAGE_API].get(key, defaultValue);
  68. },
  69. callback(event, isSelectedValue, isSelectedText) {
  70. let value = isSelectedValue;
  71. log.info(`选择:${isSelectedText}`);
  72. this.props[PROPS_STORAGE_API].set(key, value);
  73. if (typeof callback === "function") {
  74. callback(event, value, isSelectedText);
  75. }
  76. },
  77. data: selectData
  78. };
  79. Reflect.set(result.attributes, ATTRIBUTE_KEY, key);
  80. Reflect.set(result.attributes, ATTRIBUTE_DEFAULT_VALUE, defaultValue);
  81. Reflect.set(result.props, PROPS_STORAGE_API, {
  82. get(key2, defaultValue2) {
  83. return PopsPanel.getValue(key2, defaultValue2);
  84. },
  85. set(key2, value) {
  86. PopsPanel.setValue(key2, value);
  87. }
  88. });
  89. return result;
  90. };
  91. const UISwitch = function(text, key, defaultValue, clickCallBack, description, afterAddToUListCallBack) {
  92. let result = {
  93. text,
  94. type: "switch",
  95. description,
  96. attributes: {},
  97. props: {},
  98. getValue() {
  99. return Boolean(
  100. this.props[PROPS_STORAGE_API].get(key, defaultValue)
  101. );
  102. },
  103. callback(event, __value) {
  104. let value = Boolean(__value);
  105. log.success(`${value ? "开启" : "关闭"} ${text}`);
  106. this.props[PROPS_STORAGE_API].set(key, value);
  107. },
  108. afterAddToUListCallBack
  109. };
  110. Reflect.set(result.attributes, ATTRIBUTE_KEY, key);
  111. Reflect.set(result.attributes, ATTRIBUTE_DEFAULT_VALUE, defaultValue);
  112. Reflect.set(result.props, PROPS_STORAGE_API, {
  113. get(key2, defaultValue2) {
  114. return PopsPanel.getValue(key2, defaultValue2);
  115. },
  116. set(key2, value) {
  117. PopsPanel.setValue(key2, value);
  118. }
  119. });
  120. return result;
  121. };
  122. const afterEnterDeepMenuCallBack = (formConfig, container) => {
  123. let $oneClickOpen = container.sectionBodyContainer.querySelector(
  124. ".keyboard-oneClickOpen"
  125. );
  126. let $oneClickClose = container.sectionBodyContainer.querySelector(
  127. ".keyboard-oneClickClose"
  128. );
  129. let clickCallBack = (isOpen) => {
  130. var _a2;
  131. (_a2 = container.sectionBodyContainer) == null ? void 0 : _a2.querySelectorAll(".pops-panel-switch").forEach(($ele) => {
  132. let $input = $ele.querySelector(
  133. ".pops-panel-switch__input"
  134. );
  135. let $checkbox = $ele.querySelector(
  136. ".pops-panel-switch__core"
  137. );
  138. if (isOpen) {
  139. if (!$input.checked) {
  140. $checkbox.click();
  141. }
  142. } else {
  143. if ($input.checked) {
  144. $checkbox.click();
  145. }
  146. }
  147. });
  148. };
  149. domUtils.on($oneClickOpen, "click", (event) => {
  150. utils.preventEvent(event);
  151. clickCallBack(true);
  152. });
  153. domUtils.on($oneClickClose, "click", (event) => {
  154. utils.preventEvent(event);
  155. clickCallBack(false);
  156. });
  157. };
  158. const AutoOpenOrClose = {
  159. text: (
  160. /*html*/
  161. `
  162. <p>注:开启是禁用该快捷键、关闭是不禁用该快捷键</p>
  163. <a href="javascript:;" class="keyboard-oneClickOpen">禁用全部快捷键</a>
  164. <br>
  165. <a href="javascript:;" class="keyboard-oneClickClose">取消禁用全部快捷键</a>
  166. `
  167. ),
  168. afterEnterDeepMenuCallBack
  169. };
  170. const PanelCommonConfig = {
  171. id: "panel-config-common",
  172. title: "通用",
  173. forms: [
  174. {
  175. text: "",
  176. type: "forms",
  177. forms: [
  178. {
  179. text: "Toast配置",
  180. type: "deepMenu",
  181. forms: [
  182. {
  183. text: "",
  184. type: "forms",
  185. forms: [
  186. UISelect(
  187. "Toast位置",
  188. "qmsg-config-position",
  189. "bottom",
  190. [
  191. {
  192. value: "topleft",
  193. text: "左上角"
  194. },
  195. {
  196. value: "top",
  197. text: "顶部"
  198. },
  199. {
  200. value: "topright",
  201. text: "右上角"
  202. },
  203. {
  204. value: "left",
  205. text: "左边"
  206. },
  207. {
  208. value: "center",
  209. text: "中间"
  210. },
  211. {
  212. value: "right",
  213. text: "右边"
  214. },
  215. {
  216. value: "bottomleft",
  217. text: "左下角"
  218. },
  219. {
  220. value: "bottom",
  221. text: "底部"
  222. },
  223. {
  224. value: "bottomright",
  225. text: "右下角"
  226. }
  227. ],
  228. (event, isSelectValue, isSelectText) => {
  229. log.info("设置当前Qmsg弹出位置" + isSelectText);
  230. },
  231. "Toast显示在页面九宫格的位置"
  232. ),
  233. UISelect(
  234. "最多显示的数量",
  235. "qmsg-config-maxnums",
  236. 3,
  237. [
  238. {
  239. value: 1,
  240. text: "1"
  241. },
  242. {
  243. value: 2,
  244. text: "2"
  245. },
  246. {
  247. value: 3,
  248. text: "3"
  249. },
  250. {
  251. value: 4,
  252. text: "4"
  253. },
  254. {
  255. value: 5,
  256. text: "5"
  257. }
  258. ],
  259. void 0,
  260. "限制Toast显示的数量"
  261. ),
  262. UISwitch(
  263. "逆序弹出",
  264. "qmsg-config-showreverse",
  265. false,
  266. void 0,
  267. "修改Toast弹出的顺序"
  268. )
  269. ]
  270. }
  271. ]
  272. }
  273. ]
  274. },
  275. {
  276. type: "forms",
  277. text: "",
  278. forms: [
  279. {
  280. text: "功能",
  281. type: "deepMenu",
  282. forms: [
  283. {
  284. text: "",
  285. type: "forms",
  286. forms: [
  287. UISwitch(
  288. "伪装登录(不可用)",
  289. "disguiseLogin",
  290. false,
  291. void 0,
  292. "使用随机UID进行伪装"
  293. ),
  294. UISwitch(
  295. "initial-scale=1",
  296. "dy-initialScale",
  297. false,
  298. void 0,
  299. "可配合手机模式放大页面"
  300. ),
  301. UISwitch(
  302. "移除<meta> apple-itunes-app",
  303. "dy-apple-removeMetaAppleItunesApp",
  304. true,
  305. void 0,
  306. "Safari使用,移除顶部横幅【Open in the 抖音 app】"
  307. ),
  308. UISwitch(
  309. "监听Router改变",
  310. "dy-common-listenRouterChange",
  311. true,
  312. void 0,
  313. "功能重载"
  314. ),
  315. UISwitch(
  316. "移除某些Cookie",
  317. "dy-cookie-remove__ac__",
  318. false,
  319. void 0,
  320. "阻止触发验证弹窗(maybe)"
  321. )
  322. ]
  323. },
  324. {
  325. text: "Url重定向",
  326. type: "forms",
  327. forms: [
  328. UISwitch(
  329. "重定向/home",
  330. "douyin-redirect-url-home-to-root",
  331. false,
  332. void 0,
  333. "/home => /"
  334. )
  335. ]
  336. }
  337. ]
  338. },
  339. {
  340. type: "deepMenu",
  341. text: "禁用抖音快捷键",
  342. afterEnterDeepMenuCallBack: AutoOpenOrClose.afterEnterDeepMenuCallBack,
  343. forms: [
  344. {
  345. type: "forms",
  346. text: AutoOpenOrClose.text,
  347. forms: [
  348. UISwitch(
  349. "赞|取消赞",
  350. "dy-keyboard-hook-likeOrDislike",
  351. false,
  352. void 0,
  353. "Z"
  354. ),
  355. UISwitch(
  356. "评论",
  357. "dy-keyboard-hook-comment",
  358. false,
  359. void 0,
  360. "X"
  361. ),
  362. UISwitch(
  363. "开启/关闭弹幕",
  364. "dy-keyboard-hook-danmaku-enable",
  365. false,
  366. void 0,
  367. "B"
  368. ),
  369. UISwitch(
  370. "收藏/取消收藏",
  371. "dy-keyboard-hook-collect-enable",
  372. false,
  373. void 0,
  374. "C"
  375. ),
  376. UISwitch(
  377. "复制分享口令",
  378. "dy-keyboard-hook-copyShareLink",
  379. false,
  380. void 0,
  381. "V"
  382. ),
  383. UISwitch(
  384. "清屏",
  385. "dy-keyboard-hook-clearScreen",
  386. false,
  387. void 0,
  388. "J"
  389. ),
  390. UISwitch(
  391. "自动连播",
  392. "dy-keyboard-hook-automaticBroadcast",
  393. false,
  394. void 0,
  395. "K"
  396. ),
  397. UISwitch(
  398. "视频信息",
  399. "dy-keyboard-hook-videoInfo",
  400. false,
  401. void 0,
  402. "I"
  403. ),
  404. UISwitch(
  405. "不感兴趣",
  406. "dy-keyboard-hook-notInterested",
  407. false,
  408. void 0,
  409. "R"
  410. ),
  411. UISwitch(
  412. "进入作者主页",
  413. "dy-keyboard-hook-enterAuthorHomePage",
  414. false,
  415. void 0,
  416. "F"
  417. ),
  418. UISwitch(
  419. "关注/取消关注",
  420. "dy-keyboard-hook-follow",
  421. false,
  422. void 0,
  423. "G"
  424. ),
  425. UISwitch(
  426. "抖音搜索",
  427. "dy-keyboard-hook-search",
  428. false,
  429. void 0,
  430. "Shift+F"
  431. ),
  432. UISwitch(
  433. "一键关闭当前页",
  434. "dy-keyboard-hook-closeTheCurrentPageWithOneClick",
  435. false,
  436. void 0,
  437. "Shift+Q"
  438. ),
  439. UISwitch(
  440. "上下翻页",
  441. "dy-keyboard-hook-pageUpAndDown",
  442. false,
  443. void 0,
  444. "↑↓"
  445. ),
  446. UISwitch(
  447. "快进快退",
  448. "dy-keyboard-hook-fastForwardAndFastBack",
  449. false,
  450. void 0,
  451. "← →"
  452. ),
  453. UISwitch(
  454. "暂停",
  455. "dy-keyboard-hook-pause",
  456. false,
  457. void 0,
  458. "空格"
  459. ),
  460. UISwitch(
  461. "网页内全屏",
  462. "dy-keyboard-hook-fullScreenInsideThePage",
  463. false,
  464. void 0,
  465. "Y"
  466. ),
  467. UISwitch(
  468. "全屏",
  469. "dy-keyboard-hook-fullScreen",
  470. false,
  471. void 0,
  472. "H"
  473. ),
  474. UISwitch(
  475. "稍后再看",
  476. "dy-keyboard-hook-watchItOutLater",
  477. false,
  478. void 0,
  479. "L"
  480. ),
  481. UISwitch(
  482. "音量调整",
  483. "dy-keyboard-hook-volumeAdjustment",
  484. false,
  485. void 0,
  486. "Shift + / Shift -"
  487. ),
  488. UISwitch(
  489. "呼出快捷键列表",
  490. "dy-keyboard-hook-listOfCallShortcutKeys",
  491. false,
  492. void 0,
  493. "?"
  494. ),
  495. UISwitch(
  496. "关闭快捷键列表",
  497. "dy-keyboard-hook-closeTheShortcutKeyList",
  498. false,
  499. void 0,
  500. "ESC"
  501. ),
  502. UISwitch(
  503. "相关推荐",
  504. "dy-keyboard-hook-relevantRecommendation",
  505. false,
  506. void 0,
  507. "N"
  508. )
  509. ]
  510. }
  511. ]
  512. }
  513. ]
  514. },
  515. {
  516. text: "",
  517. type: "forms",
  518. forms: [
  519. {
  520. text: "布局屏蔽-全局",
  521. type: "deepMenu",
  522. afterEnterDeepMenuCallBack: AutoOpenOrClose.afterEnterDeepMenuCallBack,
  523. forms: [
  524. {
  525. type: "forms",
  526. text: AutoOpenOrClose.text,
  527. forms: [
  528. UISwitch(
  529. "【屏蔽】登录(不可用)弹窗",
  530. "watchLoginDialogToClose",
  531. true,
  532. void 0,
  533. "屏蔽元素且自动等待元素出现并关闭登录(不可用)弹窗"
  534. ),
  535. UISwitch(
  536. "【屏蔽】底部?按钮",
  537. "shieldBottomQuestionButton",
  538. true,
  539. void 0,
  540. "屏蔽元素"
  541. )
  542. ]
  543. }
  544. ]
  545. },
  546. {
  547. text: "布局屏蔽-左侧导航栏",
  548. type: "deepMenu",
  549. afterEnterDeepMenuCallBack: AutoOpenOrClose.afterEnterDeepMenuCallBack,
  550. forms: [
  551. {
  552. type: "forms",
  553. text: AutoOpenOrClose.text,
  554. forms: [
  555. UISwitch(
  556. "【屏蔽】左侧导航栏",
  557. "shieldLeftNavigator",
  558. false,
  559. void 0,
  560. "屏蔽元素"
  561. ),
  562. UISwitch(
  563. "【屏蔽】首页",
  564. "shieldLeftNavigator-tab-home",
  565. false,
  566. void 0,
  567. "屏蔽元素"
  568. ),
  569. UISwitch(
  570. "【屏蔽】推荐",
  571. "shieldLeftNavigator-tab-recommend",
  572. false,
  573. void 0,
  574. "屏蔽元素"
  575. ),
  576. UISwitch(
  577. "【屏蔽】关注",
  578. "shieldLeftNavigator-tab-follow",
  579. false,
  580. void 0,
  581. "屏蔽元素"
  582. ),
  583. UISwitch(
  584. "【屏蔽】朋友",
  585. "shieldLeftNavigator-tab-friend",
  586. false,
  587. void 0,
  588. "屏蔽元素"
  589. ),
  590. UISwitch(
  591. "【屏蔽】我的",
  592. "shieldLeftNavigator-tab-user_self",
  593. false,
  594. void 0,
  595. "屏蔽元素"
  596. ),
  597. UISwitch(
  598. "【屏蔽】喜欢",
  599. "shieldLeftNavigator-tab-user_self_like",
  600. false,
  601. void 0,
  602. "屏蔽元素"
  603. ),
  604. UISwitch(
  605. "【屏蔽】收藏",
  606. "shieldLeftNavigator-tab-user_self_collection",
  607. false,
  608. void 0,
  609. "屏蔽元素"
  610. ),
  611. UISwitch(
  612. "【屏蔽】观看历史",
  613. "shieldLeftNavigator-tab-user_self_record",
  614. false,
  615. void 0,
  616. "屏蔽元素"
  617. ),
  618. UISwitch(
  619. "【屏蔽】看奥运",
  620. "shieldLeftNavigator-tab-olympics",
  621. false,
  622. void 0,
  623. "屏蔽元素"
  624. ),
  625. UISwitch(
  626. "【屏蔽】直播",
  627. "shieldLeftNavigator-tab-live",
  628. false,
  629. void 0,
  630. "屏蔽元素"
  631. ),
  632. UISwitch(
  633. "【屏蔽】放映厅",
  634. "shieldLeftNavigator-tab-vs",
  635. false,
  636. void 0,
  637. "屏蔽元素"
  638. ),
  639. UISwitch(
  640. "【屏蔽】短剧",
  641. "shieldLeftNavigator-tab-series",
  642. false,
  643. void 0,
  644. "屏蔽元素"
  645. ),
  646. UISwitch(
  647. "【屏蔽】知识",
  648. "shieldLeftNavigator-tab-channel_300203",
  649. false,
  650. void 0,
  651. "屏蔽元素"
  652. ),
  653. UISwitch(
  654. "【屏蔽】游戏",
  655. "shieldLeftNavigator-tab-channel_300205",
  656. false,
  657. void 0,
  658. "屏蔽元素"
  659. ),
  660. UISwitch(
  661. "【屏蔽】二次元",
  662. "shieldLeftNavigator-tab-channel_300206",
  663. false,
  664. void 0,
  665. "屏蔽元素"
  666. ),
  667. UISwitch(
  668. "【屏蔽】音乐",
  669. "shieldLeftNavigator-tab-channel_300209",
  670. false,
  671. void 0,
  672. "屏蔽元素"
  673. ),
  674. UISwitch(
  675. "【屏蔽】美食",
  676. "shieldLeftNavigator-tab-channel_300204",
  677. false,
  678. void 0,
  679. "屏蔽元素"
  680. ),
  681. UISwitch(
  682. "【屏蔽】美好跨年季",
  683. "shieldLeftNavigator-tab-activity_2644292",
  684. false,
  685. void 0,
  686. "屏蔽元素"
  687. ),
  688. UISwitch(
  689. "【屏蔽】2025新春环游记",
  690. "shieldLeftNavigator-tab-activity_2643710",
  691. false,
  692. void 0,
  693. "屏蔽元素"
  694. )
  695. ]
  696. }
  697. ]
  698. },
  699. {
  700. text: "布局屏蔽-顶部导航栏",
  701. type: "deepMenu",
  702. afterEnterDeepMenuCallBack: AutoOpenOrClose.afterEnterDeepMenuCallBack,
  703. forms: [
  704. {
  705. text: AutoOpenOrClose.text,
  706. type: "forms",
  707. forms: [
  708. UISwitch(
  709. "【屏蔽】顶部导航栏",
  710. "shieldTopNavigator",
  711. false,
  712. void 0,
  713. "屏蔽元素"
  714. ),
  715. UISwitch(
  716. "【屏蔽】右侧菜单栏",
  717. "shield-topNav-rightMenu",
  718. false,
  719. void 0,
  720. "屏蔽元素"
  721. ),
  722. UISwitch(
  723. "【屏蔽】客户端提示",
  724. "shieldClientTip",
  725. true,
  726. void 0,
  727. "屏蔽元素"
  728. ),
  729. UISwitch(
  730. "【屏蔽】充钻石",
  731. "shieldFillingBricksAndStones",
  732. true,
  733. void 0,
  734. "屏蔽元素"
  735. ),
  736. UISwitch(
  737. "【屏蔽】客户端",
  738. "shieldClient",
  739. true,
  740. void 0,
  741. "屏蔽元素"
  742. ),
  743. UISwitch(
  744. "【屏蔽】快捷访问",
  745. "shieldQuickAccess",
  746. false,
  747. void 0,
  748. "屏蔽元素"
  749. ),
  750. UISwitch(
  751. "【屏蔽】通知",
  752. "shieldNotifitation",
  753. false,
  754. void 0,
  755. "屏蔽元素"
  756. ),
  757. UISwitch(
  758. "【屏蔽】私信",
  759. "shieldPrivateMessage",
  760. false,
  761. void 0,
  762. "屏蔽元素"
  763. ),
  764. UISwitch(
  765. "【屏蔽】投稿",
  766. "shieldSubmission",
  767. false,
  768. void 0,
  769. "屏蔽元素"
  770. ),
  771. UISwitch(
  772. "【屏蔽】壁纸",
  773. "shieldWallpaper",
  774. false,
  775. void 0,
  776. "屏蔽元素"
  777. ),
  778. UISwitch(
  779. "【屏蔽】更多",
  780. "shield-topNav-rightMenu-more",
  781. false,
  782. void 0,
  783. "屏蔽元素"
  784. ),
  785. UISwitch(
  786. "【屏蔽】登录(不可用)头像",
  787. "shield-topNav-rightMenu-loginAvatar",
  788. false,
  789. void 0,
  790. "屏蔽元素"
  791. )
  792. ]
  793. }
  794. ]
  795. },
  796. {
  797. text: "布局屏蔽-搜索",
  798. type: "deepMenu",
  799. afterEnterDeepMenuCallBack: AutoOpenOrClose.afterEnterDeepMenuCallBack,
  800. forms: [
  801. {
  802. text: AutoOpenOrClose.text,
  803. type: "forms",
  804. forms: [
  805. UISwitch(
  806. "【屏蔽】搜索框",
  807. "shieldSearch",
  808. false,
  809. void 0,
  810. "屏蔽元素"
  811. ),
  812. UISwitch(
  813. "【屏蔽】搜索框的提示",
  814. "shieldSearchPlaceholder",
  815. false,
  816. void 0,
  817. "屏蔽元素"
  818. ),
  819. UISwitch(
  820. "【屏蔽】猜你想搜",
  821. "shieldSearchGuessYouWantToSearch",
  822. false,
  823. void 0,
  824. "屏蔽元素"
  825. ),
  826. UISwitch(
  827. "【屏蔽】抖音热点",
  828. "shieldSearchTiktokHotspot",
  829. false,
  830. void 0,
  831. "屏蔽元素"
  832. )
  833. ]
  834. }
  835. ]
  836. },
  837. {
  838. type: "deepMenu",
  839. text: "布局屏蔽-鼠标悬浮提示",
  840. afterEnterDeepMenuCallBack: AutoOpenOrClose.afterEnterDeepMenuCallBack,
  841. forms: [
  842. {
  843. type: "forms",
  844. text: AutoOpenOrClose.text + "<br>视频区域-右侧工具栏",
  845. forms: [
  846. UISwitch(
  847. "进入作者主页",
  848. "dy-video-mouseHoverTip-rightToolBar-enterUserHome",
  849. false
  850. ),
  851. UISwitch(
  852. "关注",
  853. "dy-video-mouseHoverTip-rightToolBar-follow",
  854. false
  855. ),
  856. UISwitch(
  857. "点赞",
  858. "dy-video-mouseHoverTip-rightToolBar-addLike",
  859. false
  860. ),
  861. UISwitch(
  862. "评论",
  863. "dy-video-mouseHoverTip-rightToolBar-comment",
  864. false
  865. ),
  866. UISwitch(
  867. "收藏",
  868. "dy-video-mouseHoverTip-rightToolBar-collect",
  869. false
  870. ),
  871. UISwitch(
  872. "分享",
  873. "dy-video-mouseHoverTip-rightToolBar-share",
  874. false
  875. ),
  876. UISwitch(
  877. "看相关",
  878. "dy-video-mouseHoverTip-rightToolBar-seeCorrelation",
  879. false
  880. )
  881. ]
  882. },
  883. {
  884. type: "forms",
  885. text: "视频区域-底部工具栏",
  886. forms: [
  887. UISwitch(
  888. "自动连播",
  889. "dy-video-mouseHoverTip-bottomToolBar-automaticBroadcast",
  890. false
  891. ),
  892. UISwitch(
  893. "清屏",
  894. "dy-video-mouseHoverTip-bottomToolBar-clearScreen",
  895. false
  896. ),
  897. UISwitch(
  898. "稍后再看",
  899. "dy-video-mouseHoverTip-bottomToolBar-watchLater",
  900. false
  901. ),
  902. UISwitch(
  903. "网页全屏",
  904. "dy-video-mouseHoverTip-bottomToolBar-pageFullScreen",
  905. false
  906. ),
  907. UISwitch(
  908. "全屏",
  909. "dy-video-mouseHoverTip-bottomToolBar-fullScreen",
  910. false
  911. )
  912. ]
  913. }
  914. ]
  915. }
  916. ]
  917. }
  918. ]
  919. };
  920. const DouYinDanmuFilter = {
  921. key: "douyin-live-danmu-rule",
  922. $data: {
  923. rule: []
  924. },
  925. init() {
  926. this.resetRule();
  927. this.initRule();
  928. },
  929. /**
  930. * 初始化解析规则
  931. */
  932. initRule() {
  933. let localRule = this.get().trim();
  934. let localRuleSplit = localRule.split("\n");
  935. localRuleSplit.forEach((item) => {
  936. if (item.trim() == "") return;
  937. item = item.trim();
  938. let itemRegExp = new RegExp(item.trim());
  939. this.$data.rule.push(itemRegExp);
  940. });
  941. },
  942. /**
  943. * 重置规则数据
  944. */
  945. resetRule() {
  946. this.$data.rule = [];
  947. },
  948. /**
  949. * 通知弹幕改变(可能是新增)
  950. */
  951. change() {
  952. var _a2, _b, _c, _d, _e, _f, _g, _h;
  953. let danmakuQueue = Array.from(
  954. $$(
  955. "xg-danmu.xgplayer-danmu > div > div:not([data-is-filter])"
  956. )
  957. );
  958. for (let messageIndex = 0; messageIndex < danmakuQueue.length; messageIndex++) {
  959. let $danmuItem = danmakuQueue[messageIndex];
  960. let $messageIns = (_d = (_c = (_b = (_a2 = utils.getReactObj($danmuItem)) == null ? void 0 : _a2.reactFiber) == null ? void 0 : _b.return) == null ? void 0 : _c.memoizedProps) == null ? void 0 : _d.message;
  961. let message = ((_e = $messageIns == null ? void 0 : $messageIns.payload) == null ? void 0 : _e.content) || ((_g = (_f = $messageIns == null ? void 0 : $messageIns.payload) == null ? void 0 : _f.common) == null ? void 0 : _g.describe);
  962. let method = $messageIns.method;
  963. let chat_by = (_h = $messageIns == null ? void 0 : $messageIns.payload) == null ? void 0 : _h.chat_by;
  964. let flag = false;
  965. if (!flag) {
  966. if (method === "WebcastGiftMessage" && PopsPanel.getValue("live-danmu-shield-gift")) {
  967. flag = true;
  968. } else if (method === "WebcastChatMessage") {
  969. if (chat_by === "0") ;
  970. else if (chat_by === "9" && PopsPanel.getValue("live-danmu-shield-lucky-bag")) {
  971. flag = true;
  972. } else ;
  973. } else ;
  974. }
  975. if (!flag) {
  976. flag = flag && Boolean(
  977. this.$data.rule.find((ruleItem) => {
  978. if (typeof message === "string") {
  979. if (message.match(ruleItem)) {
  980. log.info("过滤弹幕: " + message);
  981. return true;
  982. }
  983. }
  984. })
  985. );
  986. }
  987. if (flag) {
  988. $danmuItem.setAttribute("data-is-filter", "true");
  989. domUtils.hide($danmuItem);
  990. }
  991. }
  992. },
  993. set(value) {
  994. _GM_setValue(this.key, value);
  995. },
  996. get() {
  997. return _GM_getValue(this.key, "");
  998. }
  999. };
  1000. const DouYinLiveDanmuku = {
  1001. /**
  1002. * 弹幕过滤
  1003. */
  1004. filterDanmu() {
  1005. utils.waitNode("xg-danmu.xgplayer-danmu", 1e5).then(($danmu) => {
  1006. if (!$danmu) {
  1007. log.error("xg-danmu.xgplayer-danmu获取失败");
  1008. return;
  1009. }
  1010. log.success("弹幕过滤");
  1011. DouYinDanmuFilter.init();
  1012. utils.mutationObserver($danmu, {
  1013. config: {
  1014. childList: true,
  1015. subtree: true
  1016. },
  1017. immediate: true,
  1018. callback: () => {
  1019. DouYinDanmuFilter.change();
  1020. }
  1021. });
  1022. });
  1023. }
  1024. };
  1025. const ReactUtils = {
  1026. /**
  1027. * 等待react某个属性并进行设置
  1028. */
  1029. async waitReactPropsToSet($target, propName, needSetList) {
  1030. function getTarget() {
  1031. let __target__ = null;
  1032. if (typeof $target === "string") {
  1033. __target__ = document.querySelector($target);
  1034. } else if (typeof $target === "function") {
  1035. __target__ = $target();
  1036. } else if ($target instanceof HTMLElement) {
  1037. __target__ = $target;
  1038. }
  1039. return __target__;
  1040. }
  1041. if (typeof $target === "string") {
  1042. let $ele = await utils.waitNode($target, 1e4);
  1043. if (!$ele) {
  1044. return;
  1045. }
  1046. }
  1047. if (!Array.isArray(needSetList)) {
  1048. needSetList = [needSetList];
  1049. }
  1050. needSetList.forEach((needSetOption) => {
  1051. if (typeof needSetOption.msg === "string") {
  1052. log.info(needSetOption.msg);
  1053. }
  1054. function checkReactInstance() {
  1055. let target = getTarget();
  1056. if (target == null) {
  1057. return false;
  1058. }
  1059. let targetInstance = utils.getReactObj(target);
  1060. if (targetInstance == null) {
  1061. return false;
  1062. }
  1063. let targetInstanceProp = targetInstance[propName];
  1064. if (targetInstanceProp == null) {
  1065. return false;
  1066. }
  1067. let needOwnCheck = needSetOption.check(targetInstanceProp);
  1068. return Boolean(needOwnCheck);
  1069. }
  1070. utils.waitPropertyByInterval(
  1071. () => {
  1072. return getTarget();
  1073. },
  1074. checkReactInstance,
  1075. 250,
  1076. 1e4
  1077. ).then(() => {
  1078. let target = getTarget();
  1079. if (target == null) {
  1080. return;
  1081. }
  1082. let targetInstance = utils.getReactObj(target);
  1083. if (targetInstance == null) {
  1084. return;
  1085. }
  1086. let targetInstanceProp = targetInstance[propName];
  1087. if (targetInstanceProp == null) {
  1088. return;
  1089. }
  1090. needSetOption.set(targetInstanceProp, target);
  1091. });
  1092. });
  1093. }
  1094. };
  1095. const CommonUtil = {
  1096. /**
  1097. * 添加屏蔽CSS
  1098. * @param args
  1099. * @example
  1100. * addBlockCSS("")
  1101. * addBlockCSS("","")
  1102. * addBlockCSS(["",""])
  1103. */
  1104. addBlockCSS(...args) {
  1105. let selectorList = [];
  1106. if (args.length === 0) {
  1107. return;
  1108. }
  1109. if (args.length === 1 && typeof args[0] === "string" && args[0].trim() === "") {
  1110. return;
  1111. }
  1112. args.forEach((selector) => {
  1113. if (Array.isArray(selector)) {
  1114. selectorList = selectorList.concat(selector);
  1115. } else {
  1116. selectorList.push(selector);
  1117. }
  1118. });
  1119. return addStyle(`${selectorList.join(",\n")}{display: none !important;}`);
  1120. },
  1121. /**
  1122. * 设置GM_getResourceText的style内容
  1123. * @param resourceMapData 资源数据
  1124. * @example
  1125. * setGMResourceCSS({
  1126. * keyName: "ViewerCSS",
  1127. * url: "https://example.com/example.css",
  1128. * })
  1129. */
  1130. setGMResourceCSS(resourceMapData) {
  1131. let cssText = typeof _GM_getResourceText === "function" ? _GM_getResourceText(resourceMapData.keyName) : "";
  1132. if (typeof cssText === "string" && cssText) {
  1133. addStyle(cssText);
  1134. } else {
  1135. CommonUtil.loadStyleLink(resourceMapData.url);
  1136. }
  1137. },
  1138. /**
  1139. * 添加<link>标签
  1140. * @param url
  1141. * @example
  1142. * loadStyleLink("https://example.com/example.css")
  1143. */
  1144. async loadStyleLink(url) {
  1145. let $link = document.createElement("link");
  1146. $link.rel = "stylesheet";
  1147. $link.type = "text/css";
  1148. $link.href = url;
  1149. domUtils.ready(() => {
  1150. document.head.appendChild($link);
  1151. });
  1152. },
  1153. /**
  1154. * 添加<script>标签
  1155. * @param url
  1156. * @example
  1157. * loadStyleLink("https://example.com/example.js")
  1158. */
  1159. async loadScript(url) {
  1160. let $script = document.createElement("script");
  1161. $script.src = url;
  1162. return new Promise((resolve) => {
  1163. $script.onload = () => {
  1164. resolve(null);
  1165. };
  1166. (document.head || document.documentElement).appendChild($script);
  1167. });
  1168. },
  1169. /**
  1170. * 将url修复,例如只有search的链接修复为完整的链接
  1171. *
  1172. * 注意:不包括http转https
  1173. * @param url 需要修复的链接
  1174. * @example
  1175. * 修复前:`/xxx/xxx?ss=ssss`
  1176. * 修复后:`https://xxx.xxx.xxx/xxx/xxx?ss=ssss`
  1177. * @example
  1178. * 修复前:`//xxx/xxx?ss=ssss`
  1179. * 修复后:`https://xxx.xxx.xxx/xxx/xxx?ss=ssss`
  1180. * @example
  1181. * 修复前:`https://xxx.xxx.xxx/xxx/xxx?ss=ssss`
  1182. * 修复后:`https://xxx.xxx.xxx/xxx/xxx?ss=ssss`
  1183. * @example
  1184. * 修复前:`xxx/xxx?ss=ssss`
  1185. * 修复后:`https://xxx.xxx.xxx/xxx/xxx?ss=ssss`
  1186. */
  1187. fixUrl(url) {
  1188. url = url.trim();
  1189. if (url.match(/^http(s|):\/\//i)) {
  1190. return url;
  1191. } else {
  1192. if (!url.startsWith("/")) {
  1193. url += "/";
  1194. }
  1195. url = window.location.origin + url;
  1196. return url;
  1197. }
  1198. },
  1199. /**
  1200. * http转https
  1201. * @param url 需要修复的链接
  1202. * @example
  1203. * 修复前:
  1204. * 修复后:
  1205. * @example
  1206. * 修复前:
  1207. * 修复后:
  1208. */
  1209. fixHttps(url) {
  1210. if (url.startsWith("https://")) {
  1211. return url;
  1212. }
  1213. if (!url.startsWith("http://")) {
  1214. return url;
  1215. }
  1216. let urlInstance = new URL(url);
  1217. urlInstance.protocol = "https:";
  1218. return urlInstance.toString();
  1219. }
  1220. };
  1221. const DouYinLiveBlock = {
  1222. init() {
  1223. PopsPanel.execMenuOnce("live-shieldGiftColumn", () => {
  1224. return this.shieldGiftColumn();
  1225. });
  1226. PopsPanel.execMenuOnce("live-shieldTopToolBarInfo", () => {
  1227. return this.shieldTopToolBarInfo();
  1228. });
  1229. PopsPanel.execMenuOnce("live-shieldGiftEffects", () => {
  1230. return this.shieldGiftEffects();
  1231. });
  1232. PopsPanel.execMenuOnce("live-shieldLucky", () => {
  1233. return this.shieldLucky();
  1234. });
  1235. PopsPanel.execMenuOnce("live-shielYellowCar", () => {
  1236. return this.shieldYellowCar();
  1237. });
  1238. PopsPanel.execMenuOnce("live-shieldDanmuku", () => {
  1239. return this.shieldDanmu();
  1240. });
  1241. DouYinLiveChatRoomBlock.init();
  1242. DouYinLiveVideoAreaRightMenu.init();
  1243. },
  1244. /**
  1245. * 屏蔽弹幕
  1246. */
  1247. shieldDanmu() {
  1248. log.info("屏蔽弹幕");
  1249. return [CommonUtil.addBlockCSS("xg-danmu.xgplayer-danmu")];
  1250. },
  1251. /**
  1252. * 【屏蔽】顶栏信息
  1253. * 包括直播作者、右侧的礼物展馆
  1254. */
  1255. shieldTopToolBarInfo() {
  1256. log.info("【屏蔽】顶栏信息");
  1257. return [
  1258. CommonUtil.addBlockCSS(
  1259. 'div[data-e2e="living-container"] div[id*="living_room_player_container"] > pace-island[id^="island_"]',
  1260. // 2024.12.26
  1261. 'div[data-e2e="living-container"] div[id*="living_room_player_container"] >div>div>pace-island[id^="island_"]:has(.__isFullPlayer)',
  1262. // 全屏状态下的
  1263. 'div[data-e2e="living-container"] xg-bar.xg-top-bar'
  1264. )
  1265. ];
  1266. },
  1267. /**
  1268. * 【屏蔽】礼物特效
  1269. */
  1270. shieldGiftEffects() {
  1271. log.info("【屏蔽】礼物特效");
  1272. let result = [
  1273. CommonUtil.addBlockCSS(
  1274. // ↓该屏蔽会把连麦的用户也屏蔽了
  1275. // '.basicPlayer[data-e2e="basicPlayer"] pace-island[id^="island_"]:has(>div>div>div)'
  1276. // 排除掉福袋
  1277. '.basicPlayer[data-e2e="basicPlayer"] > pace-island[id^="island_"]:not(:has(.ShortTouchContainer)):has(>div > div:not([class*="video_layout_container"]) > div)'
  1278. )
  1279. ];
  1280. domUtils.ready(() => {
  1281. utils.waitNode(() => {
  1282. return domUtils.selector(
  1283. "xg-icon.pluginContainer > div:contains('屏蔽礼物特效')"
  1284. );
  1285. }, 1e4).then(($el) => {
  1286. var _a2, _b, _c, _d;
  1287. if (!$el) {
  1288. log.error("屏蔽礼物特效按钮不存在,获取超时");
  1289. return;
  1290. }
  1291. let { reactFiber } = utils.getReactObj($el);
  1292. let onClick = (_d = (_c = (_b = (_a2 = reactFiber == null ? void 0 : reactFiber.memoizedProps) == null ? void 0 : _a2.children) == null ? void 0 : _b[1]) == null ? void 0 : _c.props) == null ? void 0 : _d.onClick;
  1293. if (typeof onClick === "function") {
  1294. log.info(`调用屏蔽礼物特效按钮的onClick函数`);
  1295. onClick();
  1296. } else {
  1297. log.error(`调用屏蔽礼物特效按钮的onClick函数失败,未获取到`);
  1298. }
  1299. });
  1300. });
  1301. return result;
  1302. },
  1303. /**
  1304. * 【屏蔽】福袋
  1305. */
  1306. shieldLucky() {
  1307. log.info("【屏蔽】福袋");
  1308. return [
  1309. CommonUtil.addBlockCSS(
  1310. '.basicPlayer[data-e2e="basicPlayer"] > pace-island[id^="island_"]:has(.ShortTouchContainer):has(>div > div:not([class*="video_layout_container"]) > div)'
  1311. )
  1312. ];
  1313. },
  1314. /**
  1315. * 【屏蔽】小黄车
  1316. */
  1317. shieldYellowCar() {
  1318. log.info("【屏蔽】小黄车");
  1319. return [
  1320. CommonUtil.addBlockCSS(
  1321. 'div[id^="living_room_player_container"] .basicPlayer > div:has(div[data-e2e="yellowCart-container"])'
  1322. )
  1323. ];
  1324. },
  1325. /**
  1326. * 【屏蔽】底部的礼物栏
  1327. */
  1328. shieldGiftColumn() {
  1329. log.info("【屏蔽】底部的礼物栏");
  1330. return [
  1331. CommonUtil.addBlockCSS(
  1332. // 2025.2.18
  1333. 'div[data-e2e="living-container"] [id^="living_room_player_container"] > :last-child:has(>.gitBarOptimizeEnabled )',
  1334. // Firefox上的CSS,多了个pace-island
  1335. 'div[data-e2e="living-container"] >div> div:has(>pace-island >.gitBarOptimizeEnabled)',
  1336. // 全屏状态下的
  1337. 'div[data-e2e="living-container"] xg-controls > div:has(div[data-e2e="gifts-container"]):not(:has(video))'
  1338. ),
  1339. addStyle(
  1340. /*css*/
  1341. `
  1342. /* 去除全屏状态下的礼物栏后,上面的工具栏bottom也去除 */
  1343. div[data-e2e="living-container"] xg-controls xg-inner-controls:has(+div div[data-e2e="gifts-container"]){
  1344. bottom: 0 !important;
  1345. }`
  1346. )
  1347. ];
  1348. }
  1349. };
  1350. const DouYinLiveChatRoomBlock = {
  1351. init() {
  1352. PopsPanel.execMenuOnce("live-shieldChatRoom", () => {
  1353. return this.shieldChatRoom();
  1354. });
  1355. PopsPanel.execMenuOnce("live-shielChatRoomVipSeats", () => {
  1356. return this.shielChatRoomVipSeats();
  1357. });
  1358. PopsPanel.execMenuOnce("dy-live-shieldUserLevelIcon", () => {
  1359. return this.shieldUserLevelIcon();
  1360. });
  1361. PopsPanel.execMenuOnce("dy-live-shieldUserVIPIcon", () => {
  1362. return this.shieldUserVIPIcon();
  1363. });
  1364. PopsPanel.execMenuOnce("dy-live-shieldUserFansIcon", () => {
  1365. return this.shieldUserFansIcon();
  1366. });
  1367. PopsPanel.execMenuOnce("dy-live-shieldMessage", () => {
  1368. return this.shieldMessage();
  1369. });
  1370. },
  1371. /**
  1372. * 【屏蔽】评论区(聊天室)
  1373. */
  1374. shieldChatRoom() {
  1375. log.info("【屏蔽】评论区(聊天室)");
  1376. return [
  1377. CommonUtil.addBlockCSS("#chatroom"),
  1378. addStyle(
  1379. /*css*/
  1380. `
  1381. div[data-e2e="living-container"],
  1382. div[data-e2e="living-container"] > div{
  1383. margin-bottom: 0px !important;
  1384. }`
  1385. )
  1386. ];
  1387. },
  1388. /**
  1389. * 【屏蔽】评论区的贵宾席
  1390. */
  1391. shielChatRoomVipSeats() {
  1392. log.info("【屏蔽】评论区的贵宾席");
  1393. return [
  1394. CommonUtil.addBlockCSS(
  1395. "#chatroom > div > div:has(#audiencePanelScrollId)",
  1396. // Firefox上的CSS,多了个pace-island
  1397. "#chatroom > pace-island > div > div:has(#audiencePanelScrollId)"
  1398. )
  1399. ];
  1400. },
  1401. /**
  1402. * 【屏蔽】用户等级图标
  1403. */
  1404. shieldUserLevelIcon() {
  1405. log.info("【屏蔽】用户等级图标");
  1406. return [
  1407. CommonUtil.addBlockCSS(
  1408. '#chatroom .webcast-chatroom___item span:has(>img[src*="level"])'
  1409. )
  1410. ];
  1411. },
  1412. /**
  1413. * 【屏蔽】VIP图标
  1414. */
  1415. shieldUserVIPIcon() {
  1416. log.info("【屏蔽】VIP图标");
  1417. return [
  1418. CommonUtil.addBlockCSS(
  1419. '#chatroom .webcast-chatroom___item span:has(>img[src*="subscribe"])'
  1420. )
  1421. ];
  1422. },
  1423. /**
  1424. * 【屏蔽】粉丝牌
  1425. */
  1426. shieldUserFansIcon() {
  1427. log.info("【屏蔽】粉丝牌");
  1428. return [
  1429. CommonUtil.addBlockCSS(
  1430. '#chatroom .webcast-chatroom___item span:has(>div[style*="fansclub"])',
  1431. '#chatroom .webcast-chatroom___item span:has(>img[src*="fansclub"])'
  1432. )
  1433. ];
  1434. },
  1435. /**
  1436. * 【屏蔽】信息播报
  1437. */
  1438. shieldMessage() {
  1439. log.info("【屏蔽】信息播报");
  1440. return [
  1441. CommonUtil.addBlockCSS(
  1442. "#chatroom .webcast-chatroom___bottom-message",
  1443. // 上面的滚动播报,xxx加入了直播间
  1444. "#chatroom >div:nth-child(2)>div>div:nth-child(3)",
  1445. // Firefox的,多了个pace-island
  1446. "#chatroom >pace-island>div>div:first-child>div:nth-child(3)"
  1447. )
  1448. ];
  1449. }
  1450. };
  1451. const DouYinLiveVideoAreaRightMenu = {
  1452. init() {
  1453. PopsPanel.execMenuOnce("dy-live-blockVideoRightMenu-downloadClient", () => {
  1454. return this.blockDownloadClient();
  1455. });
  1456. },
  1457. /**
  1458. * 【屏蔽】右键菜单-下载客户端
  1459. */
  1460. blockDownloadClient() {
  1461. log.info(`【屏蔽】右键菜单-下载客户端`);
  1462. return [
  1463. CommonUtil.addBlockCSS(
  1464. '.__menu_container_className:has(>a[href*="douyin-pc-web"])'
  1465. )
  1466. ];
  1467. }
  1468. };
  1469. const DouYinLivePlayerInstance = {
  1470. $data: {
  1471. playerInstance: null
  1472. },
  1473. $el: {
  1474. $playerIns: null
  1475. },
  1476. /**
  1477. * 添加油猴菜单
  1478. */
  1479. initMenu() {
  1480. GM_Menu.add({
  1481. key: "live-parsePlayerInstance",
  1482. text: "⚙ PlayerInstance",
  1483. autoReload: false,
  1484. showText(text, enable) {
  1485. return text;
  1486. },
  1487. callback: () => {
  1488. let $playerIns = $(
  1489. `[id^="living_room_player_container"]`
  1490. );
  1491. if (!$playerIns) {
  1492. log.error("获取playerInstance所在的元素失败");
  1493. Qmsg.error("获取playerInstance所在的元素失败");
  1494. return;
  1495. }
  1496. this.$el.$playerIns = $playerIns;
  1497. let playerInstance = this.parseElementPlayerIns(this.$el.$playerIns);
  1498. if (playerInstance == null) {
  1499. log.error("获取playerInstance失败");
  1500. log.error("获取playerInstance失败");
  1501. return;
  1502. }
  1503. this.$data.playerInstance = playerInstance;
  1504. this.showParseDialog();
  1505. }
  1506. });
  1507. },
  1508. /**
  1509. * 解析元素上的播放器实例
  1510. */
  1511. parseElementPlayerIns($ele) {
  1512. var _a2, _b, _c, _d;
  1513. let react = utils.getReactObj($ele);
  1514. return (_d = (_c = (_b = (_a2 = react == null ? void 0 : react.reactFiber) == null ? void 0 : _a2.child) == null ? void 0 : _b.child) == null ? void 0 : _c.memoizedProps) == null ? void 0 : _d.playerInstance;
  1515. },
  1516. /**
  1517. * 显示解析的信息弹窗
  1518. */
  1519. showParseDialog() {
  1520. var _a2, _b, _c, _d;
  1521. log.info(["解析的信息:", this.$data.playerInstance]);
  1522. let blobSrc = ((_a2 = this.$data.playerInstance) == null ? void 0 : _a2.url) || ((_b = this.$data.playerInstance) == null ? void 0 : _b.src);
  1523. let pushSrc = (_c = this.$data.playerInstance) == null ? void 0 : _c.config.url;
  1524. __pops.alert({
  1525. title: {
  1526. text: "解析信息",
  1527. position: "center"
  1528. },
  1529. content: {
  1530. text: (
  1531. /*html*/
  1532. `
  1533. <div class="live-dy-parse-container">
  1534. <div class="live-dy-parse-item">
  1535. <div class="live-dy-parse-item-name">推流地址:</div>
  1536. <a class="live-dy-parse-item-value" href="${pushSrc}" target="_blank">${pushSrc}
  1537. </a>
  1538. </div>
  1539. <div class="live-dy-parse-item">
  1540. <div class="live-dy-parse-item-name">blob地址:</div>
  1541. <a class="live-dy-parse-item-value" href="${blobSrc}" target="_blank">${blobSrc}
  1542. </a>
  1543. </div>
  1544. <div class="live-dy-parse-item">
  1545. <div class="live-dy-parse-item-name">播放器版本:</div>
  1546. <div class="live-dy-parse-item-value">${(_d = this.$data.playerInstance) == null ? void 0 : _d.version}
  1547. </div>
  1548. </div>
  1549. </div>
  1550. `
  1551. ),
  1552. html: true
  1553. },
  1554. mask: {
  1555. enable: false
  1556. },
  1557. width: window.innerWidth > 550 ? "550px" : "88wv",
  1558. height: window.innerHeight > 550 ? "550px" : "70vh",
  1559. style: (
  1560. /*css*/
  1561. `
  1562. .live-dy-parse-container{
  1563. display: flex;
  1564. flex-direction: column;
  1565. gap: 10px;
  1566. }
  1567. .live-dy-parse-item{
  1568. display: flex;
  1569. flex-wrap: wrap;
  1570. border: 1px solid #919191;
  1571. border-left: 0px;
  1572. border-right: 0px;
  1573. width: 100%;
  1574. background: #0af9ee;
  1575. padding: 5px 5px;
  1576. }
  1577. `
  1578. )
  1579. });
  1580. }
  1581. };
  1582. class ShortCut {
  1583. constructor(key) {
  1584. /** 存储的键 */
  1585. __publicField(this, "key", "short-cut");
  1586. /** 配置 */
  1587. __publicField(this, "$data");
  1588. /** 是否存在等待按下的按键 */
  1589. __publicField(this, "isWaitPress", false);
  1590. /**
  1591. * 当前等待按下的按键实例
  1592. */
  1593. __publicField(this, "currentWaitEnterPressInstanceHandler", null);
  1594. if (typeof key === "string") {
  1595. this.key = key;
  1596. }
  1597. this.$data = {
  1598. /**
  1599. * 其它实例的快捷键的配置
  1600. *
  1601. * 这里一般是用于在录入快捷键时判断是否存在重复的快捷键
  1602. */
  1603. otherShortCutOptions: []
  1604. };
  1605. }
  1606. /**
  1607. * 初始化配置默认值
  1608. */
  1609. initConfig(key, option) {
  1610. if (this.hasOption(key)) ;
  1611. else {
  1612. this.setOption(key, option);
  1613. }
  1614. }
  1615. /** 获取存储的键 */
  1616. getStorageKey() {
  1617. return this.key;
  1618. }
  1619. /**
  1620. * 获取本地存储的所有值
  1621. */
  1622. getLocalAllOptions() {
  1623. return _GM_getValue(this.key, []);
  1624. }
  1625. /**
  1626. * 判断是否存在该配置
  1627. * @param key 键
  1628. */
  1629. hasOption(key) {
  1630. let localOptions = this.getLocalAllOptions();
  1631. let findOption = localOptions.find((item) => item.key === key);
  1632. return !!findOption;
  1633. }
  1634. /**
  1635. * 判断是否存在该配置的value值
  1636. * @param key 键
  1637. */
  1638. hasOptionValue(key) {
  1639. if (this.hasOption(key)) {
  1640. let option = this.getOption(key);
  1641. return !((option == null ? void 0 : option.value) == null);
  1642. } else {
  1643. return false;
  1644. }
  1645. }
  1646. /**
  1647. * 获取配置
  1648. * @param key 键
  1649. * @param defaultValue 默认值
  1650. */
  1651. getOption(key, defaultValue) {
  1652. let localOptions = this.getLocalAllOptions();
  1653. let findOption = localOptions.find((item) => item.key === key);
  1654. return findOption ?? defaultValue;
  1655. }
  1656. /**
  1657. * 设置配置
  1658. * @param key 键
  1659. * @param value 配置
  1660. */
  1661. setOption(key, value) {
  1662. let localOptions = this.getLocalAllOptions();
  1663. let findIndex = localOptions.findIndex((item) => item.key === key);
  1664. if (findIndex == -1) {
  1665. localOptions.push({
  1666. key,
  1667. value
  1668. });
  1669. } else {
  1670. Reflect.set(localOptions[findIndex], "value", value);
  1671. }
  1672. _GM_setValue(this.key, localOptions);
  1673. }
  1674. /**
  1675. * 清空当前已有配置录入的值
  1676. * @param key
  1677. */
  1678. emptyOption(key) {
  1679. let result = false;
  1680. let localOptions = this.getLocalAllOptions();
  1681. let findIndex = localOptions.findIndex((item) => item.key === key);
  1682. if (findIndex !== -1) {
  1683. localOptions[findIndex].value = null;
  1684. result = true;
  1685. }
  1686. _GM_setValue(this.key, localOptions);
  1687. return result;
  1688. }
  1689. /**
  1690. * 删除配置
  1691. * @param key 键
  1692. */
  1693. deleteOption(key) {
  1694. let result = false;
  1695. let localValue = this.getLocalAllOptions();
  1696. let findValueIndex = localValue.findIndex((item) => item.key === key);
  1697. if (findValueIndex !== -1) {
  1698. localValue.splice(findValueIndex, 1);
  1699. result = true;
  1700. }
  1701. _GM_setValue(this.key, localValue);
  1702. return result;
  1703. }
  1704. /**
  1705. * 把配置的快捷键转成文字
  1706. * @param keyboardValue
  1707. */
  1708. translateKeyboardValueToButtonText(keyboardValue) {
  1709. let result = "";
  1710. keyboardValue.ohterCodeList.forEach((ohterCodeKey) => {
  1711. result += utils.stringTitleToUpperCase(ohterCodeKey, true) + " + ";
  1712. });
  1713. result += utils.stringTitleToUpperCase(keyboardValue.keyName);
  1714. return result;
  1715. }
  1716. /**
  1717. * 获取快捷键显示的文字
  1718. * @param key 本地存储的快捷键键名
  1719. * @param defaultShowText 默认显示的文字
  1720. */
  1721. getShowText(key, defaultShowText) {
  1722. if (this.hasOption(key)) {
  1723. let localOption = this.getOption(key);
  1724. if (localOption.value == null) {
  1725. return defaultShowText;
  1726. } else {
  1727. return this.translateKeyboardValueToButtonText(localOption.value);
  1728. }
  1729. } else {
  1730. return defaultShowText;
  1731. }
  1732. }
  1733. /**
  1734. * 录入快捷键
  1735. * @param key 本地存储的快捷键键名
  1736. */
  1737. async enterShortcutKeys(key) {
  1738. const that = this;
  1739. return new Promise((resolve) => {
  1740. this.isWaitPress = true;
  1741. let keyboardListener = domUtils.listenKeyboard(
  1742. window,
  1743. "keyup",
  1744. (keyName, keyValue, ohterCodeList) => {
  1745. const currentOption = {
  1746. keyName,
  1747. keyValue,
  1748. ohterCodeList
  1749. };
  1750. let result = {};
  1751. try {
  1752. const shortcutJSONString = JSON.stringify(currentOption);
  1753. const allOptions = this.getLocalAllOptions();
  1754. if (Array.isArray(this.$data.otherShortCutOptions)) {
  1755. allOptions.push(...this.$data.otherShortCutOptions);
  1756. }
  1757. for (let index = 0; index < allOptions.length; index++) {
  1758. let localValue = allOptions[index];
  1759. if (localValue.key === key) {
  1760. continue;
  1761. }
  1762. const localShortCutJSONString = JSON.stringify(localValue.value);
  1763. let isUsedByOtherOption = false;
  1764. if (localValue.value != null && shortcutJSONString === localShortCutJSONString) {
  1765. isUsedByOtherOption = true;
  1766. }
  1767. if (isUsedByOtherOption) {
  1768. result = {
  1769. status: false,
  1770. key: localValue.key,
  1771. option: currentOption
  1772. };
  1773. return;
  1774. }
  1775. }
  1776. this.setOption(key, currentOption);
  1777. result = {
  1778. status: true,
  1779. key,
  1780. option: currentOption
  1781. };
  1782. } catch (error) {
  1783. console.log(error);
  1784. result = {
  1785. status: false,
  1786. key,
  1787. option: currentOption
  1788. };
  1789. } finally {
  1790. that.isWaitPress = false;
  1791. keyboardListener.removeListen();
  1792. that.currentWaitEnterPressInstanceHandler = null;
  1793. resolve(result);
  1794. }
  1795. }
  1796. );
  1797. that.currentWaitEnterPressInstanceHandler = null;
  1798. that.currentWaitEnterPressInstanceHandler = () => {
  1799. that.isWaitPress = false;
  1800. keyboardListener.removeListen();
  1801. };
  1802. });
  1803. }
  1804. /**
  1805. * 取消当前的录入快捷键操作
  1806. */
  1807. cancelEnterShortcutKeys() {
  1808. if (typeof this.currentWaitEnterPressInstanceHandler === "function") {
  1809. this.currentWaitEnterPressInstanceHandler();
  1810. }
  1811. }
  1812. /**
  1813. * 初始化全局键盘监听
  1814. * @param shortCutOption 快捷键配置 一般是{ "键名": { callback: ()=>{}}},键名是本地存储的自定义快捷键的键名
  1815. * @param config 配置
  1816. */
  1817. initGlobalKeyboardListener(shortCutOption, config) {
  1818. let localOptions = this.getLocalAllOptions();
  1819. if (!localOptions.length) {
  1820. log.warn("没有设置快捷键");
  1821. return;
  1822. }
  1823. const that = this;
  1824. function setListenKeyboard($ele, option) {
  1825. domUtils.listenKeyboard(
  1826. $ele,
  1827. "keydown",
  1828. (keyName, keyValue, ohterCodeList, event) => {
  1829. if (that.isWaitPress) {
  1830. return;
  1831. }
  1832. if (config == null ? void 0 : config.isPrevent) {
  1833. utils.preventEvent(event);
  1834. }
  1835. localOptions = that.getLocalAllOptions();
  1836. let findShortcutIndex = localOptions.findIndex((item) => {
  1837. let option2 = item.value;
  1838. let tempOption = {
  1839. keyName,
  1840. keyValue,
  1841. ohterCodeList
  1842. };
  1843. if (JSON.stringify(option2) === JSON.stringify(tempOption)) {
  1844. return item;
  1845. }
  1846. });
  1847. if (findShortcutIndex != -1) {
  1848. let findShortcut = localOptions[findShortcutIndex];
  1849. if (findShortcut.key in option) {
  1850. log.info(["调用快捷键", findShortcut]);
  1851. option[findShortcut.key].callback();
  1852. }
  1853. }
  1854. },
  1855. {
  1856. capture: Boolean(config == null ? void 0 : config.capture)
  1857. }
  1858. );
  1859. }
  1860. let WindowShortCutOption = {};
  1861. let ElementShortCutOption = {};
  1862. Object.keys(shortCutOption).forEach((localKey) => {
  1863. let option = shortCutOption[localKey];
  1864. if (option.target == null || typeof option.target === "string" && option.target === "") {
  1865. option.target = "window";
  1866. }
  1867. if (option.target === "window") {
  1868. Reflect.set(WindowShortCutOption, localKey, option);
  1869. } else {
  1870. Reflect.set(ElementShortCutOption, localKey, option);
  1871. }
  1872. });
  1873. setListenKeyboard(window, WindowShortCutOption);
  1874. domUtils.ready(() => {
  1875. Object.keys(ElementShortCutOption).forEach(async (localKey) => {
  1876. let option = ElementShortCutOption[localKey];
  1877. if (typeof option.target === "string") {
  1878. utils.waitNode(option.target, 1e4).then(($ele) => {
  1879. if (!$ele) {
  1880. return;
  1881. }
  1882. let __option = {};
  1883. Reflect.set(__option, localKey, option);
  1884. setListenKeyboard($ele, __option);
  1885. });
  1886. } else if (typeof option.target === "function") {
  1887. let target = await option.target();
  1888. if (target == null) {
  1889. return;
  1890. }
  1891. let __option = {};
  1892. Reflect.set(__option, localKey, option);
  1893. setListenKeyboard(target, __option);
  1894. } else {
  1895. let __option = {};
  1896. Reflect.set(__option, localKey, option);
  1897. setListenKeyboard(option.target, __option);
  1898. }
  1899. });
  1900. });
  1901. }
  1902. }
  1903. const DouYinLiveShortCut = {
  1904. shortCut: new ShortCut("live-short-cut"),
  1905. $data: {
  1906. blockChatRoom: false
  1907. },
  1908. init() {
  1909. this.shortCut.initGlobalKeyboardListener(this.getShortCutMap());
  1910. },
  1911. getShortCutMap() {
  1912. return {
  1913. "dy-live-block-chatroom": {
  1914. target: "window",
  1915. callback() {
  1916. log.info("快捷键 ==> 【屏蔽】聊天室");
  1917. let flag = PopsPanel.getValue("live-shieldChatRoom");
  1918. PopsPanel.setValue("live-shieldChatRoom", !flag);
  1919. }
  1920. },
  1921. "dy-live-shieldGiftEffects": {
  1922. target: "window",
  1923. callback: () => {
  1924. log.info("快捷键 ==> 【屏蔽】礼物特效");
  1925. let flag = PopsPanel.getValue("live-shieldGiftEffects");
  1926. PopsPanel.setValue("live-shieldGiftEffects", !flag);
  1927. }
  1928. },
  1929. "dy-live-shortcut-changeVideoMuted": {
  1930. target: "window",
  1931. callback() {
  1932. log.info(`触发快捷键 ==> 切换静音状态`);
  1933. $$("video").forEach(($video) => {
  1934. let muted = !$video.muted;
  1935. log.success(`切换video标签的静音状态为 ${muted}`);
  1936. $video.muted = muted;
  1937. });
  1938. }
  1939. }
  1940. };
  1941. }
  1942. };
  1943. const VideoQualityMap = {
  1944. auto: {
  1945. label: "自动",
  1946. sign: 0
  1947. },
  1948. origin: {
  1949. label: "潮汐海灵",
  1950. sign: 5
  1951. },
  1952. uhd: {
  1953. label: "蓝光",
  1954. sign: 4
  1955. },
  1956. hd: {
  1957. label: "超清",
  1958. sign: 3
  1959. },
  1960. sd: {
  1961. label: "高清",
  1962. sign: 2
  1963. },
  1964. ld: {
  1965. label: "标清",
  1966. sign: 1
  1967. }
  1968. };
  1969. const DouYinLive = {
  1970. init() {
  1971. DouYinLiveBlock.init();
  1972. DouYinLiveShortCut.init();
  1973. PopsPanel.execMenu("live-danmu-shield-rule-enable", () => {
  1974. DouYinLiveDanmuku.filterDanmu();
  1975. });
  1976. PopsPanel.execMenu("live-unlockImageQuality", () => {
  1977. this.unlockImageQuality();
  1978. });
  1979. PopsPanel.execMenuOnce("live-waitToRemovePauseDialog", () => {
  1980. this.waitToRemovePauseDialog();
  1981. });
  1982. PopsPanel.execMenu("live-pauseVideo", () => {
  1983. this.pauseVideo();
  1984. });
  1985. PopsPanel.execMenu("live-bgColor-enable", () => {
  1986. PopsPanel.execMenuOnce("live-changeBackgroundColor", (value) => {
  1987. return this.changeBackgroundColor(value);
  1988. });
  1989. });
  1990. PopsPanel.execMenuOnce("live-parsePlayerInstance", () => {
  1991. DouYinLivePlayerInstance.initMenu();
  1992. });
  1993. domUtils.ready(() => {
  1994. PopsPanel.execMenu("live-chooseQuality", (quality) => {
  1995. if (quality === "auto") {
  1996. return;
  1997. }
  1998. this.chooseQuality(quality);
  1999. });
  2000. PopsPanel.execMenu("live-autoEnterElementFullScreen", () => {
  2001. this.autoEnterElementFullScreen();
  2002. });
  2003. });
  2004. },
  2005. /**
  2006. * 自动进入网页全屏
  2007. */
  2008. autoEnterElementFullScreen() {
  2009. ReactUtils.waitReactPropsToSet(
  2010. "xg-icon.xgplayer-fullscreen + xg-icon div:has(>svg)",
  2011. "reactFiber",
  2012. {
  2013. check(reactInstance) {
  2014. var _a2;
  2015. return typeof ((_a2 = reactInstance == null ? void 0 : reactInstance.memoizedProps) == null ? void 0 : _a2.onClick) === "function";
  2016. },
  2017. set(reactInstance, $target) {
  2018. log.success("自动进入网页全屏");
  2019. reactInstance.memoizedProps.onClick();
  2020. }
  2021. }
  2022. );
  2023. },
  2024. /**
  2025. * 选择画质
  2026. * @param quality 选择的画质
  2027. */
  2028. chooseQuality(quality = "origin") {
  2029. ReactUtils.waitReactPropsToSet(
  2030. 'xg-inner-controls xg-right-grid >div:has([data-e2e="quality-selector"])',
  2031. "reactProps",
  2032. {
  2033. check(reactInstance) {
  2034. var _a2, _b, _c, _d, _e, _f, _g, _h, _i;
  2035. return typeof ((_d = (_c = (_b = (_a2 = reactInstance == null ? void 0 : reactInstance.children) == null ? void 0 : _a2.props) == null ? void 0 : _b.children) == null ? void 0 : _c.props) == null ? void 0 : _d.qualityHandler) === "object" && typeof ((_i = (_h = (_g = (_f = (_e = reactInstance == null ? void 0 : reactInstance.children) == null ? void 0 : _e.props) == null ? void 0 : _f.children) == null ? void 0 : _g.props) == null ? void 0 : _h.qualityHandler) == null ? void 0 : _i.getCurrentQualityList) === "function";
  2036. },
  2037. set(reactInstance) {
  2038. let qualityHandler = reactInstance.children.props.children.props.qualityHandler;
  2039. let currentQualityList = qualityHandler.getCurrentQualityList();
  2040. if (!currentQualityList.includes(quality)) {
  2041. Qmsg.warning(
  2042. "当前直播没有【" + quality + "】画质,自动选择最高画质"
  2043. );
  2044. currentQualityList.sort((a, b) => {
  2045. if (!VideoQualityMap[a]) {
  2046. log.error("画质【" + a + "】不存在");
  2047. return 0;
  2048. }
  2049. if (!VideoQualityMap[b]) {
  2050. log.error("画质【" + b + "】不存在");
  2051. return 0;
  2052. }
  2053. return VideoQualityMap[a].sign - VideoQualityMap[b].sign;
  2054. });
  2055. quality = currentQualityList[currentQualityList.length - 1];
  2056. }
  2057. qualityHandler.setCurrentQuality(quality);
  2058. log.success("成功设置画质为【" + quality + "】");
  2059. }
  2060. }
  2061. );
  2062. },
  2063. /**
  2064. * 解锁画质选择
  2065. *
  2066. * 未登录(不可用)情况下最高选择【高清】画质
  2067. */
  2068. unlockImageQuality() {
  2069. log.info("解锁画质选择");
  2070. domUtils.on(
  2071. document,
  2072. "click",
  2073. 'div[data-e2e="quality-selector"] > div',
  2074. function(event, clickNode) {
  2075. var _a2, _b;
  2076. utils.preventEvent(event);
  2077. try {
  2078. let reactInstance = utils.getReactObj(clickNode);
  2079. let key = (_a2 = reactInstance == null ? void 0 : reactInstance.reactFiber) == null ? void 0 : _a2["key"];
  2080. let parent = clickNode.closest("div[data-index]");
  2081. let parentReactInstance = utils.getReactObj(parent);
  2082. let current = (_b = parentReactInstance == null ? void 0 : parentReactInstance.reactProps) == null ? void 0 : _b["children"]["ref"]["current"];
  2083. log.info("当前选择的画质: " + key);
  2084. log.info(["所有的画质: ", current.getCurrentQualityList()]);
  2085. current.setCurrentQuality(key);
  2086. } catch (error) {
  2087. log.error(error);
  2088. Qmsg.error("切换画质失败");
  2089. }
  2090. },
  2091. {
  2092. capture: true
  2093. }
  2094. );
  2095. },
  2096. /**
  2097. * 长时间无操作,已暂停播放
  2098. * 累计节能xx分钟
  2099. */
  2100. waitToRemovePauseDialog() {
  2101. log.info("监听【长时间无操作,已暂停播放】弹窗");
  2102. let checkDialogToClose = ($ele, from) => {
  2103. var _a2, _b, _c, _d, _e, _f;
  2104. let eleText = domUtils.text($ele);
  2105. if (eleText.includes("长时间无操作") && eleText.includes("暂停播放")) {
  2106. Qmsg.info(`检测${from}:出现【长时间无操作,已暂停播放】弹窗`, {
  2107. consoleLogContent: true
  2108. });
  2109. let $rect = utils.getReactObj($ele);
  2110. if (typeof $rect.reactContainer === "object") {
  2111. let closeDialogFn = utils.queryProperty($rect.reactContainer, (obj) => {
  2112. var _a3, _b2;
  2113. if (typeof obj["onClose"] === "function") {
  2114. return {
  2115. isFind: true,
  2116. data: obj["onClose"]
  2117. };
  2118. } else if (typeof ((_a3 = obj == null ? void 0 : obj["memoizedProps"]) == null ? void 0 : _a3["onClose"]) === "function") {
  2119. return {
  2120. isFind: true,
  2121. data: (_b2 = obj == null ? void 0 : obj["memoizedProps"]) == null ? void 0 : _b2["onClose"]
  2122. };
  2123. } else {
  2124. return {
  2125. isFind: false,
  2126. data: obj["child"]
  2127. };
  2128. }
  2129. }) || ((_f = (_e = (_d = (_c = (_b = (_a2 = $rect == null ? void 0 : $rect.reactContainer) == null ? void 0 : _a2.memoizedState) == null ? void 0 : _b.element) == null ? void 0 : _c.props) == null ? void 0 : _d.children) == null ? void 0 : _e.props) == null ? void 0 : _f.onClose);
  2130. if (typeof closeDialogFn === "function") {
  2131. Qmsg.success(`检测${from}:调用函数关闭弹窗`, {
  2132. consoleLogContent: true
  2133. });
  2134. closeDialogFn();
  2135. }
  2136. }
  2137. }
  2138. };
  2139. domUtils.ready(() => {
  2140. utils.mutationObserver(document.body, {
  2141. config: {
  2142. subtree: true,
  2143. childList: true
  2144. },
  2145. callback() {
  2146. $$(
  2147. "body > div[elementtiming='element-timing']"
  2148. ).forEach(($elementTiming) => {
  2149. checkDialogToClose($elementTiming, "1");
  2150. });
  2151. $$('body > div:not([id="root"])').forEach(($ele) => {
  2152. checkDialogToClose($ele, "2");
  2153. });
  2154. }
  2155. });
  2156. });
  2157. },
  2158. /**
  2159. * 暂停视频
  2160. */
  2161. pauseVideo() {
  2162. log.info("禁止自动播放视频(直播)");
  2163. utils.waitNode('.basicPlayer[data-e2e="basicPlayer"] video').then(($video) => {
  2164. domUtils.on(
  2165. $video,
  2166. "play",
  2167. () => {
  2168. $video.pause();
  2169. },
  2170. {
  2171. capture: true,
  2172. once: true
  2173. }
  2174. );
  2175. $video.autoplay = false;
  2176. $video.pause();
  2177. });
  2178. },
  2179. /**
  2180. * 修改视频背景颜色
  2181. * @param color 颜色
  2182. */
  2183. changeBackgroundColor(color) {
  2184. log.info("修改视频背景颜色");
  2185. return addStyle(
  2186. /*css*/
  2187. `
  2188. div[id^="living_room_player_container"] > div,
  2189. #chatroom > div{
  2190. background: ${color};
  2191. }
  2192. `
  2193. );
  2194. }
  2195. };
  2196. const UIButton = function(text, description, buttonText, buttonIcon, buttonIsRightIcon, buttonIconIsLoading, buttonType, clickCallBack, afterAddToUListCallBack) {
  2197. let result = {
  2198. text,
  2199. type: "button",
  2200. description,
  2201. buttonIcon,
  2202. buttonIsRightIcon,
  2203. buttonIconIsLoading,
  2204. buttonType,
  2205. buttonText,
  2206. callback(event) {
  2207. if (typeof clickCallBack === "function") {
  2208. clickCallBack(event);
  2209. }
  2210. },
  2211. afterAddToUListCallBack
  2212. };
  2213. return result;
  2214. };
  2215. const UIButtonShortCut = function(text, description, key, defaultValue, defaultButtonText, buttonType = "default", shortCut) {
  2216. let __defaultButtonText = defaultButtonText;
  2217. let getButtonText = () => {
  2218. return shortCut.getShowText(key, __defaultButtonText);
  2219. };
  2220. let result = UIButton(
  2221. text,
  2222. description,
  2223. getButtonText,
  2224. "keyboard",
  2225. false,
  2226. false,
  2227. buttonType,
  2228. async (event) => {
  2229. var _a2;
  2230. let $click = event.target;
  2231. let $btn = (_a2 = $click.closest(".pops-panel-button")) == null ? void 0 : _a2.querySelector("span");
  2232. if (shortCut.isWaitPress) {
  2233. Qmsg.warning("请先执行当前的录入操作");
  2234. return;
  2235. }
  2236. if (shortCut.hasOptionValue(key)) {
  2237. shortCut.emptyOption(key);
  2238. Qmsg.success("清空快捷键");
  2239. } else {
  2240. let loadingQmsg = Qmsg.loading("请按下快捷键...", {
  2241. showClose: true,
  2242. onClose() {
  2243. shortCut.cancelEnterShortcutKeys();
  2244. }
  2245. });
  2246. let {
  2247. status,
  2248. option,
  2249. key: isUsedKey
  2250. } = await shortCut.enterShortcutKeys(key);
  2251. loadingQmsg.close();
  2252. if (status) {
  2253. log.success(["成功录入快捷键", option]);
  2254. Qmsg.success("成功录入");
  2255. } else {
  2256. Qmsg.error(
  2257. `快捷键 ${shortCut.translateKeyboardValueToButtonText(
  2258. option
  2259. )} 已被 ${isUsedKey} 占用`
  2260. );
  2261. }
  2262. }
  2263. $btn.innerHTML = getButtonText();
  2264. }
  2265. );
  2266. result.attributes = {};
  2267. Reflect.set(result.attributes, ATTRIBUTE_INIT, () => {
  2268. return false;
  2269. });
  2270. return result;
  2271. };
  2272. const PanelLiveConfig = {
  2273. id: "panel-config-live",
  2274. title: "直播",
  2275. forms: [
  2276. {
  2277. text: "",
  2278. type: "forms",
  2279. forms: [
  2280. {
  2281. text: "功能",
  2282. type: "deepMenu",
  2283. forms: [
  2284. {
  2285. text: "功能",
  2286. type: "forms",
  2287. forms: [
  2288. UISelect(
  2289. "清晰度",
  2290. "live-chooseQuality",
  2291. "origin",
  2292. (() => {
  2293. return Object.keys(VideoQualityMap).map((key) => {
  2294. let item = VideoQualityMap[key];
  2295. return {
  2296. value: key,
  2297. text: item.label
  2298. };
  2299. });
  2300. })(),
  2301. void 0,
  2302. "自行选择清晰度"
  2303. ),
  2304. UISwitch(
  2305. "解锁画质选择",
  2306. "live-unlockImageQuality",
  2307. true,
  2308. void 0,
  2309. "未登录(不可用)的情况下选择原画实际上是未登录(不可用)的情况下最高选择的画质"
  2310. ),
  2311. UISwitch(
  2312. "自动进入网页全屏",
  2313. "live-autoEnterElementFullScreen",
  2314. false,
  2315. void 0,
  2316. "网页加载完毕后自动点击网页全屏按钮进入全屏"
  2317. ),
  2318. UISwitch(
  2319. "监听并关闭【长时间无操作,已暂停播放】弹窗",
  2320. "live-waitToRemovePauseDialog",
  2321. true,
  2322. void 0,
  2323. "自动监听并检测弹窗"
  2324. ),
  2325. UISwitch(
  2326. "禁止自动播放",
  2327. "live-pauseVideo",
  2328. false,
  2329. void 0,
  2330. "暂停直播播放"
  2331. ),
  2332. UISwitch(
  2333. "解析直播信息",
  2334. "live-parsePlayerInstance",
  2335. true,
  2336. void 0,
  2337. "开启后将在油猴菜单中新增菜单【⚙ PlayerInstance】,可解析当前的直播信息"
  2338. ),
  2339. UISwitch(
  2340. "禁用双击点赞",
  2341. "dy-live-disableDoubleClickLike",
  2342. false,
  2343. void 0,
  2344. "禁止直播视频区域双击点赞"
  2345. )
  2346. ]
  2347. },
  2348. {
  2349. text: "视频区域背景色",
  2350. type: "forms",
  2351. forms: [
  2352. UISwitch(
  2353. "启用",
  2354. "live-bgColor-enable",
  2355. false,
  2356. void 0,
  2357. "自定义视频背景色"
  2358. ),
  2359. {
  2360. type: "own",
  2361. attributes: {
  2362. "data-key": "live-changeBackgroundColor",
  2363. "data-default-value": "#000000"
  2364. },
  2365. getLiElementCallBack(liElement) {
  2366. let $left = domUtils.createElement("div", {
  2367. className: "pops-panel-item-left-text",
  2368. innerHTML: `
  2369. <p class="pops-panel-item-left-main-text">视频背景颜色</p>
  2370. <p class="pops-panel-item-left-desc-text">自定义视频背景颜色,包括评论区</p>
  2371. `
  2372. });
  2373. let $right = domUtils.createElement("div", {
  2374. className: "pops-panel-item-right",
  2375. innerHTML: `
  2376. <input type="color" class="pops-color-choose" />
  2377. `
  2378. });
  2379. let $color = $right.querySelector(
  2380. ".pops-color-choose"
  2381. );
  2382. $color.value = PopsPanel.getValue(
  2383. "live-changeBackgroundColor"
  2384. );
  2385. let $style = domUtils.createElement("style");
  2386. domUtils.append(document.head, $style);
  2387. domUtils.on(
  2388. $color,
  2389. ["input", "propertychange"],
  2390. (event) => {
  2391. log.info("选择颜色:" + $color.value);
  2392. $style.innerHTML = `
  2393. div[id^="living_room_player_container"] > div,
  2394. #chatroom > div{
  2395. background: ${$color.value};
  2396. }
  2397. `;
  2398. PopsPanel.setValue(
  2399. "live-changeBackgroundColor",
  2400. $color.value
  2401. );
  2402. }
  2403. );
  2404. liElement.appendChild($left);
  2405. liElement.appendChild($right);
  2406. return liElement;
  2407. }
  2408. }
  2409. ]
  2410. }
  2411. ]
  2412. },
  2413. {
  2414. text: "弹幕过滤器",
  2415. type: "deepMenu",
  2416. forms: [
  2417. {
  2418. text: "",
  2419. type: "forms",
  2420. forms: [
  2421. UISwitch(
  2422. "启用",
  2423. "live-danmu-shield-rule-enable",
  2424. false,
  2425. void 0,
  2426. "启用自定义的弹幕过滤规则"
  2427. ),
  2428. UISwitch(
  2429. "【屏蔽】送礼信息",
  2430. "live-danmu-shield-gift",
  2431. false,
  2432. void 0,
  2433. ""
  2434. ),
  2435. UISwitch(
  2436. "【屏蔽】福袋口令",
  2437. "live-danmu-shield-lucky-bag",
  2438. false,
  2439. void 0,
  2440. ""
  2441. ),
  2442. UIButton(
  2443. "初始化规则",
  2444. "解析并重置规则",
  2445. "重置",
  2446. void 0,
  2447. false,
  2448. false,
  2449. "primary",
  2450. () => {
  2451. DouYinDanmuFilter.init();
  2452. Qmsg.success("更新完毕");
  2453. }
  2454. ),
  2455. {
  2456. type: "own",
  2457. getLiElementCallBack(liElement) {
  2458. let textareaDiv = domUtils.createElement(
  2459. "div",
  2460. {
  2461. className: "pops-panel-textarea",
  2462. innerHTML: `<textarea placeholder="请输入屏蔽规则,每行一个" style="height:350px;"></textarea>`
  2463. },
  2464. {
  2465. style: "width: 100%;"
  2466. }
  2467. );
  2468. let textarea = textareaDiv.querySelector("textarea");
  2469. textarea.value = DouYinDanmuFilter.get();
  2470. domUtils.on(
  2471. textarea,
  2472. ["input", "propertychange"],
  2473. utils.debounce(function() {
  2474. DouYinDanmuFilter.set(textarea.value);
  2475. }, 1e3)
  2476. );
  2477. liElement.appendChild(textareaDiv);
  2478. return liElement;
  2479. }
  2480. }
  2481. ]
  2482. }
  2483. ]
  2484. },
  2485. {
  2486. text: "自定义快捷键",
  2487. type: "deepMenu",
  2488. forms: [
  2489. {
  2490. text: "",
  2491. type: "forms",
  2492. forms: [
  2493. UIButtonShortCut(
  2494. "【屏蔽】聊天室",
  2495. "",
  2496. "dy-live-block-chatroom",
  2497. void 0,
  2498. "点击录入快捷键",
  2499. void 0,
  2500. DouYinLiveShortCut.shortCut
  2501. ),
  2502. UIButtonShortCut(
  2503. "【屏蔽】礼物特效",
  2504. "",
  2505. "dy-live-shieldGiftEffects",
  2506. void 0,
  2507. "点击录入快捷键",
  2508. void 0,
  2509. DouYinLiveShortCut.shortCut
  2510. ),
  2511. UIButtonShortCut(
  2512. "切换静音状态",
  2513. "切换video标签的muted属性",
  2514. "dy-live-shortcut-changeVideoMuted",
  2515. void 0,
  2516. "点击录入快捷键",
  2517. void 0,
  2518. DouYinLiveShortCut.shortCut
  2519. )
  2520. ]
  2521. }
  2522. ]
  2523. },
  2524. {
  2525. type: "deepMenu",
  2526. text: "禁用抖音快捷键",
  2527. afterEnterDeepMenuCallBack: AutoOpenOrClose.afterEnterDeepMenuCallBack,
  2528. forms: [
  2529. {
  2530. type: "forms",
  2531. text: AutoOpenOrClose.text,
  2532. forms: [
  2533. UISwitch("刷新", "dy-live-refresh", false, void 0, "E"),
  2534. UISwitch(
  2535. "屏幕旋转",
  2536. "dy-live-screenRotation",
  2537. false,
  2538. void 0,
  2539. "D"
  2540. ),
  2541. UISwitch(
  2542. "开启小窗模式",
  2543. "dy-live-enableSmallWindowMode",
  2544. false,
  2545. void 0,
  2546. "U"
  2547. )
  2548. ]
  2549. }
  2550. ]
  2551. }
  2552. ]
  2553. },
  2554. {
  2555. text: "",
  2556. type: "forms",
  2557. forms: [
  2558. {
  2559. text: "布局屏蔽-视频区域内",
  2560. type: "deepMenu",
  2561. afterEnterDeepMenuCallBack: AutoOpenOrClose.afterEnterDeepMenuCallBack,
  2562. forms: [
  2563. {
  2564. text: AutoOpenOrClose.text,
  2565. type: "forms",
  2566. forms: [
  2567. UISwitch(
  2568. "【屏蔽】顶栏信息",
  2569. "live-shieldTopToolBarInfo",
  2570. false,
  2571. void 0,
  2572. "屏蔽元素,包括直播作者、右侧的礼物展馆"
  2573. ),
  2574. UISwitch(
  2575. "【屏蔽】底部的礼物栏",
  2576. "live-shieldGiftColumn",
  2577. false,
  2578. void 0,
  2579. "屏蔽元素"
  2580. ),
  2581. UISwitch(
  2582. "【屏蔽】礼物特效",
  2583. "live-shieldGiftEffects",
  2584. false,
  2585. void 0,
  2586. "屏蔽元素"
  2587. ),
  2588. UISwitch(
  2589. "【屏蔽】福袋",
  2590. "live-shieldLucky",
  2591. false,
  2592. void 0,
  2593. "屏蔽元素"
  2594. ),
  2595. UISwitch(
  2596. "【屏蔽】弹幕",
  2597. "live-shieldDanmuku",
  2598. false,
  2599. void 0,
  2600. "屏蔽元素"
  2601. ),
  2602. UISwitch(
  2603. "【屏蔽】小黄车",
  2604. "live-shielYellowCar",
  2605. false,
  2606. void 0,
  2607. "屏蔽元素"
  2608. )
  2609. ]
  2610. },
  2611. {
  2612. type: "forms",
  2613. text: "右键菜单",
  2614. forms: [
  2615. UISwitch(
  2616. "【屏蔽】下载客户端",
  2617. "dy-live-blockVideoRightMenu-downloadClient",
  2618. true,
  2619. void 0,
  2620. "屏蔽右键菜单项"
  2621. )
  2622. ]
  2623. }
  2624. ]
  2625. },
  2626. {
  2627. text: "布局屏蔽-聊天室",
  2628. type: "deepMenu",
  2629. afterEnterDeepMenuCallBack: AutoOpenOrClose.afterEnterDeepMenuCallBack,
  2630. forms: [
  2631. {
  2632. text: AutoOpenOrClose.text,
  2633. type: "forms",
  2634. forms: [
  2635. UISwitch(
  2636. "【屏蔽】聊天室",
  2637. "live-shieldChatRoom",
  2638. false,
  2639. void 0,
  2640. "屏蔽元素"
  2641. ),
  2642. UISwitch(
  2643. "【屏蔽】贵宾席",
  2644. "live-shielChatRoomVipSeats",
  2645. false,
  2646. void 0,
  2647. "屏蔽元素"
  2648. ),
  2649. UISwitch(
  2650. "【屏蔽】用户等级图标",
  2651. "dy-live-shieldUserLevelIcon",
  2652. false,
  2653. void 0,
  2654. "屏蔽元素"
  2655. ),
  2656. UISwitch(
  2657. "【屏蔽】VIP图标",
  2658. "dy-live-shieldUserVIPIcon",
  2659. false,
  2660. void 0,
  2661. "屏蔽元素"
  2662. ),
  2663. UISwitch(
  2664. "【屏蔽】粉丝牌",
  2665. "dy-live-shieldUserFansIcon",
  2666. false,
  2667. void 0,
  2668. "屏蔽元素"
  2669. ),
  2670. UISwitch(
  2671. "【屏蔽】信息播报",
  2672. "dy-live-shieldMessage",
  2673. false,
  2674. void 0,
  2675. "底部滚动播报的的xxx来了,xxx给主播点赞"
  2676. )
  2677. ]
  2678. }
  2679. ]
  2680. }
  2681. ]
  2682. }
  2683. ]
  2684. };
  2685. const DouYinUtils = {
  2686. /**
  2687. * 判断是否是竖屏
  2688. *
  2689. * window.screen.orientation.type
  2690. * + landscape-primary 横屏
  2691. * + portrait-primary 竖屏
  2692. */
  2693. isVerticalScreen() {
  2694. return !window.screen.orientation.type.includes("landscape");
  2695. }
  2696. };
  2697. const DouYinRouter = {
  2698. /**
  2699. * 直播
  2700. */
  2701. isLive() {
  2702. return window.location.hostname === "live.douyin.com" || this.isFollowLive() || this.isRootLive();
  2703. },
  2704. /**
  2705. * 关注-直播
  2706. *
  2707. * + /follow/live/
  2708. */
  2709. isFollowLive() {
  2710. return this.isIndex() && window.location.pathname.startsWith("/follow/live/");
  2711. },
  2712. /**
  2713. * 刷视频时的点击进去的直播
  2714. *
  2715. * + /root/live/
  2716. */
  2717. isRootLive() {
  2718. return this.isIndex() && window.location.pathname.startsWith("/root/live/");
  2719. },
  2720. /**
  2721. * 是否是抖音主站
  2722. */
  2723. isIndex() {
  2724. return window.location.hostname === "www.douyin.com";
  2725. },
  2726. /**
  2727. * 推荐视频
  2728. *
  2729. * + /?recommend=1
  2730. */
  2731. isRecommend() {
  2732. let searchParams = new URLSearchParams(window.location.search);
  2733. return this.isIndex() && searchParams.has("recommend");
  2734. },
  2735. /**
  2736. * 搜索
  2737. *
  2738. * + /search/
  2739. * + /root/search/
  2740. */
  2741. isSearch() {
  2742. return this.isIndex() && (window.location.pathname.startsWith("/search/") || this.isRootSearch());
  2743. },
  2744. /**
  2745. * 其它地方进去的搜索
  2746. *
  2747. * + /root/search/
  2748. */
  2749. isRootSearch() {
  2750. return this.isIndex() && window.location.pathname.startsWith("/root/search/");
  2751. },
  2752. /**
  2753. * 例如:知识、二次元、游戏、美食等
  2754. *
  2755. * + /channel/
  2756. */
  2757. isChannel() {
  2758. return this.isIndex() && window.location.pathname.startsWith("/channel/");
  2759. },
  2760. /**
  2761. * 精选
  2762. *
  2763. * + /discover/
  2764. */
  2765. isDiscover() {
  2766. return this.isIndex() && window.location.pathname.startsWith("/discover/");
  2767. },
  2768. /**
  2769. * 用户主页
  2770. *
  2771. * + /user/
  2772. */
  2773. isUser() {
  2774. return this.isIndex() && window.location.pathname.startsWith("/user/");
  2775. },
  2776. /**
  2777. * 单个视频,一般是分享的视频链接
  2778. *
  2779. * + /video/
  2780. */
  2781. isVideo() {
  2782. return this.isIndex() && window.location.pathname.startsWith("/video/");
  2783. },
  2784. /**
  2785. * 笔记图文
  2786. *
  2787. * + /note/
  2788. */
  2789. isNote() {
  2790. return this.isIndex() && window.location.pathname.startsWith("/note/");
  2791. }
  2792. };
  2793. const MobileCSS$1 = '/* 竖屏且高度小于550px */\r\n@media screen and (max-width: 550px) and (orientation: portrait) {\r\n /* 右侧工具栏放大 */\r\n .basePlayerContainer .positionBox {\r\n bottom: 80px !important;\r\n padding-right: 5px !important;\r\n scale: unset !important;\r\n transform: scale3d(1.12, 1.12, 1.12) !important;\r\n }\r\n /* 右侧工具栏的svg再放大 */\r\n .basePlayerContainer .positionBox svg {\r\n transform: scale3d(1.12, 1.12, 1.12);\r\n }\r\n /* 重置关注按钮的scale */\r\n .basePlayerContainer\r\n .positionBox\r\n .dy-tip-container\r\n div[data-e2e="feed-follow-icon"]\r\n svg {\r\n scale: unset !important;\r\n }\r\n\r\n /* 调整顶部搜索框的宽度 */\r\n #douyin-header\r\n div[data-click="doubleClick"]\r\n > div[data-click="doubleClick"]\r\n > div:has(input[data-e2e="searchbar-input"]) {\r\n width: 150px;\r\n padding-right: 0;\r\n max-width: unset;\r\n flex: 1;\r\n }\r\n /* 搜索框获取焦点时自动放大宽度 */\r\n #douyin-header\r\n div[data-click="doubleClick"]\r\n > div[data-click="doubleClick"]\r\n > div:has(input[data-e2e="searchbar-input"]:focus) {\r\n width: 100vw;\r\n width: 100dvw;\r\n }\r\n /* 去除设置min-width超出浏览器宽度的问题 */\r\n body {\r\n min-width: 100% !important;\r\n }\r\n /* 去除设置width导致顶部工具栏超出浏览器宽度的问题 */\r\n #douyin-right-container #douyin-header {\r\n width: 100%;\r\n }\r\n /* 去除设置 */\r\n #douyin-right-container #douyin-header > div[data-click="doubleClick"] {\r\n min-width: 100%;\r\n }\r\n\r\n /* /video/xxx页面 */\r\n /* 点赞、评论、分享偏移 */\r\n div[data-e2e="video-detail"]\r\n .leftContainer\r\n .basePlayerContainer\r\n .positionBox {\r\n padding-right: 30px !important;\r\n }\r\n /* 底部工具栏右侧的按钮 */\r\n div[data-e2e="video-detail"]\r\n .leftContainer\r\n .xgplayer.xgplayer-pc\r\n .xg-right-grid {\r\n margin-right: 35px !important;\r\n }\r\n /* 评论区全屏 */\r\n div[data-e2e="video-detail"]\r\n .leftContainer\r\n > div:has(.comment-mainContent[data-e2e="comment-list"]),\r\n div[data-e2e="video-detail"]\r\n .leftContainer\r\n > div\r\n > div:has(.comment-mainContent[data-e2e="comment-list"]) {\r\n width: 100vw !important;\r\n }\r\n\r\n /* 设置视频区域的高度 */\r\n #slidelist {\r\n width: 100vw;\r\n height: calc(100vh - var(--header-height)) !important;\r\n }\r\n /* 修正网页全屏下的视频高度 */\r\n #slidelist[class*="isCssFullScreen"] {\r\n height: 100vh !important;\r\n }\r\n /* 去除视频区域右侧偏移 */\r\n .is-mobile-pc div[data-e2e="slideList"] {\r\n padding-right: 0px !important;\r\n height: 100% !important;\r\n }\r\n}\r\n\r\n/* 横屏且高度小于550px */\r\n@media screen and (max-height: 550px) and (orientation: landscape) {\r\n /* 右侧工具栏缩小 */\r\n .basePlayerContainer .positionBox {\r\n transform: scale(0.95) !important;\r\n bottom: 42px !important;\r\n padding-right: 10px !important;\r\n }\r\n /* 右侧工具栏的svg再缩小 */\r\n .basePlayerContainer .positionBox svg {\r\n transform: scale3d(0.95, 0.95, 0.95);\r\n }\r\n /* 修复全屏下不显示视频底部的控制栏 */\r\n .isCssFullScreen [data-e2e="slideList"] {\r\n min-height: auto !important;\r\n }\r\n}\r\n';
  2794. const DouYinVideoPlayerCommentBlockElement = {
  2795. init() {
  2796. PopsPanel.execMenuOnce("dy-video-shieldUserCommentToolBar", () => {
  2797. return this.shieldUserCommentToolBar();
  2798. });
  2799. PopsPanel.execMenuOnce(
  2800. "dy-video-shieldUserCommentEveryOneAllSearch",
  2801. () => {
  2802. return this.shieldUserCommentEveryOneAllSearch();
  2803. }
  2804. );
  2805. },
  2806. /**
  2807. * 【屏蔽】评论工具栏
  2808. */
  2809. shieldUserCommentToolBar() {
  2810. log.info("【屏蔽】评论工具栏");
  2811. return [CommonUtil.addBlockCSS(".comment-input-container")];
  2812. },
  2813. /**
  2814. * 【屏蔽】大家都在搜
  2815. */
  2816. shieldUserCommentEveryOneAllSearch() {
  2817. log.info("【屏蔽】大家都在搜");
  2818. return [CommonUtil.addBlockCSS(".comment-header-with-search")];
  2819. }
  2820. };
  2821. const DouYinVideoPlayerBlockElement_BottomToolbar = {
  2822. init() {
  2823. PopsPanel.execMenuOnce("shieldBottomVideoToolBar", () => {
  2824. return this.shieldBottomVideoToolBar();
  2825. });
  2826. PopsPanel.execMenuOnce("dy-video-bottom-shieldVideoInfoWrap", () => {
  2827. return this.shieldVideoInfoWrap();
  2828. });
  2829. PopsPanel.execMenuOnce("shieldBottomVideoToolbarDanmuContainer", () => {
  2830. return this.shieldBottomVideoToolbarDanmuContainer();
  2831. });
  2832. },
  2833. /**
  2834. * 【屏蔽】底部视频工具栏
  2835. */
  2836. shieldBottomVideoToolBar() {
  2837. log.info("【屏蔽】底部视频工具栏");
  2838. return [
  2839. CommonUtil.addBlockCSS("xg-controls.xgplayer-controls"),
  2840. // 修复屏蔽后视频信息区域未沉底
  2841. DouYinVideoPlayer.removeStyleBottom(),
  2842. addStyle(
  2843. /*css*/
  2844. `
  2845. /* 视频标题往下移 */
  2846. div:has(> #video-info-wrap){
  2847. bottom: 0px !important;
  2848. }
  2849. `
  2850. )
  2851. ];
  2852. },
  2853. /**
  2854. * 【屏蔽】视频信息
  2855. */
  2856. shieldVideoInfoWrap() {
  2857. log.info("【屏蔽】视频信息");
  2858. return [CommonUtil.addBlockCSS("#video-info-wrap")];
  2859. },
  2860. /**
  2861. * 【屏蔽】底部视频工具栏的弹幕容器
  2862. */
  2863. shieldBottomVideoToolbarDanmuContainer() {
  2864. log.info("【屏蔽】底部视频工具栏的弹幕容器");
  2865. return [
  2866. CommonUtil.addBlockCSS(
  2867. 'xg-controls xg-inner-controls .danmakuContainer[data-e2e="danmaku-container"]'
  2868. )
  2869. ];
  2870. }
  2871. };
  2872. const DouYinVideoPlayerBlockElement_RightToolbar = {
  2873. init() {
  2874. PopsPanel.execMenuOnce("shieldPlaySwitchButton", () => {
  2875. return this.shieldPlaySwitchButton();
  2876. });
  2877. PopsPanel.execMenuOnce("shieldAuthorAvatar", () => {
  2878. return this.shieldAuthorAvatar();
  2879. });
  2880. PopsPanel.execMenuOnce("shieldLikeButton", () => {
  2881. return this.shieldLikeButton();
  2882. });
  2883. PopsPanel.execMenuOnce("shieldCommentButton", () => {
  2884. return this.shieldCommentButton();
  2885. });
  2886. PopsPanel.execMenuOnce("shieldCollectionButton", () => {
  2887. return this.shieldCollectionButton();
  2888. });
  2889. PopsPanel.execMenuOnce("shieldSharenButton", () => {
  2890. return this.shieldSharenButton();
  2891. });
  2892. PopsPanel.execMenuOnce("shieldRelatedRecommendationsButton", () => {
  2893. return this.shieldRelatedRecommendationsButton();
  2894. });
  2895. PopsPanel.execMenuOnce("shieldMoreButton", () => {
  2896. return this.shieldMoreButton();
  2897. });
  2898. },
  2899. /**
  2900. * 【屏蔽】切换播放
  2901. */
  2902. shieldPlaySwitchButton() {
  2903. log.info("【屏蔽】切换播放");
  2904. return [
  2905. CommonUtil.addBlockCSS(
  2906. '.positionBox .xgplayer-playswitch[data-state="normal"]',
  2907. "div.xgplayer-playswitch",
  2908. /* 全屏下的右侧的切换播放 */
  2909. ".xgplayer-playswitch"
  2910. ),
  2911. addStyle(
  2912. /*css*/
  2913. `
  2914. div[data-e2e="slideList"]{
  2915. /* 修复屏蔽后的视频宽度占据 */
  2916. padding: 0px !important;
  2917. }
  2918. `
  2919. )
  2920. ];
  2921. },
  2922. /**
  2923. * 【屏蔽】作者头像
  2924. */
  2925. shieldAuthorAvatar() {
  2926. log.info("【屏蔽】作者头像");
  2927. return [
  2928. CommonUtil.addBlockCSS(
  2929. 'div.dy-tip-container:has([data-e2e="video-avatar"])',
  2930. // 2024.7.2 新增其它的样式匹配
  2931. '.basePlayerContainer div[aria-describedby]:has([data-e2e="video-avatar"])'
  2932. )
  2933. ];
  2934. },
  2935. /**
  2936. * 【屏蔽】点赞
  2937. */
  2938. shieldLikeButton() {
  2939. log.info("【屏蔽】点赞");
  2940. return [
  2941. CommonUtil.addBlockCSS(
  2942. 'div.dy-tip-container:has([data-e2e="video-player-digg"])',
  2943. // 2024.7.2 新增其它的样式匹配
  2944. '.basePlayerContainer div[aria-describedby]:has([data-e2e="video-player-digg"])'
  2945. )
  2946. ];
  2947. },
  2948. /**
  2949. * 【屏蔽】评论
  2950. */
  2951. shieldCommentButton() {
  2952. log.info("【屏蔽】评论");
  2953. return [
  2954. CommonUtil.addBlockCSS(
  2955. 'div.dy-tip-container:has([data-e2e="feed-comment-icon"])',
  2956. // 2024.7.2 新增其它的样式匹配
  2957. '.basePlayerContainer div[aria-describedby]:has([data-e2e="feed-comment-icon"])'
  2958. )
  2959. ];
  2960. },
  2961. /**
  2962. * 【屏蔽】收藏
  2963. */
  2964. shieldCollectionButton() {
  2965. log.info("【屏蔽】收藏");
  2966. return [
  2967. CommonUtil.addBlockCSS(
  2968. 'div.dy-tip-container:has([data-e2e="video-player-collect"])',
  2969. // 2024.7.2 新增其它的样式匹配
  2970. '.basePlayerContainer div[data-e2e="video-player-collect"][data-e2e-state="video-player-no-collect"]'
  2971. )
  2972. ];
  2973. },
  2974. /**
  2975. * 【屏蔽】分享
  2976. */
  2977. shieldSharenButton() {
  2978. log.info("【屏蔽】分享");
  2979. return [
  2980. CommonUtil.addBlockCSS(
  2981. 'div.dy-tip-container:has([data-e2e="video-player-share"])',
  2982. // 2024.7.2 新增其它的样式匹配
  2983. '.basePlayerContainer div:has(>div[data-e2e="video-player-share"])'
  2984. )
  2985. ];
  2986. },
  2987. /**
  2988. * 【屏蔽】看相关
  2989. */
  2990. shieldRelatedRecommendationsButton() {
  2991. log.info("【屏蔽】看相关");
  2992. return [
  2993. CommonUtil.addBlockCSS(
  2994. 'div.dy-tip-container:has(path[d="M14 8a8 8 0 00-8 8v4a8 8 0 008 8h8a8 8 0 008-8v-4a8 8 0 00-8-8h-8zm8.5 10.866a1 1 0 000-1.732l-6-3.464a1 1 0 00-1.5.866v6.928a1 1 0 001.5.866l6-3.464z"])',
  2995. 'div.dy-tip-container:has(path[d=" M-4,-10 C-4,-10 4,-10 4,-10 C8.418000221252441,-10 12,-6.418000221252441 12,-2 C12,-2 12,2 12,2 C12,6.418000221252441 8.418000221252441,10 4,10 C4,10 -4,10 -4,10 C-8.418000221252441,10 -12,6.418000221252441 -12,2 C-12,2 -12,-2 -12,-2 C-12,-6.418000221252441 -8.418000221252441,-10 -4,-10z M4.5,0.8659999966621399 C5.166999816894531,0.48100000619888306 5.166999816894531,-0.48100000619888306 4.5,-0.8659999966621399 C4.5,-0.8659999966621399 -1.5,-4.329999923706055 -1.5,-4.329999923706055 C-2.1670000553131104,-4.715000152587891 -3,-4.234000205993652 -3,-3.4639999866485596 C-3,-3.4639999866485596 -3,3.4639999866485596 -3,3.4639999866485596 C-3,4.234000205993652 -2.1670000553131104,4.715000152587891 -1.5,4.329999923706055 C-1.5,4.329999923706055 4.5,0.8659999966621399 4.5,0.8659999966621399z"])',
  2996. // 2024.7.2 新增其它的样式匹配
  2997. '.basePlayerContainer div[aria-describedby]:has(path[d="M14 8a8 8 0 00-8 8v4a8 8 0 008 8h8a8 8 0 008-8v-4a8 8 0 00-8-8h-8zm8.5 10.866a1 1 0 000-1.732l-6-3.464a1 1 0 00-1.5.866v6.928a1 1 0 001.5.866l6-3.464z"])',
  2998. // 2024.7.15
  2999. '.basePlayerContainer div[aria-describedby]:has(path[d="M14 8a8 8 0 0 0-8 8v4a8 8 0 0 0 8 8h8a8 8 0 0 0 8-8v-4a8 8 0 0 0-8-8h-8zm8.5 10.866a1 1 0 0 0 0-1.732l-6-3.464a1 1 0 0 0-1.5.866v6.928a1 1 0 0 0 1.5.866l6-3.464z"])',
  3000. // 2024.7.16 移动端的屏蔽规则
  3001. '.basePlayerContainer div[aria-describedby]:has(path[d=" M-4,-10 C-4,-10 4,-10 4,-10 C8.418000221252441,-10 12,-6.418000221252441 12,-2 C12,-2 12,2 12,2 C12,6.418000221252441 8.418000221252441,10 4,10 C4,10 -4,10 -4,10 C-8.418000221252441,10 -12,6.418000221252441 -12,2 C-12,2 -12,-2 -12,-2 C-12,-6.418000221252441 -8.418000221252441,-10 -4,-10z M4.5,0.8659999966621399 C5.166999816894531,0.48100000619888306 5.166999816894531,-0.48100000619888306 4.5,-0.8659999966621399 C4.5,-0.8659999966621399 -1.5,-4.329999923706055 -1.5,-4.329999923706055 C-2.1670000553131104,-4.715000152587891 -3,-4.234000205993652 -3,-3.4639999866485596 C-3,-3.4639999866485596 -3,3.4639999866485596 -3,3.4639999866485596 C-3,4.234000205993652 -2.1670000553131104,4.715000152587891 -1.5,4.329999923706055 C-1.5,4.329999923706055 4.5,0.8659999966621399 4.5,0.8659999966621399z"])'
  3002. ),
  3003. addStyle(
  3004. /*css*/
  3005. `
  3006. /* 修复分享的悬浮框距离底部的高度 */
  3007. [data-e2e="video-player-share"]+div[data-e2e="video-share-container"] > div:first-child{
  3008. bottom: 0px !important;
  3009. }
  3010. `
  3011. )
  3012. ];
  3013. },
  3014. /**
  3015. * 【屏蔽】更多
  3016. */
  3017. shieldMoreButton() {
  3018. log.info("【屏蔽】更多");
  3019. return [
  3020. CommonUtil.addBlockCSS(
  3021. 'div.dy-tip-container:has([data-e2e="video-play-more"])',
  3022. // 2024.7.2 新增其它的样式匹配
  3023. '.basePlayerContainer div[data-e2e="video-play-more"]'
  3024. ),
  3025. addStyle(
  3026. /*css*/
  3027. `
  3028. /* 修复分享的悬浮框距离底部的高度 */
  3029. [data-e2e="video-player-share"]+div[data-e2e="video-share-container"] > div:first-child{
  3030. bottom: 0px !important;
  3031. }
  3032. `
  3033. )
  3034. ];
  3035. }
  3036. };
  3037. const DouYinVideoPlayerBlockElement = {
  3038. init() {
  3039. PopsPanel.execMenuOnce("shieldRightExpandCommentButton", () => {
  3040. return this.shieldRightExpandCommentButton();
  3041. });
  3042. PopsPanel.execMenuOnce("shieldSearchFloatingBar", () => {
  3043. return this.shieldSearchFloatingBar();
  3044. });
  3045. PopsPanel.execMenuOnce("shieldCloseFullScreenButton", () => {
  3046. return this.shieldCloseFullScreenButton();
  3047. });
  3048. PopsPanel.execMenuOnce("dy-video-blockShopInfo", () => {
  3049. return this.blockShopInfo();
  3050. });
  3051. DouYinVideoPlayerBlockElement_BottomToolbar.init();
  3052. DouYinVideoPlayerBlockElement_RightToolbar.init();
  3053. DouYinVideoPlayerCommentBlockElement.init();
  3054. },
  3055. /**
  3056. * 【屏蔽】右侧的展开评论按钮
  3057. */
  3058. shieldRightExpandCommentButton() {
  3059. log.info("【屏蔽】右侧的展开评论按钮");
  3060. return [
  3061. CommonUtil.addBlockCSS(
  3062. '#sliderVideo[data-e2e="feed-active-video"] > div > div > button[type="button"]',
  3063. '.playerContainer button[type=button] svg > g[filter] > path[d="M21.316 29.73a1.393 1.393 0 01-1.97 0l-5.056-5.055a1.393 1.393 0 010-1.97l.012-.011 5.044-5.045a1.393 1.393 0 011.97 1.97l-4.07 4.071 4.07 4.071a1.393 1.393 0 010 1.97z"]'
  3064. ),
  3065. addStyle(
  3066. /*css*/
  3067. `
  3068. .basePlayerContainer .positionBox{
  3069. padding-right: 20px !important;
  3070. }`
  3071. )
  3072. ];
  3073. },
  3074. /**
  3075. * 左上角的鼠标的快捷搜索热点的悬浮栏
  3076. * 【屏蔽】搜索悬浮栏
  3077. */
  3078. shieldSearchFloatingBar() {
  3079. log.info("【屏蔽】搜索悬浮栏");
  3080. let result = [];
  3081. result.push(
  3082. CommonUtil.addBlockCSS(
  3083. /* 看相关页面的 */
  3084. "#slideMode + div",
  3085. // 2024.7.16
  3086. '.playerContainer .slider-video>div>div:has([data-e2e="searchbar-button"])'
  3087. )
  3088. );
  3089. if (DouYinRouter.isSearch() || DouYinRouter.isDiscover()) {
  3090. result.push(
  3091. CommonUtil.addBlockCSS(
  3092. // 2024.7.30
  3093. '#douyin-right-container> div>div>div> div:has( div> input[data-e2e="searchbar-input"])'
  3094. )
  3095. );
  3096. }
  3097. return result;
  3098. },
  3099. /**
  3100. * 【屏蔽】网页全屏关闭按钮
  3101. */
  3102. shieldCloseFullScreenButton() {
  3103. log.info("【屏蔽】网页全屏关闭按钮");
  3104. let result = [];
  3105. result.push(
  3106. CommonUtil.addBlockCSS(
  3107. // 2024.7.16
  3108. '.playerContainer .slider-video>div>div:has(path[d="M17.448 17.448a1.886 1.886 0 0 1-2.668 0L9 11.668l-5.78 5.78A1.886 1.886 0 1 1 .552 14.78L6.332 9 .552 3.22A1.886 1.886 0 1 1 3.22.552L9 6.332l5.78-5.78a1.886 1.886 0 1 1 2.668 2.668L11.668 9l5.78 5.78a1.886 1.886 0 0 1 0 2.668z"])'
  3109. )
  3110. );
  3111. if (DouYinRouter.isSearch() || DouYinRouter.isDiscover()) {
  3112. result.push(
  3113. CommonUtil.addBlockCSS(
  3114. '#douyin-right-container div>div:has(>svg>path[d="M17.448 17.448a1.886 1.886 0 0 1-2.668 0L9 11.668l-5.78 5.78A1.886 1.886 0 1 1 .552 14.78L6.332 9 .552 3.22A1.886 1.886 0 1 1 3.22.552L9 6.332l5.78-5.78a1.886 1.886 0 1 1 2.668 2.668L11.668 9l5.78 5.78a1.886 1.886 0 0 1 0 2.668z"])'
  3115. )
  3116. );
  3117. }
  3118. return result;
  3119. },
  3120. /**
  3121. * 【屏蔽】购物信息
  3122. */
  3123. blockShopInfo() {
  3124. log.info(`【屏蔽】购物信息`);
  3125. return CommonUtil.addBlockCSS(`.xgplayer-shop-anchor`);
  3126. }
  3127. };
  3128. class GestureBack {
  3129. constructor(config) {
  3130. /**
  3131. * 是否正在后退
  3132. */
  3133. __publicField(this, "isBacking", false);
  3134. __publicField(this, "config");
  3135. this.config = config;
  3136. this.enterGestureBackMode = this.enterGestureBackMode.bind(this);
  3137. this.quitGestureBackMode = this.quitGestureBackMode.bind(this);
  3138. this.popStateEvent = this.popStateEvent.bind(this);
  3139. if (typeof this.config.backDelayTime !== "number" || isNaN(this.config.backDelayTime)) {
  3140. this.config.backDelayTime = 150;
  3141. }
  3142. if (this.config.win == null) {
  3143. this.config.win = self;
  3144. }
  3145. }
  3146. /**
  3147. * popstate事件函数
  3148. * @param event
  3149. */
  3150. popStateEvent(event) {
  3151. utils.preventEvent(event);
  3152. if (this.isBacking) {
  3153. return;
  3154. }
  3155. this.quitGestureBackMode(true);
  3156. }
  3157. /**
  3158. * 进入手势模式
  3159. */
  3160. enterGestureBackMode() {
  3161. log.success("进入手势模式");
  3162. let pushUrl = this.config.hash;
  3163. if (!pushUrl.startsWith("#")) {
  3164. if (!pushUrl.startsWith("/")) {
  3165. pushUrl = "/" + pushUrl;
  3166. }
  3167. pushUrl = "#" + pushUrl;
  3168. }
  3169. if (this.config.useUrl) {
  3170. pushUrl = this.config.win.location.origin + this.config.win.location.pathname + this.config.win.location.search + pushUrl;
  3171. }
  3172. this.config.win.history.pushState({}, "", pushUrl);
  3173. log.success("监听popstate事件");
  3174. domUtils.on(this.config.win, "popstate", this.popStateEvent, {
  3175. capture: true
  3176. });
  3177. }
  3178. /**
  3179. * 退出手势模式
  3180. * @param isUrlChange 是否是url改变触发的
  3181. */
  3182. async quitGestureBackMode(isUrlChange = false) {
  3183. this.isBacking = true;
  3184. log.success("退出手势模式");
  3185. if (typeof this.config.beforeHistoryBackCallBack === "function") {
  3186. this.config.beforeHistoryBackCallBack(isUrlChange);
  3187. }
  3188. let maxDate = Date.now() + 1e3 * 5;
  3189. while (true) {
  3190. if (Date.now() > maxDate) {
  3191. log.error("未知情况,history.back()失败,无法退出手势模式");
  3192. break;
  3193. }
  3194. if (this.config.win.location.hash.endsWith(this.config.hash)) {
  3195. log.info("history.back()");
  3196. this.config.win.history.back();
  3197. await utils.sleep(this.config.backDelayTime || 150);
  3198. } else {
  3199. break;
  3200. }
  3201. }
  3202. log.success("移除popstate事件");
  3203. domUtils.off(this.config.win, "popstate", this.popStateEvent, {
  3204. capture: true
  3205. });
  3206. this.isBacking = false;
  3207. if (typeof this.config.afterHistoryBackCallBack === "function") {
  3208. this.config.afterHistoryBackCallBack(isUrlChange);
  3209. }
  3210. }
  3211. }
  3212. const DouYinGestureBackHashConfig = {
  3213. /** 进入视频评论区 */
  3214. videoCommentDrawer: "videoCommentDrawer"
  3215. };
  3216. const DouYinGestureBackClearHash = () => {
  3217. let findValue = Object.values(DouYinGestureBackHashConfig).find((hash) => {
  3218. return globalThis.location.hash.endsWith(hash);
  3219. });
  3220. if (findValue) {
  3221. globalThis.location.hash = "";
  3222. log.success(`发现残留的手势返回hash,已清理 ==> ` + findValue);
  3223. }
  3224. };
  3225. const DouYinVideoPlayerBlockMouseHoverTip = {
  3226. init() {
  3227. DouYinVideoPlayerBlockMouseHoverTip_RightToolBar.init();
  3228. DouYinVideoPlayerBlockMouseHoverTip_BottomToolBar.init();
  3229. }
  3230. };
  3231. const DouYinVideoPlayerBlockMouseHoverTip_RightToolBar = {
  3232. init() {
  3233. PopsPanel.execMenuOnce(
  3234. "dy-video-mouseHoverTip-rightToolBar-enterUserHome",
  3235. () => {
  3236. return this.blockEnterUserHomeMouseHoverTip();
  3237. }
  3238. );
  3239. PopsPanel.execMenuOnce("dy-video-mouseHoverTip-rightToolBar-follow", () => {
  3240. return this.blockFollowMouseHoverTip();
  3241. });
  3242. PopsPanel.execMenuOnce(
  3243. "dy-video-mouseHoverTip-rightToolBar-addLike",
  3244. () => {
  3245. return this.blockAddLikeMouseHoverTip();
  3246. }
  3247. );
  3248. PopsPanel.execMenuOnce(
  3249. "dy-video-mouseHoverTip-rightToolBar-comment",
  3250. () => {
  3251. return this.blockCommentMouseHoverTip();
  3252. }
  3253. );
  3254. PopsPanel.execMenuOnce(
  3255. "dy-video-mouseHoverTip-rightToolBar-collect",
  3256. () => {
  3257. return this.blockCollectMouseHoverTip();
  3258. }
  3259. );
  3260. PopsPanel.execMenuOnce("dy-video-mouseHoverTip-rightToolBar-share", () => {
  3261. return this.blockShareMouseHoverTip();
  3262. });
  3263. PopsPanel.execMenuOnce(
  3264. "dy-video-mouseHoverTip-rightToolBar-seeCorrelation",
  3265. () => {
  3266. return this.blockSeeCorrelationMouseHoverTip();
  3267. }
  3268. );
  3269. },
  3270. /**
  3271. * 禁用进入作者主页按钮的悬浮提示
  3272. */
  3273. blockEnterUserHomeMouseHoverTip() {
  3274. log.info(`禁用进入作者主页按钮的悬浮提示`);
  3275. return CommonUtil.addBlockCSS(
  3276. ` div > div:has( >a[data-e2e="video-avatar"]) + .semi-portal`
  3277. );
  3278. },
  3279. /**
  3280. * 禁用关注按钮的悬浮提示
  3281. */
  3282. blockFollowMouseHoverTip() {
  3283. log.info(`禁用关注按钮的悬浮提示`);
  3284. return CommonUtil.addBlockCSS(
  3285. `div[data-e2e="feed-follow-icon"] .semi-portal`
  3286. );
  3287. },
  3288. /**
  3289. * 禁用点赞按钮的悬浮提示
  3290. */
  3291. blockAddLikeMouseHoverTip() {
  3292. log.info(`禁用点赞按钮的悬浮提示`);
  3293. return CommonUtil.addBlockCSS(
  3294. `div[data-e2e="video-player-digg"] + .semi-portal`
  3295. );
  3296. },
  3297. /**
  3298. * 禁用评论按钮的悬浮提示
  3299. */
  3300. blockCommentMouseHoverTip() {
  3301. log.info(`禁用评论按钮的悬浮提示`);
  3302. return CommonUtil.addBlockCSS(
  3303. `div[data-e2e="feed-comment-icon"] + .semi-portal`
  3304. );
  3305. },
  3306. /**
  3307. * 禁用收藏按钮的悬浮提示
  3308. */
  3309. blockCollectMouseHoverTip() {
  3310. log.info(`禁用收藏按钮的悬浮提示`);
  3311. return CommonUtil.addBlockCSS(
  3312. `div[data-e2e="video-player-collect"] + .semi-always-dark`
  3313. );
  3314. },
  3315. /**
  3316. * 禁用分享按钮的悬浮提示
  3317. */
  3318. blockShareMouseHoverTip() {
  3319. log.info(`禁用分享按钮的悬浮提示`);
  3320. return CommonUtil.addBlockCSS(`div[data-e2e="video-share-container"]`);
  3321. },
  3322. /**
  3323. * 禁用看相关推荐按钮的悬浮提示
  3324. */
  3325. blockSeeCorrelationMouseHoverTip() {
  3326. log.info(`禁用看相关推荐按钮的悬浮提示`);
  3327. return CommonUtil.addBlockCSS(
  3328. `div:has(+[data-e2e="video-play-more"]) .semi-portal`
  3329. );
  3330. }
  3331. };
  3332. const DouYinVideoPlayerBlockMouseHoverTip_BottomToolBar = {
  3333. init() {
  3334. PopsPanel.execMenuOnce(
  3335. "dy-video-mouseHoverTip-bottomToolBar-automaticBroadcast",
  3336. () => {
  3337. return this.blockAutomaticBroadcast();
  3338. }
  3339. );
  3340. PopsPanel.execMenuOnce(
  3341. "dy-video-mouseHoverTip-bottomToolBar-clearScreen",
  3342. () => {
  3343. return this.blockClearScreenMouseHoverTip();
  3344. }
  3345. );
  3346. PopsPanel.execMenuOnce(
  3347. "dy-video-mouseHoverTip-bottomToolBar-watchLater",
  3348. () => {
  3349. return this.blockWatchLaterMouseHoverTip();
  3350. }
  3351. );
  3352. PopsPanel.execMenuOnce(
  3353. "dy-video-mouseHoverTip-bottomToolBar-pageFullScreen",
  3354. () => {
  3355. return this.blockPageFullScreenMouseHoverTip();
  3356. }
  3357. );
  3358. PopsPanel.execMenuOnce(
  3359. "dy-video-mouseHoverTip-bottomToolBar-fullScreen",
  3360. () => {
  3361. return this.blockFullScreenMouseHoverTip();
  3362. }
  3363. );
  3364. },
  3365. /**
  3366. * 禁用自动连播按钮的悬浮提示
  3367. */
  3368. blockAutomaticBroadcast() {
  3369. log.info(`禁用自动连播按钮的悬浮提示`);
  3370. return CommonUtil.addBlockCSS(
  3371. `div[data-e2e="video-player-auto-play"] + .xgTips`
  3372. );
  3373. },
  3374. /**
  3375. * 禁用清屏按钮的悬浮提示
  3376. */
  3377. blockClearScreenMouseHoverTip() {
  3378. log.info(`禁用清屏按钮的悬浮提示`);
  3379. return CommonUtil.addBlockCSS(
  3380. `.xgplayer-immersive-switch-setting .xgTips`
  3381. );
  3382. },
  3383. /**
  3384. * 禁用稍后再看按钮的悬浮提示
  3385. */
  3386. blockWatchLaterMouseHoverTip() {
  3387. log.info(`禁用稍后再看按钮的悬浮提示`);
  3388. return CommonUtil.addBlockCSS(
  3389. `.xgplayer-watch-later .xgTips`,
  3390. `.xgplayer-watch-later-item + .xgTips`
  3391. );
  3392. },
  3393. /**
  3394. * 禁用网页全屏按钮的悬浮提示
  3395. */
  3396. blockPageFullScreenMouseHoverTip() {
  3397. log.info(`禁用网页全屏按钮的悬浮提示`);
  3398. return CommonUtil.addBlockCSS(`.xgplayer-page-full-screen .xgTips`);
  3399. },
  3400. /**
  3401. * 禁用全屏按钮的悬浮提示
  3402. */
  3403. blockFullScreenMouseHoverTip() {
  3404. log.info(`禁用全屏按钮的悬浮提示`);
  3405. return CommonUtil.addBlockCSS(`.xgplayer-fullscreen .xg-tips`);
  3406. }
  3407. };
  3408. const DouYinVideoPlayer = {
  3409. init() {
  3410. DouYinVideoPlayerBlockElement.init();
  3411. PopsPanel.onceExec("dy-short-cut", () => {
  3412. DouYinVideoPlayerShortCut.init();
  3413. });
  3414. DouYinVideoPlayerBlockMouseHoverTip.init();
  3415. PopsPanel.execMenuOnce("changeCommentToBottom", () => {
  3416. DouYinVideoPlayer.changeCommentToBottom();
  3417. });
  3418. PopsPanel.execMenuOnce("fullScreen", () => {
  3419. return this.fullScreen();
  3420. });
  3421. PopsPanel.execMenuOnce("parseVideo", () => {
  3422. DouYinVideoPlayer.parseVideo();
  3423. });
  3424. PopsPanel.execInheritMenuOnce(
  3425. "autoEnterElementFullScreen",
  3426. "search-autoEnterElementFullScreen",
  3427. () => {
  3428. this.autoEnterElementFullScreen();
  3429. },
  3430. (mainValue, childValue) => {
  3431. if (DouYinRouter.isSearch()) {
  3432. if (mainValue) {
  3433. if (childValue == 1) {
  3434. return true;
  3435. } else if (childValue == 0) {
  3436. return false;
  3437. } else ;
  3438. }
  3439. }
  3440. }
  3441. );
  3442. PopsPanel.execMenuOnce("dy-video-doubleClickEnterElementFullScreen", () => {
  3443. this.doubleClickEnterElementFullScreen();
  3444. });
  3445. PopsPanel.execMenu("dy-video-bgColor-enable", () => {
  3446. PopsPanel.execMenuOnce(
  3447. "dy-video-changeBackgroundColor",
  3448. (value) => {
  3449. return this.changeBackgroundColor(value);
  3450. }
  3451. );
  3452. });
  3453. PopsPanel.execMenuOnce("repairProgressBar", () => {
  3454. PopsPanel.onceExec("repairProgressBar", () => {
  3455. this.repairVideoProgressBar();
  3456. });
  3457. });
  3458. PopsPanel.execMenuOnce("dy-video-gestureBackCloseComment", () => {
  3459. this.gestureBackCloseComment();
  3460. });
  3461. PopsPanel.execMenuOnce("dy-video-waitToRemovePauseDialog", () => {
  3462. this.waitToRemovePauseDialog();
  3463. });
  3464. PopsPanel.execMenuOnce("dy-video-removeStyle-bottom", () => {
  3465. return this.removeStyleBottom();
  3466. });
  3467. domUtils.ready(() => {
  3468. DouYinVideoPlayer.chooseQuality(
  3469. PopsPanel.getValue("chooseVideoDefinition")
  3470. );
  3471. PopsPanel.execMenuOnce("mobileMode", () => {
  3472. return this.mobileMode();
  3473. });
  3474. PopsPanel.execMenuOnce("dy-video-titleInfoAutoHide", () => {
  3475. this.titleInfoAutoHide();
  3476. });
  3477. PopsPanel.execMenuOnce("dy-video-videoControlsAutoHide", () => {
  3478. this.videoControlsAutoHide();
  3479. });
  3480. PopsPanel.execMenuOnce("dy-video-rightToolBarAutoHide", () => {
  3481. this.rightToolBarAutoHide();
  3482. });
  3483. });
  3484. },
  3485. /**
  3486. * 全屏(沉浸模式)
  3487. */
  3488. fullScreen() {
  3489. log.info("全屏");
  3490. let result = [];
  3491. result.push(
  3492. CommonUtil.addBlockCSS(
  3493. /* 右侧工具栏 */
  3494. ".slider-video .positionBox",
  3495. /* 中间底部的视频信息(描述、作者、话题等) */
  3496. "#video-info-wrap",
  3497. /* 中间底部的视频控制工具栏 */
  3498. "xg-controls.xgplayer-controls"
  3499. )
  3500. );
  3501. result.push(DouYinVideoPlayerBlockElement.shieldSearchFloatingBar());
  3502. result.push(
  3503. addStyle(
  3504. /*css*/
  3505. `
  3506. /* 视频全屏 */
  3507. xg-video-container.xg-video-container{
  3508. bottom: 0px !important;
  3509. }
  3510. `
  3511. )
  3512. );
  3513. return result;
  3514. },
  3515. /**
  3516. * 自动进入网页全屏
  3517. * @param [userKeyBoard=false] 是否使用键盘触发
  3518. */
  3519. autoEnterElementFullScreen(userKeyBoard = false) {
  3520. if (userKeyBoard) {
  3521. let keydownEvent = new KeyboardEvent("keydown", {
  3522. bubbles: true,
  3523. cancelable: true,
  3524. key: "Y",
  3525. code: "KeyY",
  3526. keyCode: 89,
  3527. which: 89
  3528. });
  3529. document.dispatchEvent(keydownEvent);
  3530. } else {
  3531. utils.waitNode(
  3532. 'xg-icon[data-e2e="xgplayer-page-full-screen"] .xgplayer-icon'
  3533. ).then(($el) => {
  3534. log.success("自动进入网页全屏");
  3535. $el.click();
  3536. });
  3537. }
  3538. },
  3539. /**
  3540. * 双击进入网页全屏
  3541. */
  3542. doubleClickEnterElementFullScreen() {
  3543. let isDouble = false;
  3544. log.info("注册(不可用)双击进入网页全屏事件");
  3545. let selectorList = [".newVideoPlayer", "#sliderVideo"];
  3546. selectorList.forEach((selector) => {
  3547. domUtils.on(
  3548. document,
  3549. "click",
  3550. selector,
  3551. (event) => {
  3552. if (isDouble) {
  3553. isDouble = false;
  3554. DouYinVideoPlayer.autoEnterElementFullScreen(true);
  3555. } else {
  3556. isDouble = true;
  3557. setTimeout(() => {
  3558. isDouble = false;
  3559. }, 250);
  3560. }
  3561. }
  3562. );
  3563. });
  3564. },
  3565. /**
  3566. * 评论区修改为底部
  3567. */
  3568. changeCommentToBottom() {
  3569. log.info("评论区修改为底部");
  3570. let ATTRIBUTE_KEY2 = "data-vertical-screen";
  3571. function autoChangeCommentPosition() {
  3572. if (DouYinUtils.isVerticalScreen()) {
  3573. log.success("自动判断: 竖屏");
  3574. document.documentElement.setAttribute(ATTRIBUTE_KEY2, "true");
  3575. } else {
  3576. log.success("自动判断: 横屏");
  3577. document.documentElement.removeAttribute(ATTRIBUTE_KEY2);
  3578. }
  3579. }
  3580. autoChangeCommentPosition();
  3581. addStyle(
  3582. /*css*/
  3583. `
  3584. html[${ATTRIBUTE_KEY2}] #sliderVideo[data-e2e="feed-video"] #videoSideBar #relatedVideoCard,
  3585. html[${ATTRIBUTE_KEY2}] #sliderVideo[data-e2e="feed-video"] #videoSideCard #relatedVideoCard{
  3586. display: none !important;
  3587. }
  3588. /* 左侧的视频宽度撑满 */
  3589. html[${ATTRIBUTE_KEY2}] #sliderVideo[data-e2e] .playerContainer,
  3590. html[${ATTRIBUTE_KEY2}] #slideMode[data-e2e] .playerContainer{
  3591. width: 100% !important;
  3592. }
  3593. /* 右侧的评论区宽度撑满,position使用absolute */
  3594. html[${ATTRIBUTE_KEY2}] #sliderVideo[data-e2e="feed-active-video"] #videoSideBar:has(#relatedVideoCard),
  3595. html[${ATTRIBUTE_KEY2}] #slideMode[data-e2e="feed-active-video"] #videoSideBar:has(#relatedVideoCard),
  3596. html[${ATTRIBUTE_KEY2}] #sliderVideo[data-e2e="feed-active-video"] #videoSideCard:has(#relatedVideoCard),
  3597. html[${ATTRIBUTE_KEY2}] #slideMode[data-e2e="feed-active-video"] #videoSideCard:has(#relatedVideoCard){
  3598. width: 100%;
  3599. height: 75%;
  3600. left: 0;
  3601. right: 0;
  3602. bottom: 0;
  3603. background-color: rgba(0, 0, 0, 0.9);
  3604. transition: height .15s linear !important;
  3605. position: absolute;
  3606. }
  3607. `
  3608. );
  3609. PopsPanel.execMenuOnce(
  3610. "douyin-video-autoCheckChangeCommentToBottom",
  3611. () => {
  3612. domUtils.on(window, "resize", autoChangeCommentPosition);
  3613. }
  3614. );
  3615. },
  3616. /**
  3617. * 选择视频清晰度
  3618. * @param [mode=0] 视频播放模式
  3619. */
  3620. chooseQuality(mode = 0) {
  3621. log.info("选择视频清晰度: " + mode);
  3622. let QualitySessionKey = "MANUAL_SWITCH";
  3623. let clarityReal = [
  3624. "adapt_lowest_4_1",
  3625. "adapt_lowest_1440_1",
  3626. "normal_1080_0",
  3627. "normal_540_0",
  3628. "low_720_0",
  3629. "normal_720_0",
  3630. "low_540_0",
  3631. "lower_540_0",
  3632. "adapt_low_540_0",
  3633. "adapt_lowest_1080_1",
  3634. "adapt_lowest_720_1",
  3635. "adapt_540_1",
  3636. "adapt_lower_540_1"
  3637. ];
  3638. let definition = [
  3639. {
  3640. clarityReal,
  3641. done: 1,
  3642. gearClarity: "20",
  3643. gearName: "超清 4K",
  3644. gearType: -2,
  3645. qualityType: 72
  3646. },
  3647. {
  3648. clarityReal,
  3649. done: 1,
  3650. gearClarity: "10",
  3651. gearName: "超清 2K",
  3652. gearType: -1,
  3653. qualityType: 7
  3654. },
  3655. {
  3656. clarityReal,
  3657. done: 1,
  3658. gearClarity: "5",
  3659. gearName: "高清 1080P",
  3660. gearType: 1,
  3661. qualityType: 2
  3662. },
  3663. {
  3664. clarityReal,
  3665. done: 1,
  3666. gearClarity: "4",
  3667. gearName: "高清 720P",
  3668. gearType: 2,
  3669. qualityType: 15
  3670. },
  3671. {
  3672. clarityReal,
  3673. done: 1,
  3674. gearClarity: "3",
  3675. gearName: "标清 540P",
  3676. gearType: 3,
  3677. qualityType: 21
  3678. },
  3679. {
  3680. clarityReal,
  3681. done: 1,
  3682. gearClarity: "2",
  3683. gearName: "极速",
  3684. gearType: 4,
  3685. qualityType: 21
  3686. },
  3687. {
  3688. clarityReal,
  3689. done: 1,
  3690. gearClarity: "0",
  3691. gearName: "智能",
  3692. gearType: 0
  3693. }
  3694. ];
  3695. let choose = definition.find((item) => item.gearType === mode);
  3696. function setVideoQuality(value) {
  3697. _unsafeWindow.sessionStorage.setItem(QualitySessionKey, value);
  3698. }
  3699. if (choose) {
  3700. let chooseStr = JSON.stringify(choose);
  3701. let intervalId = setInterval(() => {
  3702. setVideoQuality(chooseStr);
  3703. }, 250);
  3704. setTimeout(() => {
  3705. clearInterval(intervalId);
  3706. }, 10 * 1e3);
  3707. log.success("设置当前视频的清晰度: " + mode);
  3708. } else {
  3709. log.error("该清晰度不存在: " + mode);
  3710. }
  3711. },
  3712. /**
  3713. * 选择视频倍速
  3714. * @param [rate="1"] 倍速
  3715. */
  3716. chooseVideoRate(rate = "1") {
  3717. let Definition_Key = "player_playbackratio";
  3718. function setRate(value = "1") {
  3719. _unsafeWindow.sessionStorage.setItem(Definition_Key, value);
  3720. $$("xg-icon.xgplayer-playback-setting").forEach(
  3721. ($playbackSetting) => {
  3722. var _a2, _b, _c, _d;
  3723. let $container = utils.getReactObj($playbackSetting).reactContainer;
  3724. (_d = (_c = (_b = (_a2 = $container == null ? void 0 : $container.memoizedState) == null ? void 0 : _a2.element) == null ? void 0 : _b.props) == null ? void 0 : _c.xgCase) == null ? void 0 : _d.updatePlayBackRatio();
  3725. }
  3726. );
  3727. }
  3728. setRate(rate);
  3729. },
  3730. /**
  3731. * 让下载按钮变成解析视频
  3732. */
  3733. parseVideo() {
  3734. log.info("让下载按钮变成解析视频");
  3735. function showParseInfoDialog(srcList) {
  3736. let contentHTML = "";
  3737. srcList.forEach((url) => {
  3738. contentHTML += /*html*/
  3739. `
  3740. <div class="douyin-video-link-item"><a href="${url}" target="_blank">${url}</a></div>
  3741. `;
  3742. });
  3743. contentHTML = /*html*/
  3744. `<div class="douyin-video-link-container">${contentHTML}</div>`;
  3745. __pops.alert({
  3746. title: {
  3747. text: "视频解析",
  3748. position: "center"
  3749. },
  3750. content: {
  3751. text: contentHTML,
  3752. html: true
  3753. },
  3754. mask: {
  3755. enable: true,
  3756. clickEvent: {
  3757. toClose: true
  3758. }
  3759. },
  3760. width: window.innerWidth > 550 ? "550px" : "88vw",
  3761. height: window.innerHeight > 550 ? "550px" : "80vh",
  3762. drag: true,
  3763. dragLimit: true,
  3764. style: (
  3765. /*css*/
  3766. `
  3767. .douyin-video-link-container{
  3768.  
  3769. }
  3770. .douyin-video-link-item{
  3771. white-space: nowrap;
  3772. overflow: hidden;
  3773. text-overflow: ellipsis;
  3774. margin: 10px;
  3775. }
  3776. .douyin-video-link-item a{
  3777.  
  3778. }
  3779. `
  3780. )
  3781. });
  3782. }
  3783. domUtils.on(
  3784. document,
  3785. "click",
  3786. 'div[data-e2e="video-share-container"] div[data-inuser="false"] button + div',
  3787. function(event) {
  3788. var _a2, _b;
  3789. let clickElement = event.target;
  3790. let rectFiber = (_a2 = utils.getReactObj(
  3791. clickElement.parentElement
  3792. )) == null ? void 0 : _a2.reactFiber;
  3793. if (!rectFiber) {
  3794. log.error("获取rectFiber属性失败");
  3795. Qmsg.error("获取rectFiber属性失败");
  3796. return;
  3797. }
  3798. try {
  3799. let awemeInfo = rectFiber.return.memoizedProps.awemeInfo;
  3800. if (!awemeInfo) {
  3801. log.error("获取awemeInfo属性失败");
  3802. Qmsg.error("获取awemeInfo属性失败");
  3803. return;
  3804. }
  3805. log.info([`解析的awemeInfo: `, awemeInfo]);
  3806. let videoDownloadUrlList = [];
  3807. let playAddr = awemeInfo.video.playAddr;
  3808. if (playAddr != null && Array.isArray(playAddr)) {
  3809. videoDownloadUrlList = videoDownloadUrlList.concat(
  3810. playAddr.map((item) => item.src)
  3811. );
  3812. }
  3813. let playAddrH265 = awemeInfo.video.playAddrH265;
  3814. if (playAddrH265 != null && Array.isArray(playAddrH265)) {
  3815. videoDownloadUrlList = videoDownloadUrlList.concat(
  3816. playAddrH265.map((item) => item.src)
  3817. );
  3818. }
  3819. let playApi = awemeInfo.video.playApi;
  3820. if (typeof playApi === "string") {
  3821. videoDownloadUrlList.push(playApi);
  3822. }
  3823. let playApiH265 = awemeInfo.video.playApiH265;
  3824. if (typeof playApiH265 === "string") {
  3825. videoDownloadUrlList.push(playApiH265);
  3826. }
  3827. let download = (_b = awemeInfo == null ? void 0 : awemeInfo.download) == null ? void 0 : _b.urlList;
  3828. if (download != null && Array.isArray(download)) {
  3829. videoDownloadUrlList = videoDownloadUrlList.concat(download);
  3830. }
  3831. if (!videoDownloadUrlList.length) {
  3832. log.error("未获取到视频的有效链接信息");
  3833. Qmsg.error("未获取到视频的有效链接信息");
  3834. return;
  3835. }
  3836. let uniqueVideoDownloadUrlList = [...new Set(videoDownloadUrlList)];
  3837. if (uniqueVideoDownloadUrlList.length != videoDownloadUrlList.length) {
  3838. log.info("去重前视频链接数量: " + videoDownloadUrlList.length);
  3839. log.info(
  3840. "去重后视频链接数量: " + uniqueVideoDownloadUrlList.length
  3841. );
  3842. }
  3843. uniqueVideoDownloadUrlList = uniqueVideoDownloadUrlList.map(
  3844. (item) => {
  3845. if (item.startsWith("http:")) {
  3846. item = item.replace("http:", "");
  3847. }
  3848. return item;
  3849. }
  3850. );
  3851. showParseInfoDialog(uniqueVideoDownloadUrlList);
  3852. } catch (error) {
  3853. log.error(["解析视频失败", error]);
  3854. Qmsg.error("解析视频失败");
  3855. }
  3856. },
  3857. {
  3858. capture: true
  3859. }
  3860. );
  3861. },
  3862. /**
  3863. * 手机模式
  3864. */
  3865. mobileMode() {
  3866. log.info("启用手机模式");
  3867. let result = [];
  3868. DouYin.initialScale();
  3869. result.push(
  3870. CommonUtil.addBlockCSS("img#douyin-temp-sidebar"),
  3871. addStyle(MobileCSS$1)
  3872. );
  3873. PopsPanel.onceExec("repairProgressBar", () => {
  3874. this.repairVideoProgressBar();
  3875. });
  3876. return result;
  3877. },
  3878. /**
  3879. * 修复进度条按钮
  3880. */
  3881. repairVideoProgressBar() {
  3882. log.info("修复进度条按钮");
  3883. addStyle(
  3884. /*css*/
  3885. `
  3886. /* 禁止触发touch事件,因为会影响到按钮点击不到 */
  3887. xg-outer,
  3888. xg-inners {
  3889. pointer-events: none;
  3890. }
  3891. `
  3892. );
  3893. domUtils.on(
  3894. document,
  3895. "touchstart",
  3896. "xg-progress",
  3897. (event) => {
  3898. let $click = event.target;
  3899. let $xg_outer = $click.querySelector("xg-outer");
  3900. if ($xg_outer) {
  3901. $xg_outer.style.height = "6px";
  3902. }
  3903. },
  3904. {
  3905. capture: true
  3906. }
  3907. );
  3908. domUtils.on(
  3909. document,
  3910. "touchend",
  3911. "xg-progress",
  3912. (event) => {
  3913. let $click = event.target;
  3914. let $xg_outer = $click.querySelector("xg-outer");
  3915. if ($xg_outer) {
  3916. $xg_outer.style.height = "";
  3917. }
  3918. },
  3919. {
  3920. capture: true
  3921. }
  3922. );
  3923. },
  3924. /**
  3925. * 修改视频背景颜色
  3926. * @param color 颜色
  3927. */
  3928. changeBackgroundColor(color) {
  3929. log.info("修改视频背景颜色");
  3930. return addStyle(
  3931. /*css*/
  3932. `
  3933. #sliderVideo > div,
  3934. /* 推荐的直播间背景 */
  3935. xgmask,
  3936. /* 用户主页的视频 */
  3937. .basePlayerContainer .imgBackground{
  3938. background: ${color} !important;
  3939. }
  3940. `
  3941. );
  3942. },
  3943. /**
  3944. * 自动隐藏视频标题
  3945. */
  3946. titleInfoAutoHide() {
  3947. log.info(`自动隐藏视频标题`);
  3948. let lockFn = new utils.LockFunction(() => {
  3949. let videoInfoList = [];
  3950. videoInfoList.push(
  3951. // 一般的推荐视频|单个视频的当前观看的视频
  3952. $(
  3953. '#sliderVideo[data-e2e="feed-active-video"] #video-info-wrap:not([data-is-inject-mouse-hide])'
  3954. ),
  3955. // 进入作者主页后的当前观看的视频
  3956. $(
  3957. '#slideMode[data-e2e="feed-active-video"] #video-info-wrap:not([data-is-inject-mouse-hide])'
  3958. ),
  3959. // 单个视频
  3960. $(
  3961. 'div[data-e2e="video-detail"] #video-info-wrap:not([data-is-inject-mouse-hide])'
  3962. )
  3963. );
  3964. if (!videoInfoList.length) {
  3965. return;
  3966. }
  3967. videoInfoList.forEach(($el) => {
  3968. if (!$el) {
  3969. return;
  3970. }
  3971. $el.setAttribute("data-is-inject-mouse-hide", "");
  3972. let timeId = setTimeout(() => {
  3973. domUtils.trigger($el, "mouseleave");
  3974. }, PopsPanel.getValue("dy-video-titleInfoAutoHide-delayTime"));
  3975. domUtils.on($el, ["mouseenter", "touchstart"], (event) => {
  3976. clearTimeout(timeId);
  3977. domUtils.css($el, "opacity", "");
  3978. });
  3979. domUtils.on($el, ["mouseleave", "touchend"], (event) => {
  3980. domUtils.css($el, "opacity", 0);
  3981. });
  3982. });
  3983. });
  3984. utils.mutationObserver(document, {
  3985. config: {
  3986. subtree: true,
  3987. childList: true
  3988. },
  3989. immediate: true,
  3990. callback: () => {
  3991. lockFn.run();
  3992. }
  3993. });
  3994. },
  3995. /**
  3996. * 自动隐藏视频控件
  3997. */
  3998. videoControlsAutoHide() {
  3999. log.info(`自动隐藏视频控件`);
  4000. let lockFn = new utils.LockFunction(() => {
  4001. let videoInfoList = [];
  4002. videoInfoList.push(
  4003. // 一般的推荐视频|单个视频的当前观看的视频
  4004. $(
  4005. `#sliderVideo[data-e2e="feed-active-video"] xg-controls.xgplayer-controls:not([data-is-inject-mouse-hide])`
  4006. ),
  4007. // 进入作者主页后的当前观看的视频
  4008. $(
  4009. '#slideMode[data-e2e="feed-active-video"] xg-controls.xgplayer-controls:not([data-is-inject-mouse-hide])'
  4010. ),
  4011. // 单个视频
  4012. $(
  4013. 'div[data-e2e="video-detail"] xg-controls.xgplayer-controls:not([data-is-inject-mouse-hide])'
  4014. )
  4015. );
  4016. if (!videoInfoList.length) {
  4017. return;
  4018. }
  4019. videoInfoList.forEach(($el) => {
  4020. if (!$el) {
  4021. return;
  4022. }
  4023. $el.setAttribute("data-is-inject-mouse-hide", "");
  4024. let timeId = setTimeout(() => {
  4025. domUtils.trigger($el, "mouseleave");
  4026. }, PopsPanel.getValue("dy-video-videoControlsAutoHide-delayTime"));
  4027. domUtils.on($el, ["mouseenter", "touchstart"], (event) => {
  4028. clearTimeout(timeId);
  4029. domUtils.css($el, "opacity", "");
  4030. });
  4031. domUtils.on($el, ["mouseleave", "touchend"], (event) => {
  4032. domUtils.css($el, "opacity", 0);
  4033. });
  4034. });
  4035. });
  4036. utils.mutationObserver(document, {
  4037. config: {
  4038. subtree: true,
  4039. childList: true
  4040. },
  4041. immediate: true,
  4042. callback: () => {
  4043. lockFn.run();
  4044. }
  4045. });
  4046. },
  4047. /**
  4048. * 自动隐藏右侧工具栏
  4049. */
  4050. rightToolBarAutoHide() {
  4051. log.info(`自动隐藏右侧工具栏`);
  4052. addStyle(
  4053. /*css*/
  4054. `
  4055. .positionBox{
  4056. transition: opacity 0.5s;
  4057. }
  4058. `
  4059. );
  4060. let lockFn = new utils.LockFunction(() => {
  4061. let videoInfoList = [];
  4062. videoInfoList.push(
  4063. // 一般的推荐视频|单个视频的当前观看的视频
  4064. $(
  4065. '#sliderVideo[data-e2e="feed-active-video"] .positionBox:not([data-is-inject-mouse-hide])'
  4066. ),
  4067. // 进入作者主页后的当前观看的视频
  4068. $(
  4069. '#slideMode[data-e2e="feed-active-video"] .positionBox:not([data-is-inject-mouse-hide])'
  4070. ),
  4071. // 单个视频
  4072. $(
  4073. 'div[data-e2e="video-detail"] .positionBox:not([data-is-inject-mouse-hide])'
  4074. )
  4075. );
  4076. if (!videoInfoList.length) {
  4077. return;
  4078. }
  4079. videoInfoList.forEach(($el) => {
  4080. if (!$el) {
  4081. return;
  4082. }
  4083. $el.setAttribute("data-is-inject-mouse-hide", "");
  4084. let timeId = setTimeout(() => {
  4085. domUtils.trigger($el, "mouseleave");
  4086. }, PopsPanel.getValue("dy-video-titleInfoAutoHide-delayTime"));
  4087. domUtils.on($el, ["mouseenter", "touchstart"], (event) => {
  4088. clearTimeout(timeId);
  4089. domUtils.css($el, "opacity", "");
  4090. });
  4091. domUtils.on($el, ["mouseleave", "touchend"], (event) => {
  4092. domUtils.css($el, "opacity", 0);
  4093. });
  4094. });
  4095. });
  4096. utils.mutationObserver(document, {
  4097. config: {
  4098. subtree: true,
  4099. childList: true
  4100. },
  4101. immediate: true,
  4102. callback: () => {
  4103. lockFn.run();
  4104. }
  4105. });
  4106. },
  4107. /**
  4108. * 手势返回关闭评论区
  4109. */
  4110. gestureBackCloseComment() {
  4111. log.info(`手势返回关闭评论区`);
  4112. let gestureback = new GestureBack({
  4113. hash: DouYinGestureBackHashConfig.videoCommentDrawer,
  4114. useUrl: true,
  4115. beforeHistoryBackCallBack(isUrlChange) {
  4116. if (isUrlChange) {
  4117. closeComment();
  4118. }
  4119. }
  4120. });
  4121. const $closeSelector = `#relatedVideoCard .semi-tabs + div svg:has(path[d="M22.133 23.776a1.342 1.342 0 1 0 1.898-1.898l-4.112-4.113 4.112-4.112a1.342 1.342 0 0 0-1.898-1.898l-4.112 4.112-4.113-4.112a1.342 1.342 0 1 0-1.898 1.898l4.113 4.112-4.113 4.113a1.342 1.342 0 0 0 1.898 1.898l4.113-4.113 4.112 4.113z"])`;
  4122. function closeComment() {
  4123. var _a2;
  4124. let $close = $($closeSelector);
  4125. if ($close) {
  4126. let rect = utils.getReactObj($close);
  4127. if (rect) {
  4128. let fn = (_a2 = rect.reactProps) == null ? void 0 : _a2.onClick;
  4129. if (typeof fn === "function") {
  4130. fn();
  4131. } else {
  4132. Qmsg.error("调用关闭评论区按钮的onClick函数失败");
  4133. }
  4134. } else {
  4135. Qmsg.error("获取关闭评论区按钮react信息失败");
  4136. }
  4137. } else {
  4138. Qmsg.error("未找到关闭评论区的按钮");
  4139. }
  4140. }
  4141. domUtils.on(
  4142. document,
  4143. "click",
  4144. `.xgplayer div[data-e2e="feed-comment-icon"]`,
  4145. (event) => {
  4146. log.info(`手势 => 打开评论区`);
  4147. utils.waitNode($closeSelector, 1e4).then(($el) => {
  4148. if (!$el) {
  4149. return;
  4150. }
  4151. log.info(`手势 => 评论区出现`);
  4152. gestureback.enterGestureBackMode();
  4153. });
  4154. },
  4155. {
  4156. capture: true
  4157. }
  4158. );
  4159. domUtils.on(
  4160. document,
  4161. "click",
  4162. $closeSelector,
  4163. (event) => {
  4164. log.info(`手势 => 关闭评论区`);
  4165. gestureback.quitGestureBackMode();
  4166. },
  4167. {
  4168. capture: true
  4169. }
  4170. );
  4171. },
  4172. /**
  4173. * 信息区域
  4174. *
  4175. * 长时间无操作,已暂停播放
  4176. */
  4177. waitToRemovePauseDialog() {
  4178. log.info("监听信息区域【长时间无操作,已暂停播放】弹窗");
  4179. let checkDialogToClose = ($ele) => {
  4180. let eleText = domUtils.text($ele);
  4181. if (eleText.includes("长时间无操作") && eleText.includes("暂停播放")) {
  4182. Qmsg.info(`出现【长时间无操作,已暂停播放】弹窗`, {
  4183. consoleLogContent: true
  4184. });
  4185. let $rect = utils.getReactObj($ele);
  4186. if (typeof $rect.reactProps === "object") {
  4187. let closeDialogFn = utils.queryProperty($rect.reactProps, (obj) => {
  4188. var _a2, _b;
  4189. if (typeof ((_a2 = obj == null ? void 0 : obj["props"]) == null ? void 0 : _a2["onClose"]) === "function") {
  4190. return {
  4191. isFind: true,
  4192. data: obj["props"]["onClose"]
  4193. };
  4194. } else {
  4195. let children = ((_b = obj == null ? void 0 : obj["props"]) == null ? void 0 : _b["children"]) ?? (obj == null ? void 0 : obj["children"]);
  4196. return {
  4197. isFind: false,
  4198. data: Array.isArray(children) ? children[0] : children
  4199. };
  4200. }
  4201. });
  4202. if (typeof closeDialogFn === "function") {
  4203. Qmsg.success(`调用函数关闭弹窗`, { consoleLogContent: true });
  4204. closeDialogFn();
  4205. }
  4206. }
  4207. }
  4208. };
  4209. domUtils.ready(() => {
  4210. utils.mutationObserver(document.body, {
  4211. config: {
  4212. subtree: true,
  4213. childList: true
  4214. },
  4215. callback() {
  4216. $$(
  4217. `.basePlayerContainer xg-bar.xg-right-bar + div`
  4218. ).forEach(($elementTiming) => {
  4219. checkDialogToClose($elementTiming);
  4220. });
  4221. }
  4222. });
  4223. });
  4224. },
  4225. /**
  4226. * 移除video的bottom偏移
  4227. */
  4228. removeStyleBottom() {
  4229. log.info(`移除videobottom偏移`);
  4230. return addStyle(
  4231. /*css*/
  4232. `
  4233. #sliderVideo[data-e2e="feed-active-video"] div:has( > div > #video-info-wrap),
  4234. div:has( > div > pace-island > #video-info-wrap ),
  4235. xg-video-container.xg-video-container{
  4236. bottom: 0 !important;
  4237. }
  4238. `
  4239. );
  4240. }
  4241. };
  4242. const DouYinVideoPlayerShortCut = {
  4243. shortCut: new ShortCut("video-short-cut"),
  4244. $data: {
  4245. rateMap: [
  4246. "0.75",
  4247. "1",
  4248. "1.25",
  4249. "1.5",
  4250. "1.75",
  4251. "2",
  4252. "3"
  4253. ]
  4254. },
  4255. init() {
  4256. this.shortCut.initGlobalKeyboardListener(this.getShortCutMap());
  4257. },
  4258. getShortCutMap() {
  4259. return {
  4260. "dy-video-rate-low": {
  4261. target: "window",
  4262. callback() {
  4263. log.info("触发快捷键 ==> 调用倍速:小");
  4264. let currentRate = _unsafeWindow.sessionStorage.getItem("player_playbackratio") ?? "1";
  4265. let findIndex = DouYinVideoPlayerShortCut.$data.rateMap.findIndex(
  4266. (rate) => {
  4267. return rate === currentRate;
  4268. }
  4269. );
  4270. if (findIndex === 0) {
  4271. log.warn("触发快捷键 ==> 已是最小倍速: " + currentRate);
  4272. return;
  4273. }
  4274. let prevRate = DouYinVideoPlayerShortCut.$data.rateMap[findIndex - 1];
  4275. log.info("触发快捷键 ==> 设置倍速: " + prevRate);
  4276. DouYinVideoPlayer.chooseVideoRate(prevRate);
  4277. }
  4278. },
  4279. "dy-video-rate-up": {
  4280. target: "window",
  4281. callback() {
  4282. log.info("触发快捷键 ==> 调用倍速:大");
  4283. let currentRate = _unsafeWindow.sessionStorage.getItem("player_playbackratio") ?? "1";
  4284. let findIndex = DouYinVideoPlayerShortCut.$data.rateMap.findIndex(
  4285. (rate) => {
  4286. return rate === currentRate;
  4287. }
  4288. );
  4289. if (findIndex === DouYinVideoPlayerShortCut.$data.rateMap.length - 1) {
  4290. log.warn("触发快捷键 ==> 已是最大倍速: " + currentRate);
  4291. return;
  4292. }
  4293. let nextRate = DouYinVideoPlayerShortCut.$data.rateMap[findIndex + 1];
  4294. log.info("触发快捷键 ==> 设置倍速: " + nextRate);
  4295. DouYinVideoPlayer.chooseVideoRate(nextRate);
  4296. }
  4297. },
  4298. "dy-video-shortcut-immersionMode": {
  4299. target: "window",
  4300. callback() {
  4301. log.info("触发快捷键 ==> 沉浸模式");
  4302. let value = PopsPanel.getValue("fullScreen");
  4303. PopsPanel.setValue("fullScreen", !value);
  4304. PopsPanel.execMenuOnce("fullScreen", () => {
  4305. return DouYinVideoPlayer.fullScreen();
  4306. });
  4307. }
  4308. },
  4309. "dy-video-shortcut-changeVideoMuted": {
  4310. target: "window",
  4311. callback() {
  4312. log.info(`触发快捷键 ==> 切换静音状态`);
  4313. $$("video").forEach(($video) => {
  4314. let muted = !$video.muted;
  4315. log.success(`切换video标签的静音状态为 ${muted}`);
  4316. $video.muted = muted;
  4317. });
  4318. }
  4319. }
  4320. };
  4321. }
  4322. };
  4323. const UISlider = function(text, key, defaultValue, min, max, changeCallBack, getToolTipContent, description, step) {
  4324. let result = {
  4325. text,
  4326. type: "slider",
  4327. description,
  4328. attributes: {},
  4329. props: {},
  4330. getValue() {
  4331. return this.props[PROPS_STORAGE_API].get(key, defaultValue);
  4332. },
  4333. getToolTipContent(value) {
  4334. if (typeof getToolTipContent === "function") {
  4335. return getToolTipContent(value);
  4336. } else {
  4337. return `${value}`;
  4338. }
  4339. },
  4340. callback(event, value) {
  4341. this.props[PROPS_STORAGE_API].set(key, value);
  4342. },
  4343. min,
  4344. max,
  4345. step
  4346. };
  4347. Reflect.set(result.attributes, ATTRIBUTE_KEY, key);
  4348. Reflect.set(result.attributes, ATTRIBUTE_DEFAULT_VALUE, defaultValue);
  4349. Reflect.set(result.props, PROPS_STORAGE_API, {
  4350. get(key2, defaultValue2) {
  4351. return PopsPanel.getValue(key2, defaultValue2);
  4352. },
  4353. set(key2, value) {
  4354. PopsPanel.setValue(key2, value);
  4355. }
  4356. });
  4357. return result;
  4358. };
  4359. const UIInput = function(text, key, defaultValue, description, changeCallBack, placeholder = "", isNumber, isPassword) {
  4360. let result = {
  4361. text,
  4362. type: "input",
  4363. isNumber: Boolean(isNumber),
  4364. isPassword: Boolean(isPassword),
  4365. props: {},
  4366. attributes: {},
  4367. description,
  4368. getValue() {
  4369. return this.props[PROPS_STORAGE_API].get(key, defaultValue);
  4370. },
  4371. callback(event, value) {
  4372. this.props[PROPS_STORAGE_API].set(key, value);
  4373. },
  4374. placeholder
  4375. };
  4376. Reflect.set(result.attributes, ATTRIBUTE_KEY, key);
  4377. Reflect.set(result.attributes, ATTRIBUTE_DEFAULT_VALUE, defaultValue);
  4378. Reflect.set(result.props, PROPS_STORAGE_API, {
  4379. get(key2, defaultValue2) {
  4380. return PopsPanel.getValue(key2, defaultValue2);
  4381. },
  4382. set(key2, value) {
  4383. PopsPanel.setValue(key2, value);
  4384. }
  4385. });
  4386. return result;
  4387. };
  4388. const UISelectMultiple = function(text, key, defaultValue, data, callback, description, placeholder = "请至少选择一个选项", selectConfirmDialogDetails) {
  4389. let selectData = [];
  4390. if (typeof data === "function") {
  4391. selectData = data();
  4392. } else {
  4393. selectData = data;
  4394. }
  4395. let result = {
  4396. text,
  4397. type: "select-multiple",
  4398. description,
  4399. placeholder,
  4400. attributes: {},
  4401. props: {},
  4402. getValue() {
  4403. return this.props[PROPS_STORAGE_API].get(key, defaultValue);
  4404. },
  4405. selectConfirmDialogDetails,
  4406. callback(selectInfo) {
  4407. let value = [];
  4408. selectInfo.forEach((selectedInfo) => {
  4409. value.push(selectedInfo.value);
  4410. });
  4411. this.props[PROPS_STORAGE_API].set(key, value);
  4412. log.info(`多选-选择:`, value);
  4413. },
  4414. data: selectData
  4415. };
  4416. Reflect.set(result.attributes, ATTRIBUTE_KEY, key);
  4417. Reflect.set(result.attributes, ATTRIBUTE_DEFAULT_VALUE, defaultValue);
  4418. Reflect.set(result.props, PROPS_STORAGE_API, {
  4419. get(key2, defaultValue2) {
  4420. return PopsPanel.getValue(key2, defaultValue2);
  4421. },
  4422. set(key2, value) {
  4423. PopsPanel.setValue(key2, value);
  4424. }
  4425. });
  4426. return result;
  4427. };
  4428. class RuleEditView {
  4429. constructor(option) {
  4430. __publicField(this, "option");
  4431. this.option = option;
  4432. }
  4433. /**
  4434. * 显示视图
  4435. */
  4436. async showView() {
  4437. var _a2;
  4438. let $dialog = __pops.confirm({
  4439. title: {
  4440. text: this.option.title,
  4441. position: "center"
  4442. },
  4443. content: {
  4444. text: (
  4445. /*html*/
  4446. `
  4447. <form class="rule-form-container" onsubmit="return false">
  4448. <ul class="rule-form-ulist">
  4449. </ul>
  4450. <input type="submit" style="display: none;" />
  4451. </form>
  4452. `
  4453. ),
  4454. html: true
  4455. },
  4456. btn: utils.assign(
  4457. {
  4458. ok: {
  4459. callback: async () => {
  4460. await submitSaveOption();
  4461. }
  4462. }
  4463. },
  4464. this.option.btn || {},
  4465. true
  4466. ),
  4467. mask: {
  4468. enable: true
  4469. },
  4470. style: (
  4471. /*css*/
  4472. `
  4473. ${__pops.config.cssText.panelCSS}
  4474. .rule-form-container {
  4475. }
  4476. .rule-form-container li{
  4477. display: flex;
  4478. align-items: center;
  4479. justify-content: space-between;
  4480. padding: 5px 20px;
  4481. gap: 10px;
  4482. }
  4483. .pops-panel-item-left-main-text{
  4484. max-width: 150px;
  4485. }
  4486. .pops-panel-item-right-text{
  4487. padding-left: 30px;
  4488. }
  4489. .pops-panel-item-right-text,
  4490. .pops-panel-item-right-main-text{
  4491. text-overflow: ellipsis;
  4492. overflow: hidden;
  4493. white-space: nowrap;
  4494. }
  4495.  
  4496. ${((_a2 = this.option) == null ? void 0 : _a2.style) ?? ""}
  4497. `
  4498. ),
  4499. width: typeof this.option.width === "function" ? this.option.width() : window.innerWidth > 500 ? "500px" : "88vw",
  4500. height: typeof this.option.height === "function" ? this.option.height() : window.innerHeight > 500 ? "500px" : "80vh"
  4501. });
  4502. let $form = $dialog.$shadowRoot.querySelector(
  4503. ".rule-form-container"
  4504. );
  4505. $dialog.$shadowRoot.querySelector(
  4506. "input[type=submit]"
  4507. );
  4508. let $ulist = $dialog.$shadowRoot.querySelector(".rule-form-ulist");
  4509. let view = await this.option.getView(await this.option.data());
  4510. $ulist.appendChild(view);
  4511. const submitSaveOption = async () => {
  4512. let result = await this.option.onsubmit($form, await this.option.data());
  4513. if (!result.success) {
  4514. return;
  4515. }
  4516. $dialog.close();
  4517. await this.option.dialogCloseCallBack(true);
  4518. };
  4519. }
  4520. }
  4521. class RuleFilterView {
  4522. constructor(option) {
  4523. __publicField(this, "option");
  4524. this.option = option;
  4525. }
  4526. showView() {
  4527. let $alert = __pops.alert({
  4528. title: {
  4529. text: this.option.title,
  4530. position: "center"
  4531. },
  4532. content: {
  4533. text: (
  4534. /*html*/
  4535. `
  4536. <div class="filter-container"></div>
  4537. `
  4538. )
  4539. },
  4540. btn: {
  4541. ok: {
  4542. text: "关闭",
  4543. type: "default"
  4544. }
  4545. },
  4546. mask: {
  4547. enable: true
  4548. },
  4549. width: window.innerWidth > 500 ? "350px" : "80vw",
  4550. height: window.innerHeight > 500 ? "300px" : "70vh",
  4551. style: (
  4552. /*css*/
  4553. `
  4554. .filter-container{
  4555. height: 100%;
  4556. display: flex;
  4557. flex-direction: column;
  4558. gap: 20px;
  4559. }
  4560. .filter-container button{
  4561. text-wrap: wrap;
  4562. padding: 8px;
  4563. height: auto;
  4564. text-align: left;
  4565. }
  4566. `
  4567. )
  4568. });
  4569. let $filterContainer = $alert.$shadowRoot.querySelector(".filter-container");
  4570. let $fragment = document.createDocumentFragment();
  4571. this.option.filterOption.forEach((filterOption) => {
  4572. let $button = document.createElement("button");
  4573. $button.innerText = filterOption.name;
  4574. let execFilterAndCloseDialog = async () => {
  4575. let allRuleInfo = await this.option.getAllRuleInfo();
  4576. allRuleInfo.forEach(async (ruleInfo) => {
  4577. let filterResult = await filterOption.filterCallBack(ruleInfo.data);
  4578. if (!filterResult) {
  4579. domUtils.hide(ruleInfo.$el, false);
  4580. } else {
  4581. domUtils.show(ruleInfo.$el, false);
  4582. }
  4583. });
  4584. if (typeof this.option.execFilterCallBack === "function") {
  4585. await this.option.execFilterCallBack();
  4586. }
  4587. $alert.close();
  4588. };
  4589. domUtils.on($button, "click", async (event) => {
  4590. utils.preventEvent(event);
  4591. if (typeof filterOption.callback === "function") {
  4592. let result = await filterOption.callback(
  4593. event,
  4594. execFilterAndCloseDialog
  4595. );
  4596. if (!result) {
  4597. return;
  4598. }
  4599. }
  4600. await execFilterAndCloseDialog();
  4601. });
  4602. $fragment.appendChild($button);
  4603. });
  4604. $filterContainer.appendChild($fragment);
  4605. }
  4606. }
  4607. class RuleView {
  4608. constructor(option) {
  4609. __publicField(this, "option");
  4610. this.option = option;
  4611. }
  4612. /**
  4613. * 显示视图
  4614. * @param filterCallBack 返回值为false隐藏,true则不隐藏(不处理)
  4615. */
  4616. async showView(filterCallBack) {
  4617. var _a2, _b, _c, _d, _e, _f, _g, _h, _i;
  4618. let $popsConfirm = __pops.confirm({
  4619. title: {
  4620. text: this.option.title,
  4621. position: "center"
  4622. },
  4623. content: {
  4624. text: (
  4625. /*html*/
  4626. `
  4627. <div class="rule-view-container">
  4628. </div>
  4629. `
  4630. ),
  4631. html: true
  4632. },
  4633. btn: {
  4634. merge: true,
  4635. reverse: false,
  4636. position: "space-between",
  4637. ok: {
  4638. enable: ((_c = (_b = (_a2 = this.option) == null ? void 0 : _a2.bottomControls) == null ? void 0 : _b.add) == null ? void 0 : _c.enable) || true,
  4639. type: "primary",
  4640. text: "添加",
  4641. callback: async (event) => {
  4642. this.showEditView(
  4643. false,
  4644. await this.option.getAddData(),
  4645. $popsConfirm.$shadowRoot
  4646. );
  4647. }
  4648. },
  4649. close: {
  4650. enable: true,
  4651. callback(event) {
  4652. $popsConfirm.close();
  4653. }
  4654. },
  4655. cancel: {
  4656. enable: ((_f = (_e = (_d = this.option) == null ? void 0 : _d.bottomControls) == null ? void 0 : _e.filter) == null ? void 0 : _f.enable) || false,
  4657. type: "default",
  4658. text: "过滤",
  4659. callback: (details, event) => {
  4660. var _a3, _b2, _c2, _d2, _e2, _f2, _g2;
  4661. if (typeof ((_c2 = (_b2 = (_a3 = this.option) == null ? void 0 : _a3.bottomControls) == null ? void 0 : _b2.filter) == null ? void 0 : _c2.callback) === "function") {
  4662. this.option.bottomControls.filter.callback();
  4663. }
  4664. let getAllRuleElement = () => {
  4665. return Array.from(
  4666. $popsConfirm.$shadowRoot.querySelectorAll(
  4667. ".rule-view-container .rule-item"
  4668. )
  4669. );
  4670. };
  4671. let $button = event.target.closest(".pops-confirm-btn").querySelector(".pops-confirm-btn-cancel span");
  4672. if (domUtils.text($button).includes("取消")) {
  4673. getAllRuleElement().forEach(($el) => {
  4674. domUtils.show($el, false);
  4675. });
  4676. domUtils.text($button, "过滤");
  4677. } else {
  4678. let ruleFilterView = new RuleFilterView({
  4679. title: ((_e2 = (_d2 = this.option.bottomControls) == null ? void 0 : _d2.filter) == null ? void 0 : _e2.title) ?? "过滤规则",
  4680. filterOption: ((_g2 = (_f2 = this.option.bottomControls) == null ? void 0 : _f2.filter) == null ? void 0 : _g2.option) || [],
  4681. execFilterCallBack() {
  4682. domUtils.text($button, "取消过滤");
  4683. },
  4684. getAllRuleInfo: () => {
  4685. return getAllRuleElement().map(($el) => {
  4686. return {
  4687. data: this.parseRuleItemElement($el).data,
  4688. $el
  4689. };
  4690. });
  4691. }
  4692. });
  4693. ruleFilterView.showView();
  4694. }
  4695. }
  4696. },
  4697. other: {
  4698. enable: ((_i = (_h = (_g = this.option) == null ? void 0 : _g.bottomControls) == null ? void 0 : _h.clear) == null ? void 0 : _i.enable) || true,
  4699. type: "xiaomi-primary",
  4700. text: `清空所有(${(await this.option.data()).length})`,
  4701. callback: (event) => {
  4702. let $askDialog = __pops.confirm({
  4703. title: {
  4704. text: "提示",
  4705. position: "center"
  4706. },
  4707. content: {
  4708. text: "确定清空所有的数据?",
  4709. html: false
  4710. },
  4711. btn: {
  4712. ok: {
  4713. enable: true,
  4714. callback: async (popsEvent) => {
  4715. var _a3, _b2, _c2;
  4716. log.success("清空所有");
  4717. if (typeof ((_c2 = (_b2 = (_a3 = this.option) == null ? void 0 : _a3.bottomControls) == null ? void 0 : _b2.clear) == null ? void 0 : _c2.callback) === "function") {
  4718. this.option.bottomControls.clear.callback();
  4719. }
  4720. let data = await this.option.data();
  4721. if (data.length) {
  4722. Qmsg.error("清理失败");
  4723. return;
  4724. } else {
  4725. Qmsg.success("清理成功");
  4726. }
  4727. await this.updateDeleteAllBtnText($popsConfirm.$shadowRoot);
  4728. this.clearContent($popsConfirm.$shadowRoot);
  4729. $askDialog.close();
  4730. }
  4731. },
  4732. cancel: {
  4733. text: "取消",
  4734. enable: true
  4735. }
  4736. },
  4737. mask: { enable: true },
  4738. width: "300px",
  4739. height: "200px"
  4740. });
  4741. }
  4742. }
  4743. },
  4744. mask: {
  4745. enable: true
  4746. },
  4747. width: window.innerWidth > 500 ? "500px" : "88vw",
  4748. height: window.innerHeight > 500 ? "500px" : "80vh",
  4749. style: (
  4750. /*css*/
  4751. `
  4752. ${__pops.config.cssText.panelCSS}
  4753. .rule-item{
  4754. display: flex;
  4755. align-items: center;
  4756. line-height: normal;
  4757. font-size: 16px;
  4758. padding: 4px 8px;
  4759. gap: 8px;
  4760. }
  4761. .rule-name{
  4762. flex: 1;
  4763. white-space: nowrap;
  4764. text-overflow: ellipsis;
  4765. overflow: hidden;
  4766. }
  4767. .rule-controls{
  4768. display: flex;
  4769. align-items: center;
  4770. text-overflow: ellipsis;
  4771. overflow: hidden;
  4772. white-space: nowrap;
  4773. gap: 8px;
  4774. padding: 0px;
  4775. }
  4776. .rule-controls-enable{
  4777. }
  4778. .rule-controls-edit{
  4779. }
  4780. .rule-controls-delete{
  4781. }
  4782. .rule-controls-edit,
  4783. .rule-controls-delete{
  4784. width: 16px;
  4785. height: 16px;
  4786. cursor: pointer;
  4787. }
  4788. `
  4789. )
  4790. });
  4791. let allData = await this.option.data();
  4792. let changeButtonText = false;
  4793. for (let index = 0; index < allData.length; index++) {
  4794. let item = allData[index];
  4795. let $ruleItemList = await this.appendRuleItemElement(
  4796. $popsConfirm.$shadowRoot,
  4797. item
  4798. );
  4799. let flag = typeof filterCallBack === "function" ? filterCallBack(item) : true;
  4800. if (!flag) {
  4801. changeButtonText = true;
  4802. $ruleItemList.forEach(($el) => {
  4803. domUtils.hide($el, false);
  4804. });
  4805. }
  4806. }
  4807. if (changeButtonText) {
  4808. let $button = $popsConfirm.$shadowRoot.querySelector(
  4809. ".pops-confirm-btn-cancel span"
  4810. );
  4811. domUtils.text($button, "取消过滤");
  4812. }
  4813. }
  4814. /**
  4815. * 解析弹窗内的各个元素
  4816. */
  4817. parseViewElement($shadowRoot) {
  4818. let $container = $shadowRoot.querySelector(
  4819. ".rule-view-container"
  4820. );
  4821. let $deleteBtn = $shadowRoot.querySelector(
  4822. ".pops-confirm-btn button.pops-confirm-btn-other"
  4823. );
  4824. return {
  4825. /** 容器 */
  4826. $container,
  4827. /** 左下角的清空按钮 */
  4828. $deleteBtn
  4829. };
  4830. }
  4831. /**
  4832. * 解析每一项的元素
  4833. */
  4834. parseRuleItemElement($ruleElement) {
  4835. let $enable = $ruleElement.querySelector(
  4836. ".rule-controls-enable"
  4837. );
  4838. let $enableSwitch = $enable.querySelector(".pops-panel-switch");
  4839. let $enableSwitchInput = $enable.querySelector(
  4840. ".pops-panel-switch__input"
  4841. );
  4842. let $enableSwitchCore = $enable.querySelector(
  4843. ".pops-panel-switch__core"
  4844. );
  4845. let $edit = $ruleElement.querySelector(".rule-controls-edit");
  4846. let $delete = $ruleElement.querySelector(
  4847. ".rule-controls-delete"
  4848. );
  4849. return {
  4850. /** 启用开关 */
  4851. $enable,
  4852. /** 启用开关的container */
  4853. $enableSwitch,
  4854. /** 启用开关的input */
  4855. $enableSwitchInput,
  4856. /** 启用开关的core */
  4857. $enableSwitchCore,
  4858. /** 编辑按钮 */
  4859. $edit,
  4860. /** 删除按钮 */
  4861. $delete,
  4862. /** 存储在元素上的数据 */
  4863. data: Reflect.get($ruleElement, "data-rule")
  4864. };
  4865. }
  4866. /**
  4867. * 创建一条规则元素
  4868. */
  4869. async createRuleItemElement(data, $shadowRoot) {
  4870. let name = await this.option.getDataItemName(data);
  4871. let $ruleItem = domUtils.createElement("div", {
  4872. className: "rule-item",
  4873. innerHTML: (
  4874. /*html*/
  4875. `
  4876. <div class="rule-name">${name}</div>
  4877. <div class="rule-controls">
  4878. <div class="rule-controls-enable">
  4879. <div class="pops-panel-switch">
  4880. <input class="pops-panel-switch__input" type="checkbox">
  4881. <span class="pops-panel-switch__core">
  4882. <div class="pops-panel-switch__action">
  4883. </div>
  4884. </span>
  4885. </div>
  4886. </div>
  4887. <div class="rule-controls-edit">
  4888. ${__pops.config.iconSVG.edit}
  4889. </div>
  4890. <div class="rule-controls-delete">
  4891. ${__pops.config.iconSVG.delete}
  4892. </div>
  4893. </div>
  4894. `
  4895. )
  4896. });
  4897. Reflect.set($ruleItem, "data-rule", data);
  4898. let switchCheckedClassName = "pops-panel-switch-is-checked";
  4899. const {
  4900. $enable,
  4901. $enableSwitch,
  4902. $enableSwitchCore,
  4903. $enableSwitchInput,
  4904. $delete,
  4905. $edit
  4906. } = this.parseRuleItemElement($ruleItem);
  4907. if (this.option.itemControls.enable.enable) {
  4908. domUtils.on($enableSwitchCore, "click", async (event) => {
  4909. let isChecked = false;
  4910. if ($enableSwitch.classList.contains(switchCheckedClassName)) {
  4911. $enableSwitch.classList.remove(switchCheckedClassName);
  4912. isChecked = false;
  4913. } else {
  4914. $enableSwitch.classList.add(switchCheckedClassName);
  4915. isChecked = true;
  4916. }
  4917. $enableSwitchInput.checked = isChecked;
  4918. await this.option.itemControls.enable.callback(data, isChecked);
  4919. });
  4920. if (await this.option.itemControls.enable.getEnable(data)) {
  4921. $enableSwitch.classList.add(switchCheckedClassName);
  4922. }
  4923. } else {
  4924. $enable.remove();
  4925. }
  4926. if (this.option.itemControls.edit.enable) {
  4927. domUtils.on($edit, "click", (event) => {
  4928. utils.preventEvent(event);
  4929. this.showEditView(true, data, $shadowRoot, $ruleItem, (newData) => {
  4930. data = null;
  4931. data = newData;
  4932. });
  4933. });
  4934. } else {
  4935. $edit.remove();
  4936. }
  4937. if (this.option.itemControls.delete.enable) {
  4938. domUtils.on($delete, "click", (event) => {
  4939. utils.preventEvent(event);
  4940. let $askDialog = __pops.confirm({
  4941. title: {
  4942. text: "提示",
  4943. position: "center"
  4944. },
  4945. content: {
  4946. text: "确定删除该条数据?",
  4947. html: false
  4948. },
  4949. btn: {
  4950. ok: {
  4951. enable: true,
  4952. callback: async (popsEvent) => {
  4953. log.success("删除数据");
  4954. let flag = await this.option.itemControls.delete.deleteCallBack(
  4955. data
  4956. );
  4957. if (flag) {
  4958. Qmsg.success("成功删除该数据");
  4959. $ruleItem.remove();
  4960. await this.updateDeleteAllBtnText($shadowRoot);
  4961. $askDialog.close();
  4962. } else {
  4963. Qmsg.error("删除该数据失败");
  4964. }
  4965. }
  4966. },
  4967. cancel: {
  4968. text: "取消",
  4969. enable: true
  4970. }
  4971. },
  4972. mask: {
  4973. enable: true
  4974. },
  4975. width: "300px",
  4976. height: "200px"
  4977. });
  4978. });
  4979. } else {
  4980. $delete.remove();
  4981. }
  4982. return $ruleItem;
  4983. }
  4984. /**
  4985. * 添加一个规则元素
  4986. */
  4987. async appendRuleItemElement($shadowRoot, data) {
  4988. let { $container } = this.parseViewElement($shadowRoot);
  4989. let $ruleItem = [];
  4990. let iteratorData = Array.isArray(data) ? data : [data];
  4991. for (let index = 0; index < iteratorData.length; index++) {
  4992. let item = iteratorData[index];
  4993. let $item = await this.createRuleItemElement(item, $shadowRoot);
  4994. $container.appendChild($item);
  4995. $ruleItem.push($item);
  4996. }
  4997. await this.updateDeleteAllBtnText($shadowRoot);
  4998. return $ruleItem;
  4999. }
  5000. /**
  5001. * 更新弹窗内容的元素
  5002. */
  5003. async updateRuleContaienrElement($shadowRoot) {
  5004. this.clearContent($shadowRoot);
  5005. const { $container } = this.parseViewElement($shadowRoot);
  5006. let data = await this.option.data();
  5007. await this.appendRuleItemElement($shadowRoot, data);
  5008. await this.updateDeleteAllBtnText($shadowRoot);
  5009. }
  5010. /**
  5011. * 更新规则元素
  5012. */
  5013. async updateRuleItemElement(data, $oldRuleItem, $shadowRoot) {
  5014. let $newRuleItem = await this.createRuleItemElement(data, $shadowRoot);
  5015. $oldRuleItem.after($newRuleItem);
  5016. $oldRuleItem.remove();
  5017. }
  5018. /**
  5019. * 清空内容
  5020. */
  5021. clearContent($shadowRoot) {
  5022. const { $container } = this.parseViewElement($shadowRoot);
  5023. domUtils.html($container, "");
  5024. }
  5025. /**
  5026. * 设置删除按钮的文字
  5027. */
  5028. setDeleteBtnText($shadowRoot, text, isHTML = false) {
  5029. const { $deleteBtn } = this.parseViewElement($shadowRoot);
  5030. if (isHTML) {
  5031. domUtils.html($deleteBtn, text);
  5032. } else {
  5033. domUtils.text($deleteBtn, text);
  5034. }
  5035. }
  5036. /**
  5037. * 更新【清空所有】的按钮的文字
  5038. * @param $shadowRoot
  5039. */
  5040. async updateDeleteAllBtnText($shadowRoot) {
  5041. let data = await this.option.data();
  5042. this.setDeleteBtnText($shadowRoot, `清空所有(${data.length})`);
  5043. }
  5044. /**
  5045. * 显示编辑视图
  5046. * @param isEdit 是否是编辑状态
  5047. * @param editData 编辑的数据
  5048. */
  5049. showEditView(isEdit, editData, $parentShadowRoot, $editRuleItemElement, updateDataCallBack) {
  5050. let dialogCloseCallBack = async (isSubmit) => {
  5051. if (isSubmit) ;
  5052. else {
  5053. if (!isEdit) {
  5054. await this.option.deleteData(editData);
  5055. }
  5056. if (typeof updateDataCallBack === "function") {
  5057. let newData = await this.option.getData(editData);
  5058. updateDataCallBack(newData);
  5059. }
  5060. }
  5061. };
  5062. let editView = new RuleEditView({
  5063. title: isEdit ? "编辑" : "添加",
  5064. data: () => {
  5065. return editData;
  5066. },
  5067. dialogCloseCallBack,
  5068. getView: async (data) => {
  5069. return await this.option.itemControls.edit.getView(data, isEdit);
  5070. },
  5071. btn: {
  5072. ok: {
  5073. enable: true,
  5074. text: isEdit ? "修改" : "添加"
  5075. },
  5076. cancel: {
  5077. callback: async (detail, event) => {
  5078. detail.close();
  5079. await dialogCloseCallBack(false);
  5080. }
  5081. },
  5082. close: {
  5083. callback: async (detail, event) => {
  5084. detail.close();
  5085. await dialogCloseCallBack(false);
  5086. }
  5087. }
  5088. },
  5089. onsubmit: async ($form, data) => {
  5090. let result = await this.option.itemControls.edit.onsubmit(
  5091. $form,
  5092. isEdit,
  5093. data
  5094. );
  5095. if (result.success) {
  5096. if (isEdit) {
  5097. Qmsg.success("修改成功");
  5098. $parentShadowRoot && await this.updateRuleItemElement(
  5099. result.data,
  5100. $editRuleItemElement,
  5101. $parentShadowRoot
  5102. );
  5103. } else {
  5104. $parentShadowRoot && await this.appendRuleItemElement(
  5105. $parentShadowRoot,
  5106. result.data
  5107. );
  5108. }
  5109. } else {
  5110. if (isEdit) {
  5111. Qmsg.error("修改失败");
  5112. }
  5113. }
  5114. return result;
  5115. },
  5116. style: this.option.itemControls.edit.style,
  5117. width: this.option.itemControls.edit.width,
  5118. height: this.option.itemControls.edit.height
  5119. });
  5120. editView.showView();
  5121. }
  5122. }
  5123. class DouYinVideoFilterBase {
  5124. constructor() {
  5125. __publicField(this, "$data", {
  5126. dislike_request_queue: []
  5127. });
  5128. }
  5129. /**
  5130. * 解析awemeInfo转为规则过滤的字典
  5131. * @param awemeInfo
  5132. * @param showLog 是否显示日志输出
  5133. */
  5134. parseAwemeInfoDictData(awemeInfo, showLog = false) {
  5135. var _a2, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u;
  5136. let authorInfo = (awemeInfo == null ? void 0 : awemeInfo["authorInfo"]) || // @ts-ignore
  5137. (awemeInfo == null ? void 0 : awemeInfo["author"]);
  5138. let nickname = (_a2 = authorInfo == null ? void 0 : authorInfo["nickname"]) == null ? void 0 : _a2.toString();
  5139. let uid = (_b = authorInfo == null ? void 0 : authorInfo["uid"]) == null ? void 0 : _b.toString();
  5140. let desc = (_c = awemeInfo == null ? void 0 : awemeInfo["desc"]) == null ? void 0 : _c.toString();
  5141. let musicAlbum = (_d = awemeInfo == null ? void 0 : awemeInfo["music"]) == null ? void 0 : _d["album"];
  5142. let musicAuthor = (_e = awemeInfo == null ? void 0 : awemeInfo["music"]) == null ? void 0 : _e["author"];
  5143. let musicTitle = (_f = awemeInfo == null ? void 0 : awemeInfo["music"]) == null ? void 0 : _f["title"];
  5144. let collectCount = ((_g = awemeInfo == null ? void 0 : awemeInfo["stats"]) == null ? void 0 : _g["collectCount"]) || // @ts-ignore
  5145. ((_h = awemeInfo == null ? void 0 : awemeInfo["statistics"]) == null ? void 0 : _h["collect_count"]);
  5146. let commentCount = ((_i = awemeInfo == null ? void 0 : awemeInfo["stats"]) == null ? void 0 : _i["commentCount"]) || // @ts-ignore
  5147. ((_j = awemeInfo == null ? void 0 : awemeInfo["statistics"]) == null ? void 0 : _j["comment_count"]);
  5148. let diggCount = ((_k = awemeInfo == null ? void 0 : awemeInfo["stats"]) == null ? void 0 : _k["diggCount"]) || // @ts-ignore
  5149. ((_l = awemeInfo == null ? void 0 : awemeInfo["statistics"]) == null ? void 0 : _l["digg_count"]);
  5150. let shareCount = ((_m = awemeInfo == null ? void 0 : awemeInfo["stats"]) == null ? void 0 : _m["shareCount"]) || // @ts-ignore
  5151. ((_n = awemeInfo == null ? void 0 : awemeInfo["statistics"]) == null ? void 0 : _n["share_count"]);
  5152. let duration = (_o = awemeInfo == null ? void 0 : awemeInfo["video"]) == null ? void 0 : _o["duration"];
  5153. let textExtraInstance = (
  5154. // @ts-ignore
  5155. (awemeInfo == null ? void 0 : awemeInfo["textExtra"]) || (awemeInfo == null ? void 0 : awemeInfo["text_extra"])
  5156. );
  5157. let textExtra = [];
  5158. let isLive = false;
  5159. let isAds = false;
  5160. let isSeriesInfo = false;
  5161. let isMixInfo = false;
  5162. let riskInfoContent = ((_p = awemeInfo == null ? void 0 : awemeInfo["riskInfos"]) == null ? void 0 : _p.content) || // @ts-ignore
  5163. ((_q = awemeInfo == null ? void 0 : awemeInfo["risk_infos"]) == null ? void 0 : _q.content);
  5164. let seriesInfoName = void 0;
  5165. let seriesInfoContentTypes = [];
  5166. let isPicture = (
  5167. // @ts-ignore
  5168. (awemeInfo == null ? void 0 : awemeInfo["aweme_type"]) === 68
  5169. );
  5170. if (typeof textExtraInstance === "object" && Array.isArray(textExtraInstance)) {
  5171. textExtraInstance == null ? void 0 : textExtraInstance.forEach((item) => {
  5172. let tagName = (item == null ? void 0 : item["hashtagName"]) || (item == null ? void 0 : item["hashtag_name"]);
  5173. if (typeof tagName === "string") {
  5174. textExtra.push(tagName);
  5175. }
  5176. });
  5177. }
  5178. let mixInfoName = void 0;
  5179. let mixInfoDesc = void 0;
  5180. let videoTagInstance = (
  5181. // @ts-ignore
  5182. (awemeInfo == null ? void 0 : awemeInfo["videoTag"]) || (awemeInfo == null ? void 0 : awemeInfo["video_tag"])
  5183. );
  5184. let videoTag = [];
  5185. let videoTagId = [];
  5186. let awemeId = (
  5187. // @ts-ignore
  5188. (awemeInfo == null ? void 0 : awemeInfo["aweme_id"]) || (awemeInfo == null ? void 0 : awemeInfo["awemeId"])
  5189. );
  5190. if (typeof videoTagInstance === "object" && Array.isArray(videoTagInstance)) {
  5191. videoTagInstance.forEach((item) => {
  5192. let tagName = (item == null ? void 0 : item["tagName"]) || (item == null ? void 0 : item["tag_name"]);
  5193. let tagId = (item == null ? void 0 : item["tagId"]) || (item == null ? void 0 : item["tag_id"]);
  5194. if (typeof tagName === "string") {
  5195. videoTag.push(tagName);
  5196. }
  5197. if (typeof tagId === "number" || typeof tagId === "string") {
  5198. videoTagId.push(tagId.toString());
  5199. }
  5200. });
  5201. }
  5202. if (typeof awemeInfo["cellRoom"] === "object" || // @ts-ignore
  5203. typeof awemeInfo["cell_room"] === "object") {
  5204. isLive = true;
  5205. if (showLog) {
  5206. log.success("直播间:cellRoom is not null");
  5207. }
  5208. }
  5209. if (awemeInfo["isAds"] || // @ts-ignore
  5210. awemeInfo["is_ads"]) {
  5211. isAds = true;
  5212. if (showLog) {
  5213. log.success("广告:isAds is true");
  5214. }
  5215. } else if (typeof awemeInfo["rawAdData"] === "string" && utils.isNotNull(awemeInfo["rawAdData"]) || // @ts-ignore
  5216. typeof awemeInfo["raw_ad_data"] === "string" && // @ts-ignore
  5217. utils.isNotNull(awemeInfo["raw_ad_data"])) {
  5218. isAds = true;
  5219. if (showLog) {
  5220. log.success("广告:rawAdData is not null");
  5221. }
  5222. } else if (awemeInfo["webRawData"]) {
  5223. if ((_s = (_r = awemeInfo["webRawData"]) == null ? void 0 : _r["brandAd"]) == null ? void 0 : _s["is_ad"]) {
  5224. isAds = true;
  5225. if (showLog) {
  5226. log.success("广告:webRawData.brandAd.is_ad is true");
  5227. }
  5228. } else if ((_u = (_t = awemeInfo["webRawData"]) == null ? void 0 : _t["insertInfo"]) == null ? void 0 : _u["is_ad"]) {
  5229. isAds = true;
  5230. if (showLog) {
  5231. log.success("广告:webRawData.insertInfo.is_ad is true");
  5232. }
  5233. }
  5234. } else if (awemeInfo["web_raw_data"]) {
  5235. if (typeof awemeInfo["web_raw_data"] === "string") ;
  5236. }
  5237. if (typeof riskInfoContent === "string" && riskInfoContent.trim() === "" || typeof riskInfoContent !== "string") {
  5238. riskInfoContent = void 0;
  5239. }
  5240. let series_info = (awemeInfo == null ? void 0 : awemeInfo["seriesInfo"]) || // @ts-ignore
  5241. (awemeInfo == null ? void 0 : awemeInfo["series_info"]);
  5242. if (typeof series_info === "object" && series_info != null) {
  5243. isSeriesInfo = true;
  5244. seriesInfoName = (series_info == null ? void 0 : series_info["seriesName"]) || // @ts-ignore
  5245. (series_info == null ? void 0 : series_info["series_name"]);
  5246. let series_content_types = (series_info == null ? void 0 : series_info["seriesContentTypes"]) || // @ts-ignore
  5247. (series_info == null ? void 0 : series_info["series_content_types"]);
  5248. if (Array.isArray(series_content_types)) {
  5249. series_content_types.forEach((it) => {
  5250. seriesInfoContentTypes.push(it["name"]);
  5251. });
  5252. }
  5253. }
  5254. let mixInfo = (awemeInfo == null ? void 0 : awemeInfo["mixInfo"]) || // @ts-ignore
  5255. (awemeInfo == null ? void 0 : awemeInfo["mix_info"]);
  5256. if (typeof mixInfo === "object" && utils.isNotNull(mixInfo)) {
  5257. mixInfoName = (mixInfo == null ? void 0 : mixInfo["mixName"]) || (mixInfo == null ? void 0 : mixInfo["mix_name"]);
  5258. mixInfoDesc = mixInfo == null ? void 0 : mixInfo["desc"];
  5259. }
  5260. if (isPicture) {
  5261. duration = void 0;
  5262. }
  5263. return {
  5264. awemeId,
  5265. nickname,
  5266. uid,
  5267. desc,
  5268. textExtra,
  5269. videoTag,
  5270. videoTagId,
  5271. musicAlbum,
  5272. musicAuthor,
  5273. musicTitle,
  5274. riskInfoContent,
  5275. seriesInfoName,
  5276. seriesInfoContentTypes,
  5277. mixInfoName,
  5278. mixInfoDesc,
  5279. collectCount,
  5280. commentCount,
  5281. diggCount,
  5282. shareCount,
  5283. duration,
  5284. isLive,
  5285. isAds,
  5286. isSeriesInfo,
  5287. isMixInfo,
  5288. isPicture
  5289. };
  5290. }
  5291. /**
  5292. * 根据视频信息,判断是否需要屏蔽
  5293. */
  5294. checkFilterWithRule(details) {
  5295. if (details.videoInfoValue == null) {
  5296. return false;
  5297. }
  5298. if (details.ruleValue == null) {
  5299. return false;
  5300. }
  5301. if (typeof details.videoInfoValue === "string") {
  5302. if (Boolean(details.videoInfoValue.match(details.ruleValue))) {
  5303. return true;
  5304. }
  5305. } else if (typeof details.videoInfoValue === "object") {
  5306. if (Array.isArray(details.videoInfoValue)) {
  5307. let findValue = details.videoInfoValue.find((awemeInfoDictValue) => {
  5308. if (typeof awemeInfoDictValue === "string" && details.ruleValue != null) {
  5309. return Boolean(awemeInfoDictValue.match(details.ruleValue));
  5310. } else {
  5311. return false;
  5312. }
  5313. });
  5314. if (findValue) {
  5315. return true;
  5316. }
  5317. }
  5318. } else if (typeof details.videoInfoValue === "number") {
  5319. if (typeof details.ruleValue === "string") {
  5320. let ruleValue = details.ruleValue.trim();
  5321. let compareNumberMatch = ruleValue.match(/(\d+)/);
  5322. if (!compareNumberMatch) {
  5323. log.warn("过滤器-解析比较大小的数字失败: ", details);
  5324. return false;
  5325. }
  5326. let compareNumber = Number(compareNumberMatch[1]);
  5327. if (ruleValue.startsWith(">")) {
  5328. if (ruleValue.startsWith(">=")) {
  5329. if (details.videoInfoValue >= compareNumber) {
  5330. return true;
  5331. }
  5332. } else {
  5333. if (details.videoInfoValue > compareNumber) {
  5334. return true;
  5335. }
  5336. }
  5337. } else if (ruleValue.startsWith("<")) {
  5338. if (ruleValue.startsWith("<=")) {
  5339. if (details.videoInfoValue <= compareNumber) {
  5340. return true;
  5341. }
  5342. } else {
  5343. if (details.videoInfoValue < compareNumber) {
  5344. return true;
  5345. }
  5346. }
  5347. } else if (ruleValue.startsWith("=")) {
  5348. if (details.videoInfoValue === compareNumber) {
  5349. return true;
  5350. }
  5351. } else {
  5352. log.warn("视频过滤器-未经允许的比较符号: ", details);
  5353. return false;
  5354. }
  5355. }
  5356. } else if (typeof details.videoInfoValue === "boolean") {
  5357. if (typeof details.ruleValue === "string") {
  5358. let trimRuleValue = details.ruleValue.trim();
  5359. return details.videoInfoValue.toString() === trimRuleValue;
  5360. }
  5361. }
  5362. return false;
  5363. }
  5364. /**
  5365. * 检测视频是否可以屏蔽,可以屏蔽返回true
  5366. * @param rule 规则
  5367. * @param awemeInfo 视频信息结构
  5368. */
  5369. checkAwemeInfoIsFilter(rule, awemeInfo) {
  5370. let transformAwemeInfo = this.parseAwemeInfoDictData(awemeInfo);
  5371. let flag = false;
  5372. let matchedFilterOption = null;
  5373. for (let index = 0; index < rule.length; index++) {
  5374. const filterOption = rule[index];
  5375. if (!Reflect.has(transformAwemeInfo, filterOption.data.ruleName)) {
  5376. continue;
  5377. }
  5378. let tagKey = filterOption.data.ruleName;
  5379. let tagValue = transformAwemeInfo[tagKey];
  5380. let details = {
  5381. videoInfoKey: tagKey,
  5382. videoInfoValue: tagValue,
  5383. ruleKey: filterOption.data.ruleName,
  5384. ruleValue: filterOption.data.ruleValue
  5385. };
  5386. flag = this.checkFilterWithRule(details);
  5387. if (flag) {
  5388. if (Array.isArray(filterOption.dynamicData) && filterOption.dynamicData.length) {
  5389. let dynamicDetailsList = [];
  5390. for (let dynamicIndex = 0; dynamicIndex < filterOption.dynamicData.length; dynamicIndex++) {
  5391. const dynamicOption = filterOption.dynamicData[dynamicIndex];
  5392. let dynamicTagKey = dynamicOption.ruleName;
  5393. let dynamicTagValue = transformAwemeInfo[dynamicTagKey];
  5394. let dynamicDetails = {
  5395. videoInfoKey: dynamicTagKey,
  5396. videoInfoValue: dynamicTagValue,
  5397. ruleKey: dynamicOption.ruleName,
  5398. ruleValue: dynamicOption.ruleValue
  5399. };
  5400. dynamicDetailsList.push(dynamicDetails);
  5401. let dynamicCheckFlag = this.checkFilterWithRule(dynamicDetails);
  5402. flag = flag && dynamicCheckFlag;
  5403. if (!flag) {
  5404. break;
  5405. }
  5406. }
  5407. if (flag) {
  5408. log.success([
  5409. `视频过滤器-多组 ==> ${filterOption.name}`,
  5410. transformAwemeInfo,
  5411. details,
  5412. dynamicDetailsList,
  5413. awemeInfo,
  5414. filterOption
  5415. ]);
  5416. }
  5417. } else {
  5418. log.success([
  5419. `视频过滤器 ==> ${filterOption.name}`,
  5420. transformAwemeInfo,
  5421. details,
  5422. awemeInfo,
  5423. filterOption
  5424. ]);
  5425. }
  5426. }
  5427. if (flag) {
  5428. matchedFilterOption = filterOption;
  5429. break;
  5430. }
  5431. }
  5432. return {
  5433. /** 是否允许过滤 */
  5434. isFilter: flag,
  5435. /** 命中的过滤规则 */
  5436. matchedFilterOption,
  5437. /** 解析出的视频信息 */
  5438. transformAwemeInfo
  5439. };
  5440. }
  5441. /**
  5442. * 发送请求-不感兴趣
  5443. * @param matchedFilterOption 命中的规则
  5444. * @param awemeInfo 视频信息结构
  5445. */
  5446. async sendDislikeVideo(matchedFilterOption, awemeInfo) {
  5447. }
  5448. removeAweme(...args) {
  5449. if (args.length === 1) {
  5450. let $video = args[0];
  5451. if ($video != null && $video instanceof HTMLElement) {
  5452. $video.remove();
  5453. }
  5454. } else if (args.length === 2) {
  5455. let videoList = args[0];
  5456. let deleteIndex = args[1];
  5457. if (typeof deleteIndex === "number") {
  5458. let item = videoList[deleteIndex];
  5459. if (item != null && item instanceof Element) {
  5460. item == null ? void 0 : item.remove();
  5461. }
  5462. videoList.splice(deleteIndex, 1);
  5463. }
  5464. }
  5465. }
  5466. }
  5467. const DouYinNetWorkHook = {
  5468. __ajaxHooker: null,
  5469. get ajaxHooker() {
  5470. if (this.__ajaxHooker == null) {
  5471. this.__ajaxHooker = utils.ajaxHooker();
  5472. }
  5473. return this.__ajaxHooker;
  5474. }
  5475. };
  5476. const PanelUISize = {
  5477. /**
  5478. * 一般设置界面的尺寸
  5479. */
  5480. setting: {
  5481. get width() {
  5482. if (window.innerWidth < 550) {
  5483. return "88vw";
  5484. } else if (window.innerWidth < 700) {
  5485. return "550px";
  5486. } else {
  5487. return "700px";
  5488. }
  5489. },
  5490. get height() {
  5491. if (window.innerHeight < 450) {
  5492. return "70vh";
  5493. } else if (window.innerHeight < 550) {
  5494. return "450px";
  5495. } else {
  5496. return "550px";
  5497. }
  5498. }
  5499. },
  5500. /**
  5501. * 信息界面,一般用于提示信息之类
  5502. */
  5503. info: {
  5504. get width() {
  5505. return window.innerWidth < 350 ? "350px" : "350px";
  5506. },
  5507. get height() {
  5508. return window.innerHeight < 250 ? "250px" : "250px";
  5509. }
  5510. }
  5511. };
  5512. const UITextArea = function(text, key, defaultValue, description, changeCallBack, placeholder = "", disabled) {
  5513. let result = {
  5514. text,
  5515. type: "textarea",
  5516. attributes: {},
  5517. props: {},
  5518. description,
  5519. placeholder,
  5520. disabled,
  5521. getValue() {
  5522. return this.props[PROPS_STORAGE_API].get(key, defaultValue);
  5523. },
  5524. callback(event, value) {
  5525. this.props[PROPS_STORAGE_API].set(key, value);
  5526. }
  5527. };
  5528. Reflect.set(result.attributes, ATTRIBUTE_KEY, key);
  5529. Reflect.set(result.attributes, ATTRIBUTE_DEFAULT_VALUE, defaultValue);
  5530. Reflect.set(result.props, PROPS_STORAGE_API, {
  5531. get(key2, defaultValue2) {
  5532. return PopsPanel.getValue(key2, defaultValue2);
  5533. },
  5534. set(key2, value) {
  5535. PopsPanel.setValue(key2, value);
  5536. }
  5537. });
  5538. return result;
  5539. };
  5540. const DouYinVideoFilter = {
  5541. $key: {
  5542. STORAGE_KEY: "dy-video-filter-rule",
  5543. ENABLE_KEY: "shieldVideo-exec-network-enable"
  5544. },
  5545. $data: {
  5546. /** 已经过滤的信息 */
  5547. isFilterAwemeInfoList: new Utils.Dictionary(),
  5548. /**
  5549. * 当命中过滤规则,如果开启了仅显示被过滤的视频,则修改isFilter值
  5550. */
  5551. get isReverse() {
  5552. return PopsPanel.getValue(
  5553. "shieldVideo-only-show-filtered-video"
  5554. );
  5555. }
  5556. },
  5557. init() {
  5558. if (DouYinRouter.isLive()) {
  5559. PopsPanel.deleteExecMenuOnce(this.$key.ENABLE_KEY);
  5560. return;
  5561. }
  5562. this.execFilter();
  5563. PopsPanel.execMenuOnce("shieldVideo-add-parseVideoInfoButton", () => {
  5564. this.addParseButton();
  5565. });
  5566. },
  5567. /**
  5568. * 执行过滤
  5569. */
  5570. execFilter() {
  5571. const that = this;
  5572. PopsPanel.execMenuOnce(this.$key.ENABLE_KEY, async () => {
  5573. log.info(`执行视频过滤器`);
  5574. let filterBase = new DouYinVideoFilterBase();
  5575. let queryScopeFilterOptionList = (scopeName) => {
  5576. if (!PopsPanel.getValue(that.$key.ENABLE_KEY)) {
  5577. return [];
  5578. }
  5579. let filterOptionList = that.getData();
  5580. if (!filterOptionList.length) {
  5581. return [];
  5582. }
  5583. let scopeNameList = Array.isArray(scopeName) ? scopeName : [scopeName];
  5584. let matchedFilterOptionList = filterOptionList.filter(
  5585. (it) => it.enable && (it.data.scope.includes("all") || Array.from(scopeNameList).findIndex(
  5586. (item) => it.data.scope.includes(
  5587. item
  5588. )
  5589. ) !== -1)
  5590. );
  5591. return matchedFilterOptionList;
  5592. };
  5593. let isFilterCallBack = (filterResult) => {
  5594. if (that.$data.isReverse) {
  5595. filterResult.isFilter = !filterResult.isFilter;
  5596. if (typeof filterResult.transformAwemeInfo.awemeId === "string" && filterResult.matchedFilterOption) {
  5597. let filterOptionList = that.$data.isFilterAwemeInfoList.get(
  5598. filterResult.transformAwemeInfo.awemeId
  5599. ) || [];
  5600. filterOptionList.push(filterResult.matchedFilterOption);
  5601. that.$data.isFilterAwemeInfoList.set(
  5602. filterResult.transformAwemeInfo.awemeId,
  5603. filterOptionList
  5604. );
  5605. }
  5606. }
  5607. };
  5608. let xhr_hook_callback_1 = (scopeName, request) => {
  5609. request.response = (response) => {
  5610. let filterOptionList = queryScopeFilterOptionList(scopeName);
  5611. if (!filterOptionList.length) {
  5612. return;
  5613. }
  5614. let data = utils.toJSON(response.responseText);
  5615. let aweme_list = data["aweme_list"];
  5616. if (Array.isArray(aweme_list)) {
  5617. for (let index = 0; index < aweme_list.length; index++) {
  5618. let awemeInfo = aweme_list[index] || {};
  5619. let filterResult = filterBase.checkAwemeInfoIsFilter(
  5620. filterOptionList,
  5621. awemeInfo
  5622. );
  5623. isFilterCallBack(filterResult);
  5624. if (filterResult.isFilter) {
  5625. filterBase.sendDislikeVideo(
  5626. filterResult.matchedFilterOption,
  5627. awemeInfo
  5628. );
  5629. filterBase.removeAweme(aweme_list, index--);
  5630. }
  5631. }
  5632. response.responseText = JSON.stringify(data);
  5633. }
  5634. };
  5635. };
  5636. let xhr_hook_callback_2 = (scopeName, request) => {
  5637. request.response = (response) => {
  5638. let filterOptionList = queryScopeFilterOptionList(scopeName);
  5639. if (!filterOptionList.length) {
  5640. return;
  5641. }
  5642. let data = utils.toJSON(response.responseText);
  5643. let aweme_list = data["data"];
  5644. if (Array.isArray(aweme_list)) {
  5645. for (let index = 0; index < aweme_list.length; index++) {
  5646. let awemeItem = aweme_list[index];
  5647. let awemeInfo = awemeItem["aweme"] || {};
  5648. if (typeof (awemeItem == null ? void 0 : awemeItem["cell_room"]) === "object" && (awemeItem == null ? void 0 : awemeItem["cell_room"]) != null) {
  5649. awemeInfo["cell_room"] = awemeItem == null ? void 0 : awemeItem["cell_room"];
  5650. }
  5651. let filterResult = filterBase.checkAwemeInfoIsFilter(
  5652. filterOptionList,
  5653. awemeInfo
  5654. );
  5655. isFilterCallBack(filterResult);
  5656. if (filterResult.isFilter) {
  5657. filterBase.sendDislikeVideo(
  5658. filterResult.matchedFilterOption,
  5659. awemeInfo
  5660. );
  5661. filterBase.removeAweme(aweme_list, index--);
  5662. }
  5663. }
  5664. response.responseText = JSON.stringify(data);
  5665. }
  5666. };
  5667. };
  5668. let xhr_hook_callback_3 = (scopeName, request) => {
  5669. request.response = (response) => {
  5670. let filterOptionList = queryScopeFilterOptionList(scopeName);
  5671. if (!filterOptionList.length) {
  5672. return;
  5673. }
  5674. let data = utils.toJSON(response.responseText);
  5675. let cards = data["cards"];
  5676. if (Array.isArray(cards)) {
  5677. for (let index = 0; index < cards.length; index++) {
  5678. let awemeItem = cards[index];
  5679. let awemeInfo = utils.toJSON((awemeItem == null ? void 0 : awemeItem["aweme"]) || "{}");
  5680. let filterResult = filterBase.checkAwemeInfoIsFilter(
  5681. filterOptionList,
  5682. awemeInfo
  5683. );
  5684. isFilterCallBack(filterResult);
  5685. if (filterResult.isFilter) {
  5686. filterBase.sendDislikeVideo(
  5687. filterResult.matchedFilterOption,
  5688. awemeInfo
  5689. );
  5690. filterBase.removeAweme(cards, index--);
  5691. }
  5692. }
  5693. response.responseText = JSON.stringify(data);
  5694. }
  5695. };
  5696. };
  5697. let xhr_hook_callback_4 = (scopeName, request) => {
  5698. request.response = (response) => {
  5699. let filterOptionList = queryScopeFilterOptionList(scopeName);
  5700. if (!filterOptionList.length) {
  5701. return;
  5702. }
  5703. let data = utils.toJSON(response.responseText);
  5704. let aweme_list = data["data"];
  5705. if (Array.isArray(aweme_list)) {
  5706. for (let index = 0; index < aweme_list.length; index++) {
  5707. let awemeItem = aweme_list[index];
  5708. let awemeInfo = awemeItem["aweme_info"] || {};
  5709. let awemeMixInfo = awemeItem == null ? void 0 : awemeItem["aweme_mix_info"];
  5710. if (awemeInfo == null && typeof awemeMixInfo && awemeMixInfo != null) {
  5711. let awemeMixInfoItems = awemeMixInfo == null ? void 0 : awemeMixInfo["mix_items"];
  5712. if (Array.isArray(awemeMixInfoItems)) {
  5713. for (let mixIndex = 0; mixIndex < awemeMixInfoItems.length; mixIndex++) {
  5714. let mixItem = awemeMixInfoItems[mixIndex];
  5715. let filterResult = filterBase.checkAwemeInfoIsFilter(
  5716. filterOptionList,
  5717. mixItem
  5718. );
  5719. isFilterCallBack(filterResult);
  5720. if (filterResult.isFilter) {
  5721. filterBase.sendDislikeVideo(
  5722. filterResult.matchedFilterOption,
  5723. mixItem
  5724. );
  5725. filterBase.removeAweme(awemeMixInfoItems, mixIndex--);
  5726. }
  5727. }
  5728. if (awemeMixInfoItems.length === 0) {
  5729. filterBase.removeAweme(aweme_list, index--);
  5730. }
  5731. }
  5732. } else {
  5733. let filterResult = filterBase.checkAwemeInfoIsFilter(
  5734. filterOptionList,
  5735. awemeInfo
  5736. );
  5737. isFilterCallBack(filterResult);
  5738. if (filterResult.isFilter) {
  5739. filterBase.sendDislikeVideo(
  5740. filterResult.matchedFilterOption,
  5741. awemeInfo
  5742. );
  5743. filterBase.removeAweme(aweme_list, index--);
  5744. }
  5745. }
  5746. }
  5747. response.responseText = JSON.stringify(data);
  5748. }
  5749. };
  5750. };
  5751. DouYinNetWorkHook.ajaxHooker.hook((request) => {
  5752. let url = CommonUtil.fixUrl(request.url);
  5753. let urlInstance = new URL(url);
  5754. if (urlInstance.pathname.startsWith("/aweme/v1/web/tab/feed")) {
  5755. xhr_hook_callback_1("xhr-tab", request);
  5756. } else if (urlInstance.pathname.startsWith("/aweme/v1/web/aweme/post/")) {
  5757. xhr_hook_callback_1("xhr-userHome", request);
  5758. } else if (urlInstance.pathname.startsWith("/aweme/v1/web/mix/aweme/")) {
  5759. xhr_hook_callback_1("xhr-mix", request);
  5760. } else if (urlInstance.pathname.startsWith("/aweme/v1/web/aweme/related/")) {
  5761. xhr_hook_callback_1("xhr-related", request);
  5762. } else if (urlInstance.pathname.startsWith("/aweme/v1/web/follow/feed")) {
  5763. xhr_hook_callback_2("xhr-follow", request);
  5764. } else if (urlInstance.pathname.startsWith("/aweme/v1/web/familiar/feed")) {
  5765. xhr_hook_callback_2("xhr-familiar", request);
  5766. } else if (urlInstance.pathname.startsWith("/aweme/v1/web/module/feed")) {
  5767. xhr_hook_callback_3("xhr-module", request);
  5768. } else if (urlInstance.pathname.startsWith(
  5769. "/aweme/v1/web/general/search/single/"
  5770. )) {
  5771. xhr_hook_callback_4("xhr-search", request);
  5772. } else if (urlInstance.pathname.startsWith("/aweme/v1/web/search/item/")) {
  5773. xhr_hook_callback_4("xhr-search", request);
  5774. }
  5775. });
  5776. });
  5777. },
  5778. /**
  5779. * 添加解析按钮
  5780. */
  5781. addParseButton() {
  5782. addStyle(
  5783. /*css*/
  5784. `
  5785. .basePlayerContainer .gm-video-filter-parse-btn{
  5786. margin-left: 4px;
  5787. }
  5788. .basePlayerContainer .gm-video-filter-parse-btn .semi-icon{
  5789. display: flex;
  5790. justify-content: center;
  5791. align-items: center;
  5792. }
  5793. .basePlayerContainer .gm-video-filter-parse-btn .semi-icon svg{
  5794. }
  5795. /* 修复搜索结果单列页面 解析按钮的高度错位 */
  5796. .searchControl33px .xg-right-grid xg-icon.gm-video-filter-parse-btn span svg{
  5797. transform: translateY(-6px) !important;
  5798. }
  5799.  
  5800. `
  5801. );
  5802. let filterBase = new DouYinVideoFilterBase();
  5803. let awemeInfoClickCallBack = ($basePlayerContainer) => {
  5804. var _a2, _b, _c, _d, _e, _f;
  5805. let that = this;
  5806. let reactFiber = (_a2 = utils.getReactObj($basePlayerContainer)) == null ? void 0 : _a2.reactFiber;
  5807. let awemeInfo = ((_c = (_b = reactFiber == null ? void 0 : reactFiber.return) == null ? void 0 : _b.memoizedProps) == null ? void 0 : _c.awemeInfo) || ((_f = (_e = (_d = reactFiber == null ? void 0 : reactFiber.return) == null ? void 0 : _d.return) == null ? void 0 : _e.memoizedProps) == null ? void 0 : _f.awemeInfo);
  5808. if (awemeInfo == null) {
  5809. Qmsg.error("未获取到awemeInfo信息", { consoleLogContent: true });
  5810. return;
  5811. }
  5812. if (typeof awemeInfo !== "object") {
  5813. Qmsg.error("获取到的awemeInfo信息不是对象", {
  5814. consoleLogContent: true
  5815. });
  5816. return;
  5817. }
  5818. let awemeInfoParsedData = filterBase.parseAwemeInfoDictData(
  5819. awemeInfo,
  5820. false
  5821. );
  5822. log.info(["视频awemeInfo:", awemeInfo, awemeInfoParsedData]);
  5823. let targetFilterOption = that.$data.isFilterAwemeInfoList.get(awemeInfoParsedData.awemeId) || [];
  5824. __pops.confirm({
  5825. title: {
  5826. text: "视频awemeInfo",
  5827. position: "center"
  5828. },
  5829. content: {
  5830. text: JSON.stringify(awemeInfoParsedData, null, 4).trim(),
  5831. html: false
  5832. },
  5833. drag: true,
  5834. btn: {
  5835. merge: targetFilterOption.length ? true : false,
  5836. position: targetFilterOption.length ? "space-between" : "flex-end",
  5837. ok: {
  5838. enable: true,
  5839. text: "添加过滤规则",
  5840. callback(eventDetails, event) {
  5841. let ruleView = that.getRuleViewInstance();
  5842. ruleView.showEditView(false, that.getTemplateData());
  5843. }
  5844. },
  5845. cancel: {
  5846. enable: true,
  5847. text: "规则管理器",
  5848. callback(eventDetails, event) {
  5849. that.showView();
  5850. }
  5851. },
  5852. other: {
  5853. enable: targetFilterOption.length ? true : false,
  5854. text: `命中的规则(${targetFilterOption.length})`,
  5855. type: "xiaomi-primary",
  5856. callback(eventDetails, event) {
  5857. that.getRuleViewInstance().showView((data) => {
  5858. let find = targetFilterOption.find((it) => {
  5859. return data.uuid === it.uuid;
  5860. });
  5861. return Boolean(find);
  5862. });
  5863. }
  5864. }
  5865. },
  5866. mask: {
  5867. enable: true,
  5868. clickEvent: {
  5869. toClose: true
  5870. }
  5871. },
  5872. width: PanelUISize.setting.width,
  5873. height: PanelUISize.setting.height,
  5874. style: (
  5875. /*css*/
  5876. `
  5877. .pops-confirm-content p{
  5878. white-space: break-spaces;
  5879. }
  5880. `
  5881. )
  5882. });
  5883. };
  5884. let lockFn = new utils.LockFunction(() => {
  5885. $$(
  5886. ".basePlayerContainer xg-right-grid:not(:has(.gm-video-filter-parse-btn))"
  5887. ).forEach(($xgRightGrid) => {
  5888. let $gmFilterParseBtn = domUtils.createElement("xg-icon", {
  5889. className: "gm-video-filter-parse-btn",
  5890. innerHTML: (
  5891. /*html*/
  5892. `
  5893. <div class="xgplayer-icon">
  5894. <span role="img" class="semi-icon semi-icon-default">
  5895. <svg
  5896. viewBox="0 0 32 32"
  5897. width="1em"
  5898. height="1em"
  5899. style="font-size: 32px"
  5900. xmlns="http://www.w3.org/2000/svg"
  5901. focusable="false"
  5902. fill="none">
  5903. <g>
  5904. <path
  5905. stroke="null"
  5906. fill="currentColor"
  5907. d="m9.78829,8.17117l1.77477,0l0,1.73974l-1.77477,0l0,4.34935a1.77477,1.73974 0 0 1 -1.77477,1.73974a1.77477,1.73974 0 0 1 1.77477,1.73974l0,4.34935l1.77477,0l0,1.73974l-1.77477,0c-0.9495,-0.23486 -1.77477,-0.78288 -1.77477,-1.73974l0,-3.47948a1.77477,1.73974 0 0 0 -1.77477,-1.73974l-0.88739,0l0,-1.73974l0.88739,0a1.77477,1.73974 0 0 0 1.77477,-1.73974l0,-3.47948a1.77477,1.73974 0 0 1 1.77477,-1.73974m12.42342,0a1.77477,1.73974 0 0 1 1.77477,1.73974l0,3.47948a1.77477,1.73974 0 0 0 1.77477,1.73974l0.88739,0l0,1.73974l-0.88739,0a1.77477,1.73974 0 0 0 -1.77477,1.73974l0,3.47948a1.77477,1.73974 0 0 1 -1.77477,1.73974l-1.77477,0l0,-1.73974l1.77477,0l0,-4.34935a1.77477,1.73974 0 0 1 1.77477,-1.73974a1.77477,1.73974 0 0 1 -1.77477,-1.73974l0,-4.34935l-1.77477,0l0,-1.73974l1.77477,0m-6.21171,10.43844a0.88739,0.86987 0 0 1 0.88739,0.86987a0.88739,0.86987 0 0 1 -0.88739,0.86987a0.88739,0.86987 0 0 1 -0.88739,-0.86987a0.88739,0.86987 0 0 1 0.88739,-0.86987m-3.54955,0a0.88739,0.86987 0 0 1 0.88739,0.86987a0.88739,0.86987 0 0 1 -0.88739,0.86987a0.88739,0.86987 0 0 1 -0.88739,-0.86987a0.88739,0.86987 0 0 1 0.88739,-0.86987m7.0991,0a0.88739,0.86987 0 0 1 0.88739,0.86987a0.88739,0.86987 0 0 1 -0.88739,0.86987a0.88739,0.86987 0 0 1 -0.88739,-0.86987a0.88739,0.86987 0 0 1 0.88739,-0.86987z"
  5908. clip-rule="evenodd"
  5909. fill-rule="evenodd" />
  5910. </g>
  5911. </svg>
  5912. </span>
  5913. </div>
  5914. <div class="xg-tips">解析信息</div>
  5915. `
  5916. )
  5917. });
  5918. domUtils.on($gmFilterParseBtn, "click", (event) => {
  5919. utils.preventEvent(event);
  5920. let $basePlayerContainer = $xgRightGrid.closest(
  5921. ".basePlayerContainer"
  5922. );
  5923. awemeInfoClickCallBack($basePlayerContainer);
  5924. });
  5925. domUtils.prepend($xgRightGrid, $gmFilterParseBtn);
  5926. });
  5927. });
  5928. utils.mutationObserver(document, {
  5929. config: {
  5930. subtree: true,
  5931. childList: true
  5932. },
  5933. immediate: true,
  5934. callback: () => {
  5935. lockFn.run();
  5936. }
  5937. });
  5938. },
  5939. /**
  5940. * 获取规则视图实例
  5941. */
  5942. getRuleViewInstance() {
  5943. let popsPanelContentUtils = __pops.config.panelHandleContentUtils();
  5944. function generateStorageApi(data) {
  5945. return {
  5946. get(key, defaultValue) {
  5947. return data[key] ?? defaultValue;
  5948. },
  5949. set(key, value) {
  5950. data[key] = value;
  5951. }
  5952. };
  5953. }
  5954. let ruleView = new RuleView({
  5955. title: "视频过滤器",
  5956. data: () => {
  5957. return this.getData();
  5958. },
  5959. getAddData: () => {
  5960. return this.getTemplateData();
  5961. },
  5962. getDataItemName: (data) => {
  5963. return data["name"];
  5964. },
  5965. updateData: (data) => {
  5966. return this.updateData(data);
  5967. },
  5968. deleteData: (data) => {
  5969. return this.deleteData(data);
  5970. },
  5971. getData: (data) => {
  5972. let allData = this.getData();
  5973. let findValue = allData.find((item) => item.uuid === data.uuid);
  5974. return findValue ?? data;
  5975. },
  5976. itemControls: {
  5977. enable: {
  5978. enable: true,
  5979. getEnable(data) {
  5980. return data.enable;
  5981. },
  5982. callback: (data, enable) => {
  5983. data.enable = enable;
  5984. this.updateData(data);
  5985. }
  5986. },
  5987. edit: {
  5988. enable: true,
  5989. getView: (data, isEdit) => {
  5990. let $fragment = document.createDocumentFragment();
  5991. if (!isEdit) {
  5992. data = this.getTemplateData();
  5993. }
  5994. let enable_template = UISwitch("启用", "enable", true);
  5995. Reflect.set(
  5996. enable_template.props,
  5997. PROPS_STORAGE_API,
  5998. generateStorageApi(data)
  5999. );
  6000. let $enable = popsPanelContentUtils.createSectionContainerItem_switch(
  6001. enable_template
  6002. );
  6003. let name_template = UIInput(
  6004. "规则名称",
  6005. "name",
  6006. "",
  6007. "",
  6008. void 0,
  6009. "必填"
  6010. );
  6011. Reflect.set(
  6012. name_template.props,
  6013. PROPS_STORAGE_API,
  6014. generateStorageApi(data)
  6015. );
  6016. let $name = popsPanelContentUtils.createSectionContainerItem_input(
  6017. name_template
  6018. );
  6019. let scope_template = UISelectMultiple(
  6020. "作用域",
  6021. "scope",
  6022. [],
  6023. [
  6024. {
  6025. text: "所有",
  6026. value: "all"
  6027. },
  6028. {
  6029. text: "精选",
  6030. value: "xhr-module"
  6031. },
  6032. {
  6033. text: "推荐",
  6034. value: "xhr-tab"
  6035. },
  6036. {
  6037. text: "关注",
  6038. value: "xhr-follow"
  6039. },
  6040. {
  6041. text: "朋友",
  6042. value: "xhr-familiar"
  6043. },
  6044. {
  6045. text: "搜索",
  6046. value: "xhr-search"
  6047. },
  6048. {
  6049. text: "用户主页",
  6050. value: "xhr-userHome"
  6051. },
  6052. {
  6053. text: "混合信息",
  6054. value: "xhr-mix"
  6055. },
  6056. {
  6057. text: "相关推荐",
  6058. value: "xhr-related"
  6059. }
  6060. ],
  6061. void 0,
  6062. "选择需要在xxx上生效的作用域"
  6063. );
  6064. Reflect.set(
  6065. scope_template.props,
  6066. PROPS_STORAGE_API,
  6067. generateStorageApi(data.data)
  6068. );
  6069. let $scope = popsPanelContentUtils.createSectionContainerItem_select_multiple_new(
  6070. scope_template
  6071. );
  6072. let douYinVideoHandlerInfoKey = [
  6073. "isLive",
  6074. "isAds",
  6075. "isSeriesInfo",
  6076. "isMixInfo",
  6077. "isPicture",
  6078. "awemeId",
  6079. "nickname",
  6080. "uid",
  6081. "desc",
  6082. "textExtra",
  6083. "videoTag",
  6084. "videoTagId",
  6085. "musicAlbum",
  6086. "musicAuthor",
  6087. "musicTitle",
  6088. "riskInfoContent",
  6089. "seriesInfoName",
  6090. "seriesInfoContentTypes",
  6091. "mixInfoName",
  6092. "mixInfoDesc",
  6093. "collectCount",
  6094. "commentCount",
  6095. "diggCount",
  6096. "shareCount",
  6097. "duration"
  6098. ];
  6099. let ruleNameDefaultValue = "nickname";
  6100. let getDynamicProp = (storageData) => {
  6101. let ruleName_template = UISelect(
  6102. "属性名",
  6103. "ruleName",
  6104. ruleNameDefaultValue,
  6105. douYinVideoHandlerInfoKey.map((item) => {
  6106. return {
  6107. text: item,
  6108. value: item
  6109. };
  6110. }),
  6111. void 0,
  6112. "选择需要的属性名 "
  6113. );
  6114. Reflect.set(
  6115. ruleName_template.props,
  6116. PROPS_STORAGE_API,
  6117. generateStorageApi(storageData)
  6118. );
  6119. let $ruleName2 = popsPanelContentUtils.createSectionContainerItem_select(
  6120. ruleName_template
  6121. );
  6122. let ruleValue_template = UITextArea(
  6123. "属性值",
  6124. "ruleValue",
  6125. "",
  6126. "如果是字符串,可正则,注意转义"
  6127. );
  6128. Reflect.set(
  6129. ruleValue_template.props,
  6130. PROPS_STORAGE_API,
  6131. generateStorageApi(storageData)
  6132. );
  6133. let $ruleValue2 = popsPanelContentUtils.createSectionContainerItem_textarea(
  6134. ruleValue_template
  6135. );
  6136. let remarks_template = UITextArea(
  6137. "备注",
  6138. "remarks",
  6139. "",
  6140. ""
  6141. );
  6142. Reflect.set(
  6143. remarks_template.props,
  6144. PROPS_STORAGE_API,
  6145. generateStorageApi(storageData)
  6146. );
  6147. let $remarks2 = popsPanelContentUtils.createSectionContainerItem_textarea(
  6148. remarks_template
  6149. );
  6150. return {
  6151. $ruleName: $ruleName2,
  6152. $ruleValue: $ruleValue2,
  6153. $remarks: $remarks2
  6154. };
  6155. };
  6156. let $dynamicContainer = domUtils.createElement("div", {
  6157. className: "rule-form-ulist-dynamic",
  6158. innerHTML: (
  6159. /*html*/
  6160. `
  6161. <div class="rule-form-ulist-dynamic__inner">
  6162.  
  6163. </div>
  6164. <div class="pops-panel-button pops-panel-button-no-icon">
  6165. <button class="pops-panel-button_inner" type="default">
  6166. <i class="pops-bottom-icon" is-loading="false"></i>
  6167. <span class="pops-panel-button-text">添加额外属性</span>
  6168. </button>
  6169. </div>
  6170. `
  6171. )
  6172. });
  6173. let $dynamicInner = $dynamicContainer.querySelector(
  6174. ".rule-form-ulist-dynamic__inner"
  6175. );
  6176. let $addDynamicButton = $dynamicContainer.querySelector(
  6177. ".pops-panel-button"
  6178. );
  6179. let addDynamicElementItem = (dynamicData = {
  6180. ruleName: ruleNameDefaultValue,
  6181. ruleValue: "",
  6182. remarks: ""
  6183. }) => {
  6184. let $dynamicUListContainer = domUtils.createElement("div", {
  6185. className: "rule-form-ulist-dynamic__inner-container",
  6186. innerHTML: (
  6187. /*html*/
  6188. `
  6189. <div class="dynamic-control-delete">
  6190. <div class="pops-panel-button pops-panel-button-no-icon">
  6191. <button class="pops-panel-button_inner" type="danger">
  6192. <i class="pops-bottom-icon" is-loading="false"></i>
  6193. <span class="pops-panel-button-text">×</span>
  6194. </button>
  6195. </div>
  6196. </div>
  6197. <ul class="dynamic-forms">
  6198.  
  6199. </ul>
  6200. `
  6201. )
  6202. });
  6203. let $dynamicDelete = $dynamicUListContainer.querySelector(
  6204. ".dynamic-control-delete"
  6205. );
  6206. domUtils.on($dynamicDelete, "click", (event) => {
  6207. utils.preventEvent(event);
  6208. $dynamicUListContainer.remove();
  6209. if (Array.isArray(data.dynamicData)) {
  6210. let findIndex = data.dynamicData.findIndex(
  6211. (it) => it == dynamicData
  6212. );
  6213. if (findIndex !== -1) {
  6214. data.dynamicData.splice(findIndex, 1);
  6215. }
  6216. }
  6217. });
  6218. let $dynamicUList = $dynamicUListContainer.querySelector(
  6219. ".dynamic-forms"
  6220. );
  6221. let {
  6222. $ruleName: $dynamic_ruleName,
  6223. $ruleValue: $dynamic_ruleValue,
  6224. $remarks: $dynamic_remarks
  6225. } = getDynamicProp(dynamicData);
  6226. $dynamicUList.appendChild($dynamic_ruleName);
  6227. $dynamicUList.appendChild($dynamic_ruleValue);
  6228. $dynamicUList.appendChild($dynamic_remarks);
  6229. $dynamicInner.appendChild($dynamicUListContainer);
  6230. };
  6231. domUtils.on($addDynamicButton, "click", (event) => {
  6232. utils.preventEvent(event);
  6233. addDynamicElementItem();
  6234. });
  6235. if (Array.isArray(data.dynamicData)) {
  6236. for (let index = 0; index < data.dynamicData.length; index++) {
  6237. const moreDataItem = data.dynamicData[index];
  6238. addDynamicElementItem(moreDataItem);
  6239. }
  6240. }
  6241. let { $ruleName, $ruleValue, $remarks } = getDynamicProp(data.data);
  6242. $fragment.append(
  6243. $enable,
  6244. $name,
  6245. $scope,
  6246. // $autoSendDisLikeRequest,
  6247. $ruleName,
  6248. $ruleValue,
  6249. $remarks,
  6250. $dynamicContainer
  6251. );
  6252. return $fragment;
  6253. },
  6254. onsubmit: ($form, isEdit, editData) => {
  6255. let $ulist_li = $form.querySelectorAll(
  6256. ".rule-form-ulist > li"
  6257. );
  6258. let data = this.getTemplateData();
  6259. if (isEdit) {
  6260. data.uuid = editData.uuid;
  6261. }
  6262. $ulist_li.forEach(($li) => {
  6263. let formConfig = Reflect.get($li, "__formConfig__");
  6264. if (!formConfig) {
  6265. return;
  6266. }
  6267. let attrs = Reflect.get(formConfig, "attributes");
  6268. if (!attrs) {
  6269. return;
  6270. }
  6271. let storageApi = Reflect.get($li, PROPS_STORAGE_API);
  6272. let key = Reflect.get(attrs, ATTRIBUTE_KEY);
  6273. let defaultValue = Reflect.get(attrs, ATTRIBUTE_DEFAULT_VALUE);
  6274. let value = storageApi.get(key, defaultValue);
  6275. if (Reflect.has(data, key)) {
  6276. Reflect.set(data, key, value);
  6277. } else if (Reflect.has(data["data"], key)) {
  6278. Reflect.set(data["data"], key, value);
  6279. } else {
  6280. log.error(`${key}不在数据中`);
  6281. }
  6282. });
  6283. $form.querySelectorAll(
  6284. ".rule-form-ulist-dynamic__inner-container"
  6285. ).forEach(($inner) => {
  6286. let dynamicData = {};
  6287. $inner.querySelectorAll(".dynamic-forms > li").forEach(($li) => {
  6288. let formConfig = Reflect.get($li, "__formConfig__");
  6289. if (!formConfig) {
  6290. return;
  6291. }
  6292. let attrs = Reflect.get(formConfig, "attributes");
  6293. if (!attrs) {
  6294. return;
  6295. }
  6296. let storageApi = Reflect.get($li, PROPS_STORAGE_API);
  6297. let key = Reflect.get(attrs, ATTRIBUTE_KEY);
  6298. let defaultValue = Reflect.get(
  6299. attrs,
  6300. ATTRIBUTE_DEFAULT_VALUE
  6301. );
  6302. let value = storageApi.get(key, defaultValue);
  6303. Reflect.set(dynamicData, key, value);
  6304. });
  6305. data.dynamicData.push(dynamicData);
  6306. });
  6307. if (data.name.trim() === "") {
  6308. Qmsg.error("规则名称不能为空");
  6309. return {
  6310. success: false,
  6311. data
  6312. };
  6313. }
  6314. if (data.data.scope.length === 0) {
  6315. Qmsg.error("作用域不能为空");
  6316. return {
  6317. success: false,
  6318. data
  6319. };
  6320. }
  6321. if (data.data.ruleName.trim() === "") {
  6322. Qmsg.error("请选择属性名");
  6323. return {
  6324. success: false,
  6325. data
  6326. };
  6327. }
  6328. if (data.data.ruleValue.trim() === "") {
  6329. Qmsg.error("属性值不能为空");
  6330. return {
  6331. success: false,
  6332. data
  6333. };
  6334. }
  6335. if (isEdit) {
  6336. return {
  6337. success: this.updateData(data),
  6338. data
  6339. };
  6340. } else {
  6341. return {
  6342. success: this.addData(data),
  6343. data
  6344. };
  6345. }
  6346. },
  6347. style: (
  6348. /*css*/
  6349. `
  6350. .pops-panel-textarea textarea{
  6351. height: 150px;
  6352. }
  6353. .pops-panel-item-left-desc-text{
  6354. line-height: normal;
  6355. margin-top: 6px;
  6356. font-size: 0.8em;
  6357. color: rgb(108, 108, 108);
  6358. }
  6359. .rule-form-ulist-dynamic{
  6360. --button-margin-top: 0px;
  6361. --button-margin-right: 0px;
  6362. --button-margin-bottom: 0px;
  6363. --button-margin-left: 0px;
  6364. display: flex;
  6365. flex-direction: column;
  6366. align-items: flex-start;
  6367. padding: 5px 20px;
  6368. }
  6369. .rule-form-ulist-dynamic__inner{
  6370. width: 100%;
  6371. }
  6372. .rule-form-ulist-dynamic__inner-container{
  6373. display: flex;
  6374. align-items: center;
  6375. }
  6376. .dynamic-forms{
  6377. width: 100%;
  6378. }
  6379. .pops-panel-textarea textarea{
  6380. height: 60px;
  6381. min-height: 60px;
  6382. width: 250px;
  6383. max-width: 400px;
  6384. min-width: 250px;
  6385. resize: auto;
  6386. transition: unset;
  6387. }
  6388. `
  6389. ),
  6390. width: () => {
  6391. return window.innerWidth > 700 ? "700px" : "88vw";
  6392. }
  6393. },
  6394. delete: {
  6395. enable: true,
  6396. deleteCallBack: (data) => {
  6397. return this.deleteData(data);
  6398. }
  6399. }
  6400. },
  6401. bottomControls: {
  6402. filter: {
  6403. enable: true,
  6404. option: [
  6405. {
  6406. name: "过滤-已启用",
  6407. filterCallBack(data) {
  6408. return data.enable;
  6409. }
  6410. },
  6411. {
  6412. name: "过滤-未启用",
  6413. filterCallBack(data) {
  6414. return !data.enable;
  6415. }
  6416. }
  6417. ]
  6418. }
  6419. }
  6420. });
  6421. return ruleView;
  6422. },
  6423. /**
  6424. * 显示视图
  6425. */
  6426. showView() {
  6427. let ruleView = this.getRuleViewInstance();
  6428. ruleView.showView();
  6429. },
  6430. /**
  6431. * 获取模板数据
  6432. */
  6433. getTemplateData() {
  6434. return {
  6435. uuid: utils.generateUUID(),
  6436. enable: true,
  6437. name: "",
  6438. data: {
  6439. scope: [],
  6440. // autoSendDisLikeRequest: false,
  6441. ruleName: "nickname",
  6442. ruleValue: "",
  6443. remarks: ""
  6444. },
  6445. dynamicData: []
  6446. };
  6447. },
  6448. /**
  6449. * 获取数据
  6450. */
  6451. getData() {
  6452. return _GM_getValue(this.$key.STORAGE_KEY, []);
  6453. },
  6454. /**
  6455. * 设置数据
  6456. * @param data
  6457. */
  6458. setData(data) {
  6459. _GM_setValue(this.$key.STORAGE_KEY, data);
  6460. },
  6461. /**
  6462. * 添加数据
  6463. * @param data
  6464. */
  6465. addData(data) {
  6466. let localData = this.getData();
  6467. let findIndex = localData.findIndex((item) => item.uuid == data.uuid);
  6468. if (findIndex === -1) {
  6469. localData.push(data);
  6470. _GM_setValue(this.$key.STORAGE_KEY, localData);
  6471. return true;
  6472. } else {
  6473. return false;
  6474. }
  6475. },
  6476. /**
  6477. * 更新数据
  6478. * @param data
  6479. */
  6480. updateData(data) {
  6481. let localData = this.getData();
  6482. let index = localData.findIndex((item) => item.uuid == data.uuid);
  6483. let updateFlag = false;
  6484. if (index !== -1) {
  6485. updateFlag = true;
  6486. localData[index] = data;
  6487. }
  6488. this.setData(localData);
  6489. return updateFlag;
  6490. },
  6491. /**
  6492. * 删除数据
  6493. * @param data
  6494. */
  6495. deleteData(data) {
  6496. let localData = this.getData();
  6497. let index = localData.findIndex((item) => item.uuid == data.uuid);
  6498. let deleteFlag = false;
  6499. if (index !== -1) {
  6500. deleteFlag = true;
  6501. localData.splice(index, 1);
  6502. }
  6503. this.setData(localData);
  6504. return deleteFlag;
  6505. },
  6506. /**
  6507. * 清空数据
  6508. */
  6509. clearData() {
  6510. _GM_deleteValue(this.$key.STORAGE_KEY);
  6511. },
  6512. /**
  6513. * 导出规则
  6514. */
  6515. exportRule(fileName = "rule.json") {
  6516. let allRule = this.getData();
  6517. let blob = new Blob([JSON.stringify(allRule, null, 4)]);
  6518. let blobUrl = window.URL.createObjectURL(blob);
  6519. let $a = document.createElement("a");
  6520. $a.href = blobUrl;
  6521. $a.download = fileName;
  6522. $a.click();
  6523. setTimeout(() => {
  6524. window.URL.revokeObjectURL(blobUrl);
  6525. }, 1500);
  6526. },
  6527. /**
  6528. * 导入规则
  6529. */
  6530. importRule() {
  6531. let $alert = __pops.alert({
  6532. title: {
  6533. text: "请选择导入方式",
  6534. position: "center"
  6535. },
  6536. content: {
  6537. text: (
  6538. /*html*/
  6539. `
  6540. <div class="import-mode" data-mode="local">本地导入</div>
  6541. <div class="import-mode" data-mode="network">网络导入</div>
  6542. `
  6543. ),
  6544. html: true
  6545. },
  6546. width: PanelUISize.info.width,
  6547. height: PanelUISize.info.height,
  6548. style: (
  6549. /*css*/
  6550. `
  6551. .import-mode{
  6552. display: inline-block;
  6553. margin: 10px;
  6554. padding: 10px;
  6555. border: 1px solid #ccc;
  6556. border-radius: 5px;
  6557. cursor: pointer;
  6558. }
  6559. `
  6560. )
  6561. });
  6562. let $local = $alert.$shadowRoot.querySelector(
  6563. ".import-mode[data-mode='local']"
  6564. );
  6565. let $network = $alert.$shadowRoot.querySelector(
  6566. ".import-mode[data-mode='network']"
  6567. );
  6568. domUtils.on($local, "click", (event) => {
  6569. utils.preventEvent(event);
  6570. $alert.close();
  6571. let $input = domUtils.createElement("input", {
  6572. type: "file",
  6573. accept: ".json"
  6574. });
  6575. domUtils.on($input, ["propertychange", "input"], (event2) => {
  6576. var _a2;
  6577. if (!((_a2 = $input.files) == null ? void 0 : _a2.length)) {
  6578. return;
  6579. }
  6580. let uploadFile = $input.files[0];
  6581. let fileReader = new FileReader();
  6582. fileReader.onload = () => {
  6583. let data = utils.toJSON(fileReader.result);
  6584. if (!Array.isArray(data)) {
  6585. log.error("不是正确的规则文件", data);
  6586. Qmsg.error("不是正确的规则文件");
  6587. return;
  6588. }
  6589. this.setData(data);
  6590. Qmsg.success(`成功导入 ${data.length}条规则`);
  6591. };
  6592. fileReader.readAsText(uploadFile, "UTF-8");
  6593. });
  6594. $input.click();
  6595. });
  6596. domUtils.on($network, "click", (event) => {
  6597. utils.preventEvent(event);
  6598. $alert.close();
  6599. __pops.prompt({
  6600. title: {
  6601. text: "网络导入",
  6602. position: "center"
  6603. },
  6604. content: {
  6605. text: "",
  6606. placeholder: "url",
  6607. focus: true
  6608. },
  6609. btn: {
  6610. ok: {
  6611. callback: async (eventDetails, event2) => {
  6612. let url = eventDetails.text;
  6613. if (utils.isNull(url)) {
  6614. Qmsg.error("请填入完整的url");
  6615. return;
  6616. }
  6617. let response = await httpx.get(url);
  6618. if (!response.status) {
  6619. return;
  6620. }
  6621. let data = utils.toJSON(response.data.responseText);
  6622. if (!Array.isArray(data)) {
  6623. log.error("不是正确的规则文件", response, data);
  6624. Qmsg.error("不是正确的规则文件");
  6625. return;
  6626. }
  6627. this.setData(data);
  6628. eventDetails.close();
  6629. Qmsg.success(`成功导入 ${data.length}条规则`);
  6630. }
  6631. }
  6632. },
  6633. width: PanelUISize.info.width,
  6634. height: "auto"
  6635. });
  6636. });
  6637. }
  6638. };
  6639. const PanelVideoConfig = {
  6640. id: "panel-config-video",
  6641. title: "视频",
  6642. forms: [
  6643. {
  6644. text: "",
  6645. type: "forms",
  6646. forms: [
  6647. {
  6648. text: "功能",
  6649. type: "deepMenu",
  6650. forms: [
  6651. {
  6652. text: "功能",
  6653. type: "forms",
  6654. forms: [
  6655. UISelect(
  6656. "清晰度",
  6657. "chooseVideoDefinition",
  6658. -2,
  6659. [
  6660. {
  6661. text: "超清 4K",
  6662. // ↓gearType
  6663. value: -2
  6664. },
  6665. {
  6666. text: "超清 2K",
  6667. value: -1
  6668. },
  6669. {
  6670. text: "高清 1080P",
  6671. value: 1
  6672. },
  6673. {
  6674. text: "高清 720P",
  6675. value: 2
  6676. },
  6677. {
  6678. text: "标清 540P",
  6679. value: 3
  6680. },
  6681. {
  6682. text: "极速",
  6683. value: 4
  6684. },
  6685. {
  6686. text: "智能",
  6687. value: 0
  6688. }
  6689. ],
  6690. void 0,
  6691. "自行选择清晰度"
  6692. ),
  6693. UISwitch(
  6694. "沉浸模式",
  6695. "fullScreen",
  6696. false,
  6697. void 0,
  6698. "移除右侧工具栏、底部信息栏等"
  6699. ),
  6700. UISwitch(
  6701. "手机模式",
  6702. "mobileMode",
  6703. false,
  6704. void 0,
  6705. "放大文字和图标,自动启用【initial-scale=1】和【修复进度条】功能"
  6706. ),
  6707. UISwitch(
  6708. "修复进度条",
  6709. "repairProgressBar",
  6710. false,
  6711. void 0,
  6712. "修复移动端不能点击拖拽和定位进度的问题(仅移动端使用)"
  6713. ),
  6714. UISwitch(
  6715. "禁用双击点赞",
  6716. "dy-video-disableDoubleClickLike",
  6717. false,
  6718. void 0,
  6719. "禁止视频区域双击点赞"
  6720. ),
  6721. UISwitch(
  6722. "手势返回关闭评论区",
  6723. "dy-video-gestureBackCloseComment",
  6724. false,
  6725. void 0,
  6726. "浏览器手势返回时关闭评论区"
  6727. ),
  6728. UISwitch(
  6729. "监听并关闭【长时间无操作,已暂停播放】弹窗",
  6730. "dy-video-waitToRemovePauseDialog",
  6731. true,
  6732. void 0,
  6733. "自动监听并检测弹窗"
  6734. ),
  6735. UISwitch(
  6736. "视频解析",
  6737. "parseVideo",
  6738. true,
  6739. void 0,
  6740. "分享->下载(灰色的也可点击)"
  6741. ),
  6742. UISwitch(
  6743. "评论区移到中间",
  6744. "changeCommentToBottom",
  6745. true,
  6746. void 0,
  6747. "修改评论区为中间弹出而非右侧区域"
  6748. ),
  6749. UISwitch(
  6750. "↑自适应评论区位置",
  6751. "douyin-video-autoCheckChangeCommentToBottom",
  6752. true,
  6753. void 0,
  6754. "根据window.screen.orientation.type自动判断是否开启【评论区移到中间】"
  6755. ),
  6756. UISwitch(
  6757. "自动进入网页全屏",
  6758. "autoEnterElementFullScreen",
  6759. false,
  6760. void 0,
  6761. "网页加载完毕后自动点击网页全屏按钮进入全屏"
  6762. ),
  6763. UISwitch(
  6764. "双击进入网页全屏",
  6765. "dy-video-doubleClickEnterElementFullScreen",
  6766. false,
  6767. void 0,
  6768. "双击视频自动进入网页全屏,检测间隔250ms"
  6769. ),
  6770. UISwitch(
  6771. "移除video的bottom偏移",
  6772. "dy-video-removeStyle-bottom",
  6773. false,
  6774. void 0,
  6775. ""
  6776. )
  6777. ]
  6778. },
  6779. {
  6780. text: "视频区域背景色",
  6781. type: "forms",
  6782. forms: [
  6783. UISwitch(
  6784. "启用",
  6785. "dy-video-bgColor-enable",
  6786. false,
  6787. void 0,
  6788. "自定义视频背景色"
  6789. ),
  6790. {
  6791. type: "own",
  6792. attributes: {
  6793. "data-key": "dy-video-changeBackgroundColor",
  6794. "data-default-value": "#000000"
  6795. },
  6796. getLiElementCallBack(liElement) {
  6797. let $left = domUtils.createElement("div", {
  6798. className: "pops-panel-item-left-text",
  6799. innerHTML: (
  6800. /*html*/
  6801. `
  6802. <p class="pops-panel-item-left-main-text">视频背景颜色</p>
  6803. <p class="pops-panel-item-left-desc-text">自定义视频背景颜色,包括评论区</p>
  6804. `
  6805. )
  6806. });
  6807. let $right = domUtils.createElement("div", {
  6808. className: "pops-panel-item-right",
  6809. innerHTML: (
  6810. /*html*/
  6811. `
  6812. <input type="color" class="pops-color-choose" />
  6813. `
  6814. )
  6815. });
  6816. let $color = $right.querySelector(
  6817. ".pops-color-choose"
  6818. );
  6819. $color.value = PopsPanel.getValue(
  6820. "dy-video-changeBackgroundColor"
  6821. );
  6822. let $style = domUtils.createElement("style");
  6823. domUtils.append(document.head, $style);
  6824. domUtils.on(
  6825. $color,
  6826. ["input", "propertychange"],
  6827. (event) => {
  6828. log.info("选择颜色:" + $color.value);
  6829. $style.innerHTML = /*css*/
  6830. `
  6831. #sliderVideo > div{
  6832. background: ${$color.value};
  6833. }
  6834. `;
  6835. PopsPanel.setValue(
  6836. "dy-video-changeBackgroundColor",
  6837. $color.value
  6838. );
  6839. }
  6840. );
  6841. liElement.appendChild($left);
  6842. liElement.appendChild($right);
  6843. return liElement;
  6844. }
  6845. }
  6846. ]
  6847. },
  6848. {
  6849. type: "forms",
  6850. text: "视频标题",
  6851. forms: [
  6852. UISwitch(
  6853. "自动隐藏视频标题",
  6854. "dy-video-titleInfoAutoHide",
  6855. false,
  6856. void 0,
  6857. "鼠标移入时自动显示,鼠标移除时自动隐藏"
  6858. ),
  6859. UISlider(
  6860. "延迟自动隐藏的时间",
  6861. "dy-video-titleInfoAutoHide-delayTime",
  6862. 3e3,
  6863. 0,
  6864. 8e3,
  6865. void 0,
  6866. (value) => {
  6867. return `${value}ms`;
  6868. },
  6869. "设置延迟自动隐藏视频标题的时间,单位(ms)",
  6870. 100
  6871. )
  6872. ]
  6873. },
  6874. {
  6875. type: "forms",
  6876. text: "底部的视频控件",
  6877. forms: [
  6878. UISwitch(
  6879. "自动隐藏视频控件",
  6880. "dy-video-videoControlsAutoHide",
  6881. false,
  6882. void 0,
  6883. "鼠标移入时自动显示,鼠标移除时自动隐藏"
  6884. ),
  6885. UISlider(
  6886. "延迟自动隐藏的时间",
  6887. "dy-video-videoControlsAutoHide-delayTime",
  6888. 3e3,
  6889. 0,
  6890. 8e3,
  6891. void 0,
  6892. (value) => {
  6893. return `${value}ms`;
  6894. },
  6895. "设置延迟自动隐藏视频标题的时间,单位(ms)",
  6896. 100
  6897. )
  6898. ]
  6899. },
  6900. {
  6901. type: "forms",
  6902. text: "右侧工具栏",
  6903. forms: [
  6904. UISwitch(
  6905. "自动隐藏右侧工具栏",
  6906. "dy-video-rightToolBarAutoHide",
  6907. false,
  6908. void 0,
  6909. "鼠标移入时自动显示,鼠标移除时自动隐藏"
  6910. ),
  6911. UISlider(
  6912. "延迟自动隐藏的时间",
  6913. "dy-video-rightToolBarAutoHide-delayTime",
  6914. 3e3,
  6915. 0,
  6916. 8e3,
  6917. void 0,
  6918. (value) => {
  6919. return `${value}ms`;
  6920. },
  6921. "设置延迟自动隐藏视频标题的时间,单位(ms)",
  6922. 100
  6923. )
  6924. ]
  6925. }
  6926. ]
  6927. },
  6928. {
  6929. text: "自定义快捷键",
  6930. type: "deepMenu",
  6931. forms: [
  6932. {
  6933. text: "",
  6934. type: "forms",
  6935. forms: [
  6936. UIButtonShortCut(
  6937. "倍速 -> 小",
  6938. "视频倍速变小",
  6939. "dy-video-rate-low",
  6940. void 0,
  6941. "点击录入快捷键",
  6942. void 0,
  6943. DouYinVideoPlayerShortCut.shortCut
  6944. ),
  6945. UIButtonShortCut(
  6946. "倍速 -> 大",
  6947. "视频倍速变大",
  6948. "dy-video-rate-up",
  6949. void 0,
  6950. "点击录入快捷键",
  6951. void 0,
  6952. DouYinVideoPlayerShortCut.shortCut
  6953. ),
  6954. UIButtonShortCut(
  6955. "沉浸模式",
  6956. "移除右侧工具栏、底部信息栏等",
  6957. "dy-video-shortcut-immersionMode",
  6958. void 0,
  6959. "点击录入快捷键",
  6960. void 0,
  6961. DouYinVideoPlayerShortCut.shortCut
  6962. ),
  6963. UIButtonShortCut(
  6964. "切换静音状态",
  6965. "切换video标签的muted属性",
  6966. "dy-video-shortcut-changeVideoMuted",
  6967. void 0,
  6968. "点击录入快捷键",
  6969. void 0,
  6970. DouYinVideoPlayerShortCut.shortCut
  6971. )
  6972. ]
  6973. }
  6974. ]
  6975. },
  6976. {
  6977. type: "deepMenu",
  6978. text: "禁用抖音快捷键",
  6979. afterEnterDeepMenuCallBack: AutoOpenOrClose.afterEnterDeepMenuCallBack,
  6980. forms: [
  6981. {
  6982. type: "forms",
  6983. text: AutoOpenOrClose.text,
  6984. forms: [
  6985. UISwitch(
  6986. "上翻页",
  6987. "dy-keyboard-hook-arrowUp-w",
  6988. false,
  6989. void 0,
  6990. "W"
  6991. ),
  6992. UISwitch(
  6993. "下翻页",
  6994. "dy-keyboard-hook-arrowDown-s",
  6995. false,
  6996. void 0,
  6997. "S"
  6998. ),
  6999. UISwitch(
  7000. "快退",
  7001. "dy-keyboard-hook-videoRewind",
  7002. false,
  7003. void 0,
  7004. "A"
  7005. ),
  7006. UISwitch(
  7007. "快进",
  7008. "dy-keyboard-hook-videoFastForward",
  7009. false,
  7010. void 0,
  7011. "D"
  7012. )
  7013. ]
  7014. }
  7015. ]
  7016. },
  7017. {
  7018. text: "过滤器",
  7019. type: "deepMenu",
  7020. forms: [
  7021. {
  7022. text: '<a href="https://gf.qytechs.cn/zh-CN/scripts/494643-%E6%8A%96%E9%9F%B3%E4%BC%98%E5%8C%96#:~:text=%E5%B1%8F%E8%94%BD%E8%A7%84%E5%88%99" target="_blank">点击查看规则</a>',
  7023. type: "forms",
  7024. forms: [
  7025. UISwitch(
  7026. "启用",
  7027. "shieldVideo-exec-network-enable",
  7028. true,
  7029. void 0,
  7030. "开启后以下功能才会生效"
  7031. ),
  7032. UISwitch(
  7033. "仅显示被过滤的视频",
  7034. "shieldVideo-only-show-filtered-video",
  7035. false,
  7036. void 0,
  7037. "只会显示过滤规则命中的视频"
  7038. ),
  7039. UISwitch(
  7040. "新增 {...} 按钮",
  7041. "shieldVideo-add-parseVideoInfoButton",
  7042. false,
  7043. void 0,
  7044. "在视频的底部的工具栏中显示 {...} 按钮,用于查看视频信息以便于进行添加过滤规则"
  7045. ),
  7046. UIButton(
  7047. "视频过滤规则",
  7048. "可过滤视频",
  7049. "自定义",
  7050. void 0,
  7051. false,
  7052. false,
  7053. "primary",
  7054. () => {
  7055. DouYinVideoFilter.showView();
  7056. }
  7057. )
  7058. ]
  7059. },
  7060. {
  7061. type: "forms",
  7062. text: "",
  7063. forms: [
  7064. UIButton(
  7065. "数据导入",
  7066. "导入自定义规则数据",
  7067. "导入",
  7068. void 0,
  7069. false,
  7070. false,
  7071. "primary",
  7072. () => {
  7073. DouYinVideoFilter.importRule();
  7074. }
  7075. ),
  7076. UIButton(
  7077. "数据导出",
  7078. "导出自定义规则数据",
  7079. "导出",
  7080. void 0,
  7081. false,
  7082. false,
  7083. "primary",
  7084. () => {
  7085. DouYinVideoFilter.exportRule(
  7086. SCRIPT_NAME + "-视频过滤规则.json"
  7087. );
  7088. }
  7089. )
  7090. ]
  7091. }
  7092. ]
  7093. }
  7094. ]
  7095. },
  7096. {
  7097. text: "",
  7098. type: "forms",
  7099. forms: [
  7100. {
  7101. text: "布局屏蔽-视频区域内",
  7102. type: "deepMenu",
  7103. afterEnterDeepMenuCallBack: AutoOpenOrClose.afterEnterDeepMenuCallBack,
  7104. forms: [
  7105. {
  7106. text: AutoOpenOrClose.text + "<br>右侧工具栏",
  7107. type: "forms",
  7108. forms: [
  7109. UISwitch(
  7110. "【屏蔽】切换播放",
  7111. "shieldPlaySwitchButton",
  7112. false,
  7113. void 0,
  7114. "屏蔽元素,在右侧作者头像上方或者是在右侧区域"
  7115. ),
  7116. UISwitch(
  7117. "【屏蔽】作者头像",
  7118. "shieldAuthorAvatar",
  7119. false,
  7120. void 0,
  7121. "屏蔽元素"
  7122. ),
  7123. UISwitch(
  7124. "【屏蔽】点赞",
  7125. "shieldLikeButton",
  7126. false,
  7127. void 0,
  7128. "屏蔽元素"
  7129. ),
  7130. UISwitch(
  7131. "【屏蔽】评论",
  7132. "shieldCommentButton",
  7133. false,
  7134. void 0,
  7135. "屏蔽元素"
  7136. ),
  7137. UISwitch(
  7138. "【屏蔽】收藏",
  7139. "shieldCollectionButton",
  7140. false,
  7141. void 0,
  7142. "屏蔽元素"
  7143. ),
  7144. UISwitch(
  7145. "【屏蔽】分享",
  7146. "shieldSharenButton",
  7147. false,
  7148. void 0,
  7149. "屏蔽元素"
  7150. ),
  7151. UISwitch(
  7152. "【屏蔽】看相关",
  7153. "shieldRelatedRecommendationsButton",
  7154. false,
  7155. void 0,
  7156. "屏蔽元素"
  7157. ),
  7158. UISwitch(
  7159. "【屏蔽】更多",
  7160. "shieldMoreButton",
  7161. false,
  7162. void 0,
  7163. "...按钮,屏蔽元素"
  7164. )
  7165. ]
  7166. },
  7167. {
  7168. text: "底部工具栏",
  7169. type: "forms",
  7170. forms: [
  7171. UISwitch(
  7172. "【屏蔽】底部视频工具栏",
  7173. "shieldBottomVideoToolBar",
  7174. false,
  7175. void 0,
  7176. "屏蔽元素"
  7177. ),
  7178. UISwitch(
  7179. "【屏蔽】弹幕容器",
  7180. "shieldBottomVideoToolbarDanmuContainer",
  7181. false,
  7182. void 0,
  7183. "屏蔽元素(不包括屏蔽弹幕)"
  7184. ),
  7185. UISwitch(
  7186. "【屏蔽】视频信息",
  7187. "dy-video-bottom-shieldVideoInfoWrap",
  7188. false,
  7189. void 0,
  7190. "屏蔽元素,可代替【清屏】功能"
  7191. )
  7192. ]
  7193. },
  7194. {
  7195. text: "其它",
  7196. type: "forms",
  7197. forms: [
  7198. UISwitch(
  7199. "【屏蔽】右侧的展开评论按钮",
  7200. "shieldRightExpandCommentButton",
  7201. true,
  7202. void 0,
  7203. "屏蔽元素"
  7204. ),
  7205. UISwitch(
  7206. "【屏蔽】搜索悬浮栏",
  7207. "shieldSearchFloatingBar",
  7208. true,
  7209. void 0,
  7210. "屏蔽元素,一般出现在左上角"
  7211. ),
  7212. UISwitch(
  7213. "【屏蔽】网页全屏关闭按钮",
  7214. "shieldCloseFullScreenButton",
  7215. true,
  7216. void 0,
  7217. "屏蔽元素,一般开启网页全屏后出现在左上角"
  7218. ),
  7219. UISwitch(
  7220. "【屏蔽】购物信息",
  7221. "dy-video-blockShopInfo",
  7222. true,
  7223. void 0,
  7224. "屏蔽元素,该元素出现在视频底部的用户名、标题信息的上面"
  7225. )
  7226. ]
  7227. }
  7228. ]
  7229. },
  7230. {
  7231. text: "布局屏蔽-评论区域内",
  7232. type: "deepMenu",
  7233. afterEnterDeepMenuCallBack: AutoOpenOrClose.afterEnterDeepMenuCallBack,
  7234. forms: [
  7235. {
  7236. text: AutoOpenOrClose.text,
  7237. type: "forms",
  7238. forms: [
  7239. UISwitch(
  7240. "【屏蔽】评论工具栏",
  7241. "dy-video-shieldUserCommentToolBar",
  7242. false,
  7243. void 0,
  7244. "屏蔽元素"
  7245. ),
  7246. UISwitch(
  7247. "【屏蔽】大家都在搜",
  7248. "dy-video-shieldUserCommentEveryOneAllSearch",
  7249. false,
  7250. void 0,
  7251. "在评论区的顶部出现"
  7252. )
  7253. ]
  7254. }
  7255. ]
  7256. }
  7257. ]
  7258. }
  7259. ]
  7260. };
  7261. const PanelSearchConfig = {
  7262. id: "panel-config-search",
  7263. title: "搜索",
  7264. forms: [
  7265. {
  7266. text: "",
  7267. type: "forms",
  7268. forms: [
  7269. {
  7270. text: "功能",
  7271. type: "deepMenu",
  7272. forms: [
  7273. {
  7274. text: "",
  7275. type: "forms",
  7276. forms: [
  7277. UISwitch(
  7278. "禁止点击视频区域进入全屏",
  7279. "dy-search-disableClickToEnterFullScreen",
  7280. false,
  7281. void 0,
  7282. "禁止点击视频区域时会触发自动进入全屏功能"
  7283. ),
  7284. UISelect(
  7285. "自动进入网页全屏",
  7286. "search-autoEnterElementFullScreen",
  7287. -1,
  7288. [
  7289. {
  7290. text: "跟随主设置",
  7291. value: -1
  7292. },
  7293. {
  7294. text: "是",
  7295. value: 1
  7296. },
  7297. {
  7298. text: "否",
  7299. value: 0
  7300. }
  7301. ],
  7302. void 0,
  7303. "网页加载完毕后自动点击网页全屏按钮进入全屏"
  7304. ),
  7305. UISelect(
  7306. "搜索结果-视频-显示样式",
  7307. "live-setSearchResultFilterWithVideoStyle",
  7308. "",
  7309. [
  7310. {
  7311. text: "默认",
  7312. value: ""
  7313. },
  7314. {
  7315. text: "单列",
  7316. value: "one"
  7317. },
  7318. {
  7319. text: "双列",
  7320. value: "double"
  7321. }
  7322. ],
  7323. void 0,
  7324. "自定义搜索结果,按视频筛选的结果项的显示样式"
  7325. )
  7326. ]
  7327. }
  7328. ]
  7329. }
  7330. ]
  7331. },
  7332. {
  7333. text: "",
  7334. type: "forms",
  7335. forms: [
  7336. {
  7337. text: "布局屏蔽",
  7338. type: "deepMenu",
  7339. afterEnterDeepMenuCallBack: AutoOpenOrClose.afterEnterDeepMenuCallBack,
  7340. forms: [
  7341. {
  7342. text: AutoOpenOrClose.text,
  7343. type: "forms",
  7344. forms: [
  7345. UISwitch(
  7346. "【屏蔽】相关搜索",
  7347. "douyin-search-shieldReleatedSearches",
  7348. false,
  7349. void 0,
  7350. "屏蔽右边的相关搜索"
  7351. )
  7352. ]
  7353. }
  7354. ]
  7355. },
  7356. {
  7357. text: "布局屏蔽-主框架",
  7358. type: "deepMenu",
  7359. forms: [
  7360. {
  7361. text: "",
  7362. type: "forms",
  7363. forms: [
  7364. UISelect(
  7365. "【屏蔽】左侧导航栏",
  7366. "search-shieldLeftNavigator",
  7367. -1,
  7368. [
  7369. {
  7370. text: "跟随主设置",
  7371. value: -1
  7372. },
  7373. {
  7374. text: "是",
  7375. value: 1
  7376. },
  7377. {
  7378. text: "否",
  7379. value: 0
  7380. }
  7381. ],
  7382. void 0,
  7383. "屏蔽元素"
  7384. ),
  7385. UISelect(
  7386. "【屏蔽】顶部导航栏",
  7387. "search-shieldTopNavigator",
  7388. -1,
  7389. [
  7390. {
  7391. text: "跟随主设置",
  7392. value: -1
  7393. },
  7394. {
  7395. text: "是",
  7396. value: 1
  7397. },
  7398. {
  7399. text: "否",
  7400. value: 0
  7401. }
  7402. ],
  7403. void 0,
  7404. "屏蔽元素"
  7405. )
  7406. ]
  7407. }
  7408. ]
  7409. }
  7410. ]
  7411. }
  7412. ]
  7413. };
  7414. const MPanelShareUserConfig = {
  7415. id: "m-panel-config-share-user",
  7416. title: "主页",
  7417. headerTitle: "/share/user<br />主页",
  7418. forms: [
  7419. {
  7420. text: "",
  7421. type: "forms",
  7422. forms: [
  7423. {
  7424. text: "覆盖点击事件",
  7425. type: "deepMenu",
  7426. forms: [
  7427. {
  7428. text: "",
  7429. type: "forms",
  7430. forms: [
  7431. UISwitch(
  7432. "视频合集",
  7433. "m-dy-share-user-coverPlayletList",
  7434. true,
  7435. void 0,
  7436. "正确跳转视频合集页面"
  7437. ),
  7438. UISwitch(
  7439. "视频列表",
  7440. "m-dy-share-user-coverPostListContainer",
  7441. true,
  7442. void 0,
  7443. "正确跳转视频页面"
  7444. )
  7445. ]
  7446. }
  7447. ]
  7448. }
  7449. ]
  7450. }
  7451. ]
  7452. };
  7453. const MPanelShareVideoConfig = {
  7454. id: "m-panel-config-share-video",
  7455. title: "视频",
  7456. headerTitle: "/share/video<br />视频",
  7457. forms: [
  7458. {
  7459. text: "",
  7460. type: "forms",
  7461. forms: [
  7462. {
  7463. text: "覆盖点击事件",
  7464. type: "deepMenu",
  7465. forms: [
  7466. {
  7467. text: "",
  7468. type: "forms",
  7469. forms: [
  7470. UISwitch(
  7471. "全局点击",
  7472. "m-dy-share-video-coverGlobalClick",
  7473. true,
  7474. void 0,
  7475. "阻止跳转至下载页"
  7476. )
  7477. ]
  7478. }
  7479. ]
  7480. }
  7481. ]
  7482. }
  7483. ]
  7484. };
  7485. const MPanelShareNoteConfig = {
  7486. id: "m-panel-config-share-note",
  7487. title: "笔记",
  7488. headerTitle: "/share/note<br />笔记",
  7489. forms: [
  7490. {
  7491. text: "",
  7492. type: "forms",
  7493. forms: [
  7494. {
  7495. text: "覆盖点击事件",
  7496. type: "deepMenu",
  7497. forms: [
  7498. {
  7499. text: "",
  7500. type: "forms",
  7501. forms: [
  7502. UISwitch(
  7503. "精彩图文",
  7504. "m-dy-share-note-coverExcitingGraphicsAndText",
  7505. true,
  7506. void 0,
  7507. "正确跳转笔记页面"
  7508. ),
  7509. UISwitch(
  7510. "用户",
  7511. "m-dy-share-note-coverUser",
  7512. true,
  7513. void 0,
  7514. "正确跳转用户主页"
  7515. ),
  7516. UISwitch(
  7517. "话题",
  7518. "m-dy-share-note-coverHashTag",
  7519. true,
  7520. void 0,
  7521. "正确跳转相关话题"
  7522. ),
  7523. UISwitch(
  7524. "音乐",
  7525. "m-dy-share-note-coverMusic",
  7526. true,
  7527. void 0,
  7528. "正确跳转相关音乐"
  7529. ),
  7530. UISwitch(
  7531. "相关推荐",
  7532. "m-dy-share-note-coverRecommend",
  7533. true,
  7534. void 0,
  7535. "正确跳转笔记页面"
  7536. )
  7537. ]
  7538. }
  7539. ]
  7540. },
  7541. {
  7542. text: "屏蔽元素",
  7543. type: "deepMenu",
  7544. forms: [
  7545. {
  7546. text: "",
  7547. type: "forms",
  7548. forms: [
  7549. UISwitch(
  7550. "【屏蔽】评论",
  7551. "m-dy-share-note-blockComment",
  7552. false,
  7553. void 0,
  7554. "屏蔽元素"
  7555. ),
  7556. UISwitch(
  7557. "【屏蔽】相关推荐",
  7558. "m-dy-share-note-blockRecommend",
  7559. false,
  7560. void 0,
  7561. "屏蔽元素"
  7562. ),
  7563. UISwitch(
  7564. "【屏蔽】底部工具栏",
  7565. "m-dy-share-note-blockFooterToobar",
  7566. false,
  7567. void 0,
  7568. "屏蔽元素"
  7569. )
  7570. ]
  7571. }
  7572. ]
  7573. }
  7574. ]
  7575. }
  7576. ]
  7577. };
  7578. const MPanelShareChallengeConfig = {
  7579. id: "m-panel-config-share-challenge",
  7580. title: "话题",
  7581. headerTitle: "/share/challenge<br />话题",
  7582. forms: [
  7583. {
  7584. text: "",
  7585. type: "forms",
  7586. forms: [
  7587. {
  7588. text: "覆盖点击事件",
  7589. type: "deepMenu",
  7590. forms: [
  7591. {
  7592. text: "",
  7593. type: "forms",
  7594. forms: [
  7595. UISwitch(
  7596. "顶部区域",
  7597. "m-dy-share-challenge-coverTopJump",
  7598. true,
  7599. void 0,
  7600. "阻止跳转至下载页面"
  7601. ),
  7602. UISwitch(
  7603. "视频卡片",
  7604. "m-dy-share-challenge-coverVideoCard",
  7605. true,
  7606. void 0,
  7607. "正确跳转视频页面"
  7608. )
  7609. ]
  7610. }
  7611. ]
  7612. }
  7613. ]
  7614. }
  7615. ]
  7616. };
  7617. const MPanelShareMusicConfig = {
  7618. id: "m-panel-config-share-music",
  7619. title: "音乐",
  7620. headerTitle: "/share/music<br />音乐",
  7621. forms: [
  7622. {
  7623. text: "",
  7624. type: "forms",
  7625. forms: [
  7626. {
  7627. text: "覆盖点击事件",
  7628. type: "deepMenu",
  7629. forms: [
  7630. {
  7631. text: "",
  7632. type: "forms",
  7633. forms: [
  7634. UISwitch(
  7635. "视频卡片",
  7636. "m-dy-share-music-coverVideoCard",
  7637. true,
  7638. void 0,
  7639. "正确跳转视频页面"
  7640. )
  7641. ]
  7642. }
  7643. ]
  7644. }
  7645. ]
  7646. }
  7647. ]
  7648. };
  7649. const PanelUserConfig = {
  7650. id: "panel-config-user",
  7651. title: "用户",
  7652. forms: [
  7653. {
  7654. text: "功能",
  7655. type: "forms",
  7656. forms: [
  7657. UISwitch(
  7658. "显示UID",
  7659. "dy-user-addShowUserUID",
  7660. true,
  7661. void 0,
  7662. "在用户信息区域下方显示当前用户的uid"
  7663. )
  7664. ]
  7665. }
  7666. ]
  7667. };
  7668. const PopsPanel = {
  7669. /** 数据 */
  7670. $data: {
  7671. __data: null,
  7672. __oneSuccessExecMenu: null,
  7673. __onceExec: null,
  7674. __listenData: null,
  7675. /**
  7676. * 菜单项的默认值
  7677. */
  7678. get data() {
  7679. if (PopsPanel.$data.__data == null) {
  7680. PopsPanel.$data.__data = new utils.Dictionary();
  7681. }
  7682. return PopsPanel.$data.__data;
  7683. },
  7684. /**
  7685. * 成功只执行了一次的项
  7686. */
  7687. get oneSuccessExecMenu() {
  7688. if (PopsPanel.$data.__oneSuccessExecMenu == null) {
  7689. PopsPanel.$data.__oneSuccessExecMenu = new utils.Dictionary();
  7690. }
  7691. return PopsPanel.$data.__oneSuccessExecMenu;
  7692. },
  7693. /**
  7694. * 成功只执行了一次的项
  7695. */
  7696. get onceExec() {
  7697. if (PopsPanel.$data.__onceExec == null) {
  7698. PopsPanel.$data.__onceExec = new utils.Dictionary();
  7699. }
  7700. return PopsPanel.$data.__onceExec;
  7701. },
  7702. /** 脚本名,一般用在设置的标题上 */
  7703. get scriptName() {
  7704. return SCRIPT_NAME;
  7705. },
  7706. /** 菜单项的总值在本地数据配置的键名 */
  7707. key: KEY,
  7708. /** 菜单项在attributes上配置的菜单键 */
  7709. attributeKeyName: ATTRIBUTE_KEY,
  7710. /** 菜单项在attributes上配置的菜单默认值 */
  7711. attributeDefaultValueName: ATTRIBUTE_DEFAULT_VALUE
  7712. },
  7713. /** 监听器 */
  7714. $listener: {
  7715. /**
  7716. * 值改变的监听器
  7717. */
  7718. get listenData() {
  7719. if (PopsPanel.$data.__listenData == null) {
  7720. PopsPanel.$data.__listenData = new utils.Dictionary();
  7721. }
  7722. return PopsPanel.$data.__listenData;
  7723. }
  7724. },
  7725. init() {
  7726. this.initPanelDefaultValue();
  7727. this.initExtensionsMenu();
  7728. },
  7729. /** 判断是否是顶层窗口 */
  7730. isTopWindow() {
  7731. return _unsafeWindow.top === _unsafeWindow.self;
  7732. },
  7733. initExtensionsMenu() {
  7734. if (!this.isTopWindow()) {
  7735. return;
  7736. }
  7737. GM_Menu.add([
  7738. {
  7739. key: "show_pops_panel_setting",
  7740. text: "⚙ 设置",
  7741. autoReload: false,
  7742. isStoreValue: false,
  7743. showText(text) {
  7744. return text;
  7745. },
  7746. callback: () => {
  7747. this.showPanel();
  7748. }
  7749. },
  7750. {
  7751. key: "show_pops_m_panel_setting",
  7752. text: "⚙ 移动端设置",
  7753. autoReload: false,
  7754. isStoreValue: false,
  7755. showText(text) {
  7756. return text;
  7757. },
  7758. callback: () => {
  7759. this.showMPanel();
  7760. }
  7761. }
  7762. ]);
  7763. },
  7764. /** 初始化菜单项的默认值保存到本地数据中 */
  7765. initPanelDefaultValue() {
  7766. let that = this;
  7767. function initDefaultValue(config) {
  7768. if (!config.attributes) {
  7769. return;
  7770. }
  7771. let needInitConfig = {};
  7772. let key = config.attributes[ATTRIBUTE_KEY];
  7773. if (key != null) {
  7774. needInitConfig[key] = config.attributes[ATTRIBUTE_DEFAULT_VALUE];
  7775. }
  7776. let __attr_init__ = config.attributes[ATTRIBUTE_INIT];
  7777. if (typeof __attr_init__ === "function") {
  7778. let __attr_result__ = __attr_init__();
  7779. if (typeof __attr_result__ === "boolean" && !__attr_result__) {
  7780. return;
  7781. }
  7782. }
  7783. let initMoreValue = config.attributes[ATTRIBUTE_INIT_MORE_VALUE];
  7784. if (initMoreValue && typeof initMoreValue === "object") {
  7785. Object.assign(needInitConfig, initMoreValue);
  7786. }
  7787. let needInitConfigList = Object.keys(needInitConfig);
  7788. if (!needInitConfigList.length) {
  7789. log.warn(["请先配置键", config]);
  7790. return;
  7791. }
  7792. needInitConfigList.forEach((__key) => {
  7793. let __defaultValue = needInitConfig[__key];
  7794. if (that.$data.data.has(__key)) {
  7795. log.warn("请检查该key(已存在): " + __key);
  7796. }
  7797. that.$data.data.set(__key, __defaultValue);
  7798. });
  7799. }
  7800. function loopInitDefaultValue(configList) {
  7801. for (let index = 0; index < configList.length; index++) {
  7802. let configItem = configList[index];
  7803. initDefaultValue(configItem);
  7804. let childForms = configItem.forms;
  7805. if (childForms && Array.isArray(childForms)) {
  7806. loopInitDefaultValue(childForms);
  7807. }
  7808. }
  7809. }
  7810. let contentConfigList = this.getPanelContentConfig().concat(
  7811. this.getMPanelContentConfig()
  7812. );
  7813. for (let index = 0; index < contentConfigList.length; index++) {
  7814. let leftContentConfigItem = contentConfigList[index];
  7815. if (!leftContentConfigItem.forms) {
  7816. continue;
  7817. }
  7818. let rightContentConfigList = leftContentConfigItem.forms;
  7819. if (rightContentConfigList && Array.isArray(rightContentConfigList)) {
  7820. loopInitDefaultValue(rightContentConfigList);
  7821. }
  7822. }
  7823. },
  7824. /**
  7825. * 设置值
  7826. * @param key 键
  7827. * @param value 值
  7828. */
  7829. setValue(key, value) {
  7830. let locaData = _GM_getValue(KEY, {});
  7831. let oldValue = locaData[key];
  7832. locaData[key] = value;
  7833. _GM_setValue(KEY, locaData);
  7834. if (this.$listener.listenData.has(key)) {
  7835. this.$listener.listenData.get(key).callback(key, oldValue, value);
  7836. }
  7837. },
  7838. /**
  7839. * 获取值
  7840. * @param key 键
  7841. * @param defaultValue 默认值
  7842. */
  7843. getValue(key, defaultValue) {
  7844. let locaData = _GM_getValue(KEY, {});
  7845. let localValue = locaData[key];
  7846. if (localValue == null) {
  7847. if (this.$data.data.has(key)) {
  7848. return this.$data.data.get(key);
  7849. }
  7850. return defaultValue;
  7851. }
  7852. return localValue;
  7853. },
  7854. /**
  7855. * 删除值
  7856. * @param key 键
  7857. */
  7858. deleteValue(key) {
  7859. let locaData = _GM_getValue(KEY, {});
  7860. let oldValue = locaData[key];
  7861. Reflect.deleteProperty(locaData, key);
  7862. _GM_setValue(KEY, locaData);
  7863. if (this.$listener.listenData.has(key)) {
  7864. this.$listener.listenData.get(key).callback(key, oldValue, void 0);
  7865. }
  7866. },
  7867. /**
  7868. * 监听调用setValue、deleteValue
  7869. * @param key 需要监听的键
  7870. * @param callback
  7871. */
  7872. addValueChangeListener(key, callback) {
  7873. let listenerId = Math.random();
  7874. this.$listener.listenData.set(key, {
  7875. id: listenerId,
  7876. key,
  7877. callback
  7878. });
  7879. return listenerId;
  7880. },
  7881. /**
  7882. * 移除监听
  7883. * @param listenerId 监听的id
  7884. */
  7885. removeValueChangeListener(listenerId) {
  7886. let deleteKey = null;
  7887. for (const [key, value] of this.$listener.listenData.entries()) {
  7888. if (value.id === listenerId) {
  7889. deleteKey = key;
  7890. break;
  7891. }
  7892. }
  7893. if (typeof deleteKey === "string") {
  7894. this.$listener.listenData.delete(deleteKey);
  7895. } else {
  7896. console.warn("没有找到对应的监听器");
  7897. }
  7898. },
  7899. /**
  7900. * 主动触发菜单值改变的回调
  7901. * @param key 菜单键
  7902. * @param newValue 想要触发的新值,默认使用当前值
  7903. * @param oldValue 想要触发的旧值,默认使用当前值
  7904. */
  7905. triggerMenuValueChange(key, newValue, oldValue) {
  7906. if (this.$listener.listenData.has(key)) {
  7907. let listenData = this.$listener.listenData.get(key);
  7908. if (typeof listenData.callback === "function") {
  7909. let value = this.getValue(key);
  7910. let __newValue = value;
  7911. let __oldValue = value;
  7912. if (typeof newValue !== "undefined" && arguments.length > 1) {
  7913. __newValue = newValue;
  7914. }
  7915. if (typeof oldValue !== "undefined" && arguments.length > 2) {
  7916. __oldValue = oldValue;
  7917. }
  7918. listenData.callback(key, __oldValue, __newValue);
  7919. }
  7920. }
  7921. },
  7922. /**
  7923. * 判断该键是否存在
  7924. * @param key 键
  7925. */
  7926. hasKey(key) {
  7927. let locaData = _GM_getValue(KEY, {});
  7928. return key in locaData;
  7929. },
  7930. /**
  7931. * 移除已执行的仅执行一次的菜单
  7932. * @param key 键
  7933. */
  7934. deleteExecMenuOnce(key) {
  7935. this.$data.oneSuccessExecMenu.delete(key);
  7936. let flag = false;
  7937. for (const [
  7938. listenerId,
  7939. listenerData
  7940. ] of this.$listener.listenData.entries()) {
  7941. if (listenerData.key === key) {
  7942. flag = true;
  7943. this.removeValueChangeListener(listenerData.id);
  7944. break;
  7945. }
  7946. }
  7947. return flag;
  7948. },
  7949. /**
  7950. * 移除已执行的仅执行一次的菜单
  7951. * @param key 键
  7952. */
  7953. deleteOnceExec(key) {
  7954. this.$data.onceExec.delete(key);
  7955. },
  7956. /**
  7957. * 自动判断菜单是否启用,然后执行回调
  7958. * @param key
  7959. * @param callback 回调
  7960. * @param [isReverse=false] 逆反判断菜单启用
  7961. */
  7962. execMenu(key, callback, isReverse = false) {
  7963. if (!(typeof key === "string" || typeof key === "object" && Array.isArray(key))) {
  7964. throw new TypeError("key 必须是字符串或者字符串数组");
  7965. }
  7966. let runKeyList = [];
  7967. if (typeof key === "object" && Array.isArray(key)) {
  7968. runKeyList = [...key];
  7969. } else {
  7970. runKeyList.push(key);
  7971. }
  7972. let value = void 0;
  7973. for (let index = 0; index < runKeyList.length; index++) {
  7974. const runKey = runKeyList[index];
  7975. if (!this.$data.data.has(runKey)) {
  7976. log.warn(`${key} 键不存在`);
  7977. return;
  7978. }
  7979. let runValue = PopsPanel.getValue(runKey);
  7980. if (isReverse) {
  7981. runValue = !runValue;
  7982. }
  7983. if (!runValue) {
  7984. break;
  7985. }
  7986. value = runValue;
  7987. }
  7988. if (value) {
  7989. callback(value);
  7990. }
  7991. },
  7992. /**
  7993. * 自动判断菜单是否启用,然后执行回调,只会执行一次
  7994. *
  7995. * 它会自动监听值改变(设置中的修改),改变后如果未执行,则执行一次
  7996. * @param key
  7997. * @param callback 回调
  7998. * @param getValueFn 自定义处理获取当前值,值true是启用并执行回调,值false是不执行回调
  7999. * @param handleValueChangeFn 自定义处理值改变时的回调,值true是启用并执行回调,值false是不执行回调
  8000. */
  8001. execMenuOnce(key, callback, getValueFn, handleValueChangeFn) {
  8002. if (typeof key !== "string") {
  8003. throw new TypeError("key 必须是字符串");
  8004. }
  8005. if (!this.$data.data.has(key)) {
  8006. log.warn(`${key} 键不存在`);
  8007. return;
  8008. }
  8009. if (this.$data.oneSuccessExecMenu.has(key)) {
  8010. return;
  8011. }
  8012. this.$data.oneSuccessExecMenu.set(key, 1);
  8013. let __getValue = () => {
  8014. let localValue = PopsPanel.getValue(key);
  8015. return typeof getValueFn === "function" ? getValueFn(key, localValue) : localValue;
  8016. };
  8017. let resultStyleList = [];
  8018. let dynamicPushStyleNode = ($style) => {
  8019. let __value = __getValue();
  8020. let dynamicResultList = [];
  8021. if ($style instanceof HTMLStyleElement) {
  8022. dynamicResultList = [$style];
  8023. } else if (Array.isArray($style)) {
  8024. dynamicResultList = [
  8025. ...$style.filter(
  8026. (item) => item != null && item instanceof HTMLStyleElement
  8027. )
  8028. ];
  8029. }
  8030. if (__value) {
  8031. resultStyleList = resultStyleList.concat(dynamicResultList);
  8032. } else {
  8033. for (let index = 0; index < dynamicResultList.length; index++) {
  8034. let $css = dynamicResultList[index];
  8035. $css.remove();
  8036. dynamicResultList.splice(index, 1);
  8037. index--;
  8038. }
  8039. }
  8040. };
  8041. let changeCallBack = (currentValue) => {
  8042. let resultList = [];
  8043. if (currentValue) {
  8044. let result = callback(currentValue, dynamicPushStyleNode);
  8045. if (result instanceof HTMLStyleElement) {
  8046. resultList = [result];
  8047. } else if (Array.isArray(result)) {
  8048. resultList = [
  8049. ...result.filter(
  8050. (item) => item != null && item instanceof HTMLStyleElement
  8051. )
  8052. ];
  8053. }
  8054. }
  8055. for (let index = 0; index < resultStyleList.length; index++) {
  8056. let $css = resultStyleList[index];
  8057. $css.remove();
  8058. resultStyleList.splice(index, 1);
  8059. index--;
  8060. }
  8061. resultStyleList = [...resultList];
  8062. };
  8063. this.addValueChangeListener(
  8064. key,
  8065. (__key, oldValue, newValue) => {
  8066. let __newValue = newValue;
  8067. if (typeof handleValueChangeFn === "function") {
  8068. __newValue = handleValueChangeFn(__key, newValue, oldValue);
  8069. }
  8070. changeCallBack(__newValue);
  8071. }
  8072. );
  8073. let value = __getValue();
  8074. if (value) {
  8075. changeCallBack(value);
  8076. }
  8077. },
  8078. /**
  8079. * 父子菜单联动,自动判断菜单是否启用,然后执行回调,只会执行一次
  8080. * @param key 菜单键
  8081. * @param childKey 子菜单键
  8082. * @param callback 回调
  8083. * @param replaceValueFn 用于修改mainValue,返回undefined则不做处理
  8084. */
  8085. execInheritMenuOnce(key, childKey, callback, replaceValueFn) {
  8086. let that = this;
  8087. const handleInheritValue = (key2, childKey2) => {
  8088. let mainValue = that.getValue(key2);
  8089. let childValue = that.getValue(childKey2);
  8090. if (typeof replaceValueFn === "function") {
  8091. let changedMainValue = replaceValueFn(mainValue, childValue);
  8092. if (changedMainValue !== void 0) {
  8093. return changedMainValue;
  8094. }
  8095. }
  8096. return mainValue;
  8097. };
  8098. this.execMenuOnce(
  8099. key,
  8100. callback,
  8101. () => {
  8102. return handleInheritValue(key, childKey);
  8103. },
  8104. () => {
  8105. return handleInheritValue(key, childKey);
  8106. }
  8107. );
  8108. this.execMenuOnce(
  8109. childKey,
  8110. () => {
  8111. },
  8112. () => false,
  8113. () => {
  8114. this.triggerMenuValueChange(key);
  8115. return false;
  8116. }
  8117. );
  8118. },
  8119. /**
  8120. * 根据key执行一次
  8121. * @param key
  8122. */
  8123. onceExec(key, callback) {
  8124. if (typeof key !== "string") {
  8125. throw new TypeError("key 必须是字符串");
  8126. }
  8127. if (this.$data.onceExec.has(key)) {
  8128. return;
  8129. }
  8130. callback();
  8131. this.$data.onceExec.set(key, 1);
  8132. },
  8133. /**
  8134. * 显示设置面板
  8135. */
  8136. showPanel() {
  8137. __pops.panel({
  8138. title: {
  8139. text: `${SCRIPT_NAME}-设置`,
  8140. position: "center",
  8141. html: false,
  8142. style: ""
  8143. },
  8144. content: this.getPanelContentConfig(),
  8145. mask: {
  8146. enable: true,
  8147. clickEvent: {
  8148. toClose: true,
  8149. toHide: false
  8150. }
  8151. },
  8152. width: PanelUISize.setting.width,
  8153. height: PanelUISize.setting.height,
  8154. drag: true,
  8155. only: true
  8156. });
  8157. },
  8158. /**
  8159. * 显示移动端设置面板
  8160. */
  8161. showMPanel() {
  8162. __pops.panel({
  8163. title: {
  8164. text: `${SCRIPT_NAME}-设置`,
  8165. position: "center",
  8166. html: false,
  8167. style: ""
  8168. },
  8169. content: this.getMPanelContentConfig(),
  8170. mask: {
  8171. enable: true,
  8172. clickEvent: {
  8173. toClose: true,
  8174. toHide: false
  8175. }
  8176. },
  8177. width: PanelUISize.setting.width,
  8178. height: PanelUISize.setting.height,
  8179. drag: true,
  8180. only: true
  8181. });
  8182. },
  8183. /**
  8184. * 获取配置内容
  8185. */
  8186. getPanelContentConfig() {
  8187. let configList = [
  8188. PanelCommonConfig,
  8189. PanelVideoConfig,
  8190. PanelSearchConfig,
  8191. PanelLiveConfig,
  8192. PanelUserConfig
  8193. ];
  8194. return configList;
  8195. },
  8196. /**
  8197. * 获取配置内容
  8198. */
  8199. getMPanelContentConfig() {
  8200. let configList = [
  8201. MPanelShareUserConfig,
  8202. MPanelShareNoteConfig,
  8203. MPanelShareChallengeConfig,
  8204. MPanelShareVideoConfig,
  8205. MPanelShareMusicConfig
  8206. ];
  8207. return configList;
  8208. }
  8209. };
  8210. const _SCRIPT_NAME_ = "抖音优化";
  8211. const utils = Utils.noConflict();
  8212. let domUtils = DOMUtils.noConflict();
  8213. const __pops = pops;
  8214. const console$1 = _unsafeWindow.console || _monkeyWindow.console;
  8215. const log = new utils.Log(_GM_info, console$1);
  8216. let SCRIPT_NAME = ((_a = _GM_info == null ? void 0 : _GM_info.script) == null ? void 0 : _a.name) || _SCRIPT_NAME_;
  8217. log.config({
  8218. debug: false,
  8219. logMaxCount: 100,
  8220. autoClearConsole: true,
  8221. tag: true
  8222. });
  8223. Qmsg.config(
  8224. Object.defineProperties(
  8225. {
  8226. html: true,
  8227. autoClose: true,
  8228. showClose: false,
  8229. zIndex: 1e7
  8230. },
  8231. {
  8232. position: {
  8233. get() {
  8234. return PopsPanel.getValue("qmsg-config-position", "bottom");
  8235. }
  8236. },
  8237. maxNums: {
  8238. get() {
  8239. return PopsPanel.getValue("qmsg-config-maxnums", 5);
  8240. }
  8241. },
  8242. showReverse: {
  8243. get() {
  8244. return PopsPanel.getValue("qmsg-config-showreverse", true);
  8245. }
  8246. }
  8247. }
  8248. )
  8249. );
  8250. const GM_Menu = new utils.GM_Menu({
  8251. GM_getValue: _GM_getValue,
  8252. GM_setValue: _GM_setValue,
  8253. GM_registerMenuCommand: _GM_registerMenuCommand,
  8254. GM_unregisterMenuCommand: _GM_unregisterMenuCommand
  8255. });
  8256. const httpx = new utils.Httpx(_GM_xmlhttpRequest);
  8257. httpx.interceptors.response.use(void 0, (data) => {
  8258. log.error(["拦截器-请求错误", data]);
  8259. if (data.type === "onabort") {
  8260. Qmsg.warning("请求取消");
  8261. } else if (data.type === "onerror") {
  8262. Qmsg.error("请求异常");
  8263. } else if (data.type === "ontimeout") {
  8264. Qmsg.error("请求超时");
  8265. } else {
  8266. Qmsg.error("其它错误");
  8267. }
  8268. return data;
  8269. });
  8270. const addStyle = utils.addStyle.bind(utils);
  8271. const $ = document.querySelector.bind(document);
  8272. const $$ = document.querySelectorAll.bind(document);
  8273. new utils.GM_Cookie();
  8274. const BlockTopNavigator = {
  8275. init() {
  8276. PopsPanel.execInheritMenuOnce(
  8277. "shieldTopNavigator",
  8278. "search-shieldTopNavigator",
  8279. () => {
  8280. return this.shieldTopNavigator();
  8281. },
  8282. (mainValue, childValue) => {
  8283. if (DouYinRouter.isSearch()) {
  8284. if (childValue == 1) {
  8285. return true;
  8286. } else if (childValue == 0) {
  8287. return false;
  8288. } else ;
  8289. }
  8290. }
  8291. );
  8292. PopsPanel.execMenuOnce("shieldClientTip", () => {
  8293. return this.shieldClientTip();
  8294. });
  8295. PopsPanel.execMenuOnce("shieldFillingBricksAndStones", () => {
  8296. return this.shieldFillingBricksAndStones();
  8297. });
  8298. PopsPanel.execMenuOnce("shieldClient", () => {
  8299. return this.shieldClient();
  8300. });
  8301. PopsPanel.execMenuOnce("shieldQuickAccess", () => {
  8302. return this.shieldQuickAccess();
  8303. });
  8304. PopsPanel.execMenuOnce("shieldNotifitation", () => {
  8305. return this.shieldNotifitation();
  8306. });
  8307. PopsPanel.execMenuOnce("shieldPrivateMessage", () => {
  8308. return this.shieldPrivateMessage();
  8309. });
  8310. PopsPanel.execMenuOnce("shieldSubmission", () => {
  8311. return this.shieldSubmission();
  8312. });
  8313. PopsPanel.execMenuOnce("shieldWallpaper", () => {
  8314. return this.shieldWallpaper();
  8315. });
  8316. PopsPanel.execMenuOnce("shieldBottomQuestionButton", () => {
  8317. return this.shieldBottomQuestionButton();
  8318. });
  8319. PopsPanel.execMenuOnce("shield-topNav-rightMenu", () => {
  8320. return this.shieldRightMenu();
  8321. });
  8322. PopsPanel.execMenuOnce("shield-topNav-rightMenu-more", () => {
  8323. return this.shieldRightMenuMore();
  8324. });
  8325. PopsPanel.execMenuOnce("shield-topNav-rightMenu-loginAvatar", () => {
  8326. return this.shieldRightMenuLoginAvatar();
  8327. });
  8328. },
  8329. /**
  8330. * 【屏蔽】顶部导航栏
  8331. */
  8332. shieldTopNavigator() {
  8333. log.info("【屏蔽】顶部导航栏");
  8334. let result = [];
  8335. result.push(CommonUtil.addBlockCSS("#douyin-header"));
  8336. result.push(
  8337. addStyle(
  8338. /*css*/
  8339. `
  8340. /* 修复视频的高度 */
  8341. #douyin-right-container{
  8342. padding-top: 0px !important;
  8343. }
  8344. /* 兼容手机模式 */
  8345. @media screen and (max-width: 550px){
  8346. .is-mobile-pc{
  8347. --header-height: 0px !important;
  8348. }
  8349. }
  8350. `
  8351. )
  8352. );
  8353. if (DouYinRouter.isSearch()) {
  8354. result.push(
  8355. addStyle(
  8356. /*css*/
  8357. `
  8358. /* 把搜索顶部的工具栏置顶 */
  8359. #search-content-area > div > div:nth-child(1) > div:nth-child(1){
  8360. top: 0;
  8361. }`
  8362. )
  8363. );
  8364. }
  8365. return result;
  8366. },
  8367. /**
  8368. * 【屏蔽】充钻石
  8369. */
  8370. shieldFillingBricksAndStones() {
  8371. log.info("【屏蔽】充钻石");
  8372. let result = [];
  8373. const iconPath = `d="M12.8013 19.9762C12.3693 20.4436 11.6307 20.4436 11.1986 19.9762L3.11756 11.2346C2.74913 10.8361 2.72958 10.2274 3.07168 9.80599L6.92716 5.05714C7.13438 4.8019 7.44562 4.65369 7.77439 4.65369H16.2256C16.5544 4.65369 16.8656 4.8019 17.0728 5.05714L20.9283 9.80599C21.2704 10.2274 21.2508 10.8361 20.8824 11.2346L12.8013 19.9762ZM4.45944 10.4765L12 18.6334L19.5405 10.4765L16.031 6.15369H7.96901L4.45944 10.4765ZM16.0867 9.09336L16.0954 10.4557C15.3615 10.4557 14.6822 10.2315 14.1281 9.85065V12.5739C14.1281 13.9502 12.964 15.0659 11.5281 15.0659C10.0922 15.0659 8.9281 13.9502 8.9281 12.5739C8.9281 11.1976 10.0922 10.0819 11.5281 10.0819C11.6486 10.0819 11.7672 10.0897 11.8834 10.1049V11.4964C11.7713 11.4625 11.6519 11.4442 11.5281 11.4442C10.8771 11.4442 10.3494 11.95 10.3494 12.5739C10.3494 13.1978 10.8771 13.7036 11.5281 13.7036C12.179 13.7036 12.7067 13.1978 12.7067 12.5739V7.21604H14.1281C14.1281 8.25285 15.005 9.09336 16.0867 9.09336Z"`;
  8374. result.push(
  8375. CommonUtil.addBlockCSS(
  8376. // 2024.8.12
  8377. `div[id^="douyin-header-menu"] pace-island > div > div:has(path[${iconPath}])`,
  8378. // 2024.7.16 更多 充钻石
  8379. 'body .semi-portal .semi-portal-inner li.semi-dropdown-item:has(a[href*="douyin_recharge"])'
  8380. )
  8381. );
  8382. if (DouYinRouter.isSearch()) {
  8383. result.push(
  8384. CommonUtil.addBlockCSS(
  8385. // 2024.8.12
  8386. `div[id^="douyin-header-menu"] > div > div > div:has(path[${iconPath}])`
  8387. )
  8388. );
  8389. } else if (DouYinRouter.isLive()) {
  8390. result.push(
  8391. CommonUtil.addBlockCSS(
  8392. // 直播
  8393. '#douyin-header pace-island[id^="island"] > div[class]:not([data-click]):has(div[data-e2e="something-button"]) > :has(path[d="M12.8013 19.9762C12.3693 20.4436 11.6307 20.4436 11.1986 19.9762L3.11756 11.2346C2.74913 10.8361 2.72958 10.2274 3.07168 9.80599L6.92716 5.05714C7.13438 4.8019 7.44562 4.65369 7.77439 4.65369H16.2256C16.5544 4.65369 16.8656 4.8019 17.0728 5.05714L20.9283 9.80599C21.2704 10.2274 21.2508 10.8361 20.8824 11.2346L12.8013 19.9762ZM4.45944 10.4765L12 18.6334L19.5405 10.4765L16.031 6.15369H7.96901L4.45944 10.4765ZM16.0867 9.09336L16.0954 10.4557C15.3615 10.4557 14.6822 10.2315 14.1281 9.85065V12.5739C14.1281 13.9502 12.964 15.0659 11.5281 15.0659C10.0922 15.0659 8.9281 13.9502 8.9281 12.5739C8.9281 11.1976 10.0922 10.0819 11.5281 10.0819C11.6486 10.0819 11.7672 10.0897 11.8834 10.1049V11.4964C11.7713 11.4625 11.6519 11.4442 11.5281 11.4442C10.8771 11.4442 10.3494 11.95 10.3494 12.5739C10.3494 13.1978 10.8771 13.7036 11.5281 13.7036C12.179 13.7036 12.7067 13.1978 12.7067 12.5739V7.21604H14.1281C14.1281 8.25285 15.005 9.09336 16.0867 9.09336Z"])'
  8394. )
  8395. );
  8396. }
  8397. return result;
  8398. },
  8399. /**
  8400. * 【屏蔽】客户端
  8401. */
  8402. shieldClient() {
  8403. log.info("【屏蔽】客户端");
  8404. let result = [];
  8405. result.push(
  8406. CommonUtil.addBlockCSS(
  8407. '#douyin-right-container pace-island[id^="island"] > div[class]:has(div[data-e2e="something-button"]) .dy-tip-container',
  8408. // 2024.7.15
  8409. 'div[id^="douyin-header-menu"] pace-island > div > div[aria-describedby]:has(a[download^="douyin-downloader"])',
  8410. // ios
  8411. 'div[id^="douyin-header-menu"] pace-island > div > div[aria-describedby]:has(a[href*="/douyin-pc-web/"])'
  8412. )
  8413. );
  8414. if (DouYinRouter.isSearch()) {
  8415. result.push(
  8416. CommonUtil.addBlockCSS(
  8417. 'div:has(> div[data-e2e="something-button"] path[d="M18.404 19.018h-12v-1.5h12v1.5zM11.654 13.457v-8.19h1.5v8.19l3.22-3.22 1.06 1.061-4.5 4.5a.75.75 0 01-1.06 0l-4.5-4.5 1.06-1.06 3.22 3.22z"])',
  8418. // 2024.7.15
  8419. 'div[id^="douyin-header-menu"] > div > div > div:has(a[download^="douyin-downloader"])'
  8420. )
  8421. );
  8422. } else if (DouYinRouter.isLive()) {
  8423. result.push(
  8424. CommonUtil.addBlockCSS(
  8425. // 直播
  8426. '#douyin-header pace-island[id^="island"] > div[class]:has(div[data-e2e="something-button"]) .dy-tip-container:has(a)',
  8427. // 直播
  8428. '#douyin-header pace-island[id^="island"] > div[class] span:has(a[download][href*="client"])',
  8429. /* 直播 更多 客户端 */
  8430. '.semi-portal-inner .semi-dropdown-content .semi-dropdown-item:has(a[download][href*="client"])'
  8431. )
  8432. );
  8433. }
  8434. return result;
  8435. },
  8436. /**
  8437. * 【屏蔽】快捷访问
  8438. */
  8439. shieldQuickAccess() {
  8440. log.info("【屏蔽】快捷访问");
  8441. let result = [];
  8442. result.push(
  8443. CommonUtil.addBlockCSS(
  8444. 'header pace-island[id^="island"] > div[class]:has(div[data-e2e="something-button"]) > :has(.quick-access-nav-icon)',
  8445. // 直播 更多里面的 快捷访问
  8446. // '.semi-portal-inner .semi-dropdown-content .semi-dropdown-item'
  8447. // 2024.7.15 更新规则
  8448. 'div[id^="douyin-header-menu"] pace-island > div > div:has(.quick-access-nav-icon)'
  8449. )
  8450. );
  8451. if (DouYinRouter.isSearch()) {
  8452. result.push(
  8453. CommonUtil.addBlockCSS("div:has(>div>div>.quick-access-nav-icon)")
  8454. );
  8455. utils.waitNode('li.semi-dropdown-item[role="menuitem"]', 1e4).then(($semi) => {
  8456. if (!$semi) {
  8457. return;
  8458. }
  8459. let observer = utils.mutationObserver(document.body, {
  8460. config: {
  8461. subtree: true,
  8462. childList: true
  8463. },
  8464. callback() {
  8465. let isFind = false;
  8466. document.querySelectorAll('li.semi-dropdown-item[role="menuitem"]').forEach(($ele) => {
  8467. var _a2;
  8468. if ((_a2 = $ele.textContent) == null ? void 0 : _a2.includes("快捷访问")) {
  8469. isFind = true;
  8470. log.success("搜索-更多-快捷访问 移除元素");
  8471. $ele.remove();
  8472. }
  8473. });
  8474. if (isFind) {
  8475. observer.disconnect();
  8476. }
  8477. }
  8478. });
  8479. });
  8480. } else if (DouYinRouter.isLive()) ;
  8481. return result;
  8482. },
  8483. /**
  8484. * 【屏蔽】通知
  8485. */
  8486. shieldNotifitation() {
  8487. log.info("【屏蔽】通知");
  8488. let result = [];
  8489. result.push(
  8490. // 2024.11.11
  8491. CommonUtil.addBlockCSS(
  8492. '#douyin-right-container #douyin-header-menuCt pace-island[id^="island"] > div[class]:has(div[data-e2e="something-button"]) > :has(path[d="M11.9998 4.50037C9.02034 4.50037 6.55167 6.81159 6.35561 9.78463L5.94855 15.9572H18.0507L17.6441 9.78506C17.4482 6.81184 14.9795 4.50037 11.9998 4.50037ZM7.85236 9.88334C7.99643 7.6987 9.81045 6.00037 11.9998 6.00037C14.1893 6.00037 16.0034 7.69888 16.1473 9.88365L16.4486 14.4572H7.55073L7.85236 9.88334Z"])'
  8493. )
  8494. );
  8495. if (DouYinRouter.isSearch()) {
  8496. result.push(
  8497. CommonUtil.addBlockCSS(
  8498. // 2024.8.12
  8499. 'div[id^="douyin-header-menu"] > div > div > ul:has(path[d="M11.9998 4.50037C9.02034 4.50037 6.55167 6.81159 6.35561 9.78463L5.94855 15.9572H18.0507L17.6441 9.78506C17.4482 6.81184 14.9795 4.50037 11.9998 4.50037ZM7.85236 9.88334C7.99643 7.6987 9.81045 6.00037 11.9998 6.00037C14.1893 6.00037 16.0034 7.69888 16.1473 9.88365L16.4486 14.4572H7.55073L7.85236 9.88334Z"])'
  8500. )
  8501. );
  8502. } else if (DouYinRouter.isLive()) {
  8503. result.push(
  8504. CommonUtil.addBlockCSS(
  8505. // 直播
  8506. 'div[id^="douyin-header-menu"] pace-island[id^="island"] > * > :has(path[d="M11.9998 4.50037C9.02034 4.50037 6.55167 6.81159 6.35561 9.78463L5.94855 15.9572H18.0507L17.6441 9.78506C17.4482 6.81184 14.9795 4.50037 11.9998 4.50037ZM7.85236 9.88334C7.99643 7.6987 9.81045 6.00037 11.9998 6.00037C14.1893 6.00037 16.0034 7.69888 16.1473 9.88365L16.4486 14.4572H7.55073L7.85236 9.88334Z"])'
  8507. )
  8508. );
  8509. }
  8510. return result;
  8511. },
  8512. /**
  8513. * 【屏蔽】私信
  8514. */
  8515. shieldPrivateMessage() {
  8516. log.info("【屏蔽】私信");
  8517. let result = [];
  8518. result.push(
  8519. CommonUtil.addBlockCSS(
  8520. '#douyin-right-container pace-island[id^="island"] > div[class]:has(div[data-e2e="something-button"]) > ul:has(div[data-e2e="im-entry"])',
  8521. // 直播
  8522. '#douyin-header pace-island[id^="island"] > div[class]:has(div[data-e2e="something-button"]) > ul:has(div[data-e2e="im-entry"])'
  8523. )
  8524. );
  8525. if (DouYinRouter.isSearch()) {
  8526. log.info("搜索-【屏蔽】私信");
  8527. result.push(
  8528. CommonUtil.addBlockCSS(
  8529. 'ul:has( div>div[data-e2e="im-entry"] )',
  8530. // 2024.7.15
  8531. 'div[id^="douyin-header-menu"] > div > div > ul:has([data-e2e="im-entry"])'
  8532. )
  8533. );
  8534. }
  8535. return result;
  8536. },
  8537. /**
  8538. * 【屏蔽】投稿
  8539. */
  8540. shieldSubmission() {
  8541. log.info("【屏蔽】投稿");
  8542. let result = [];
  8543. const iconPath = `d="M11.3487 4.90125H11.3164H11.3164C10.2479 4.90124 9.40104 4.90124 8.71799 4.95587C8.01959 5.01173 7.42807 5.12824 6.88626 5.39747C5.95866 5.8584 5.20716 6.60991 4.74622 7.53751C4.477 8.07932 4.36048 8.67084 4.30462 9.36923C4.24999 10.0523 4.24999 10.8991 4.25 11.9677V12V12.0322C4.24999 13.1008 4.24999 13.9477 4.30462 14.6307C4.36048 15.3291 4.477 15.9206 4.74622 16.4624C5.20716 17.39 5.95866 18.1415 6.88626 18.6025C7.42807 18.8717 8.01959 18.9882 8.71799 19.0441C9.40104 19.0987 10.2479 19.0987 11.3164 19.0987H11.3487H12.6513H12.6836C13.7521 19.0987 14.599 19.0987 15.282 19.0441C15.9804 18.9882 16.5719 18.8717 17.1137 18.6025C18.0413 18.1415 18.7928 17.39 19.2538 16.4624C19.523 15.9206 19.6395 15.3291 19.6954 14.6307C19.75 13.9477 19.75 13.1008 19.75 12.0322V12V11.9677C19.75 10.8991 19.75 10.0523 19.6954 9.36923C19.6395 8.67084 19.523 8.07932 19.2538 7.53751C18.7928 6.60991 18.0413 5.8584 17.1137 5.39747C16.5719 5.12824 15.9804 5.01173 15.282 4.95587C14.599 4.90124 13.7521 4.90124 12.6836 4.90125H12.6513H11.3487ZM7.55376 6.74077C7.8529 6.59212 8.22981 6.4997 8.83757 6.45109C9.45382 6.4018 10.2407 6.40125 11.3487 6.40125H12.6513C13.7593 6.40125 14.5462 6.4018 15.1624 6.45109C15.7702 6.4997 16.1471 6.59212 16.4462 6.74077C17.0809 7.05614 17.5951 7.57033 17.9105 8.205C18.0591 8.50414 18.1515 8.88105 18.2002 9.48882C18.2494 10.1051 18.25 10.8919 18.25 12C18.25 13.108 18.2494 13.8949 18.2002 14.5111C18.1515 15.1189 18.0591 15.4958 17.9105 15.7949C17.5951 16.4296 17.0809 16.9438 16.4462 17.2592C16.1471 17.4078 15.7702 17.5002 15.1624 17.5488C14.5462 17.5981 13.7593 17.5987 12.6513 17.5987H11.3487C10.2407 17.5987 9.45382 17.5981 8.83757 17.5488C8.22981 17.5002 7.8529 17.4078 7.55376 17.2592C6.91909 16.9438 6.4049 16.4296 6.08952 15.7949C5.94088 15.4958 5.84846 15.1189 5.79985 14.5111C5.75056 13.8949 5.75 13.108 5.75 12C5.75 10.8919 5.75056 10.1051 5.79985 9.48882C5.84846 8.88105 5.94088 8.50414 6.08952 8.205C6.4049 7.57033 6.91909 7.05614 7.55376 6.74077ZM11.25 15V12.75H9V11.25H11.25V8.99997H12.75V11.25H15V12.75H12.75V15H11.25Z"`;
  8544. result.push(
  8545. CommonUtil.addBlockCSS(
  8546. // 2024.8.12 更新规则
  8547. `div[id^="douyin-header-menu"] pace-island > div > div:has(path[${iconPath}])`
  8548. )
  8549. );
  8550. if (DouYinRouter.isSearch()) {
  8551. result.push(
  8552. CommonUtil.addBlockCSS(
  8553. // 2024.8.12
  8554. `div[id^="douyin-header-menu"] > div > div > div:has(path[${iconPath}])`
  8555. )
  8556. );
  8557. } else if (DouYinRouter.isLive()) {
  8558. result.push(
  8559. CommonUtil.addBlockCSS(
  8560. '#douyin-header pace-island[id^="island"] > div[class]:has(div[data-e2e="something-button"]) > :has(ul[data-e2e="cooperate-list"])'
  8561. )
  8562. );
  8563. }
  8564. return result;
  8565. },
  8566. /**
  8567. * 【屏蔽】客户端提示
  8568. */
  8569. shieldClientTip() {
  8570. log.info("【屏蔽】客户端提示");
  8571. let result = [];
  8572. result.push(
  8573. CommonUtil.addBlockCSS(
  8574. /* 右上角 通知 下载客户端,实时接收消息通知 */
  8575. 'ul li div[data-e2e="something-button"] + div div:has(>a[download*="douyin-downloader"])',
  8576. /* 右上角 个人信息 客户端登录(不可用)访问更便捷 [下载] */
  8577. '#douyin-header pace-island[id^="island_"] ul > div:has(>a[class][download])',
  8578. /* 右上角 私信 下载客户端,实时接收好友消息 */
  8579. '#douyin-header pace-island[id^="island_"] ul[class] li div[data-e2e="im-entry"] div>div div div:has(a[download][href])',
  8580. /* 右上角 壁纸 下载客户端,使用壁纸 */
  8581. '#douyin-header header div[id^="douyin-header-menu"] pace-island[id^="island_"] .dy-tip-container div:has(+ #wallpaper-modal)'
  8582. )
  8583. );
  8584. if (DouYinRouter.isSearch()) {
  8585. result.push(
  8586. CommonUtil.addBlockCSS(
  8587. /* 右上角 私信 下载客户端,实时接收好友消息 */
  8588. 'div[id^="douyin-header-menu"] ul li div[data-e2e="im-entry"] div > div > div:has(>a[download*="douyin-downloader"])',
  8589. /* 右上角 个人信息 客户端登录(不可用)访问更便捷 [下载] */
  8590. 'div[id^="douyin-header-menu"] ul > div:has(>a[download*="douyin-downloader"])'
  8591. )
  8592. );
  8593. }
  8594. return result;
  8595. },
  8596. /**
  8597. * 【屏蔽】壁纸
  8598. */
  8599. shieldWallpaper() {
  8600. log.info("【屏蔽】壁纸");
  8601. let result = [];
  8602. result.push(
  8603. CommonUtil.addBlockCSS(
  8604. // 2024.8.12
  8605. 'div[id^="douyin-header-menu"] pace-island > div > div:has(span.semi-icon path[d="M9.10335 4.79386C8.86882 4.64984 8.57425 4.64585 8.3359 4.78346C8.09755 4.92108 7.95372 5.17818 7.96117 5.4533L8.05873 9.05336L5.31808 11.3898C5.10864 11.5683 5.01381 11.8473 5.07104 12.1165C5.12826 12.3857 5.32833 12.6019 5.59229 12.6798L9.0463 13.6995L10.4215 17.028C10.5266 17.2824 10.7625 17.4588 11.0362 17.4875C11.3099 17.5163 11.5774 17.3929 11.7331 17.1659L13.3237 14.8471L16.4638 19.3577L17.6949 18.5007L14.6505 14.1276L17.3608 13.9168C17.6352 13.8954 17.8758 13.7255 17.9878 13.4741C18.0997 13.2226 18.065 12.9301 17.8972 12.7119L15.7022 9.85673L16.5462 6.35562C16.6107 6.08806 16.5234 5.80667 16.3189 5.62251C16.1144 5.43835 15.8254 5.38101 15.566 5.47312L12.1723 6.67838L9.10335 4.79386ZM9.56789 9.37117L9.49812 6.79649L11.693 8.14425C11.8862 8.26291 12.1227 8.28777 12.3364 8.21188L14.7635 7.34991L14.16 9.85382C14.1068 10.0743 14.1563 10.3069 14.2945 10.4867L15.8643 12.5286L13.2964 12.7284C13.0704 12.746 12.8644 12.8649 12.7361 13.0519L11.2792 15.1758L10.2957 12.7954C10.2091 12.5858 10.0324 12.4267 9.81491 12.3624L7.34469 11.6332L9.30473 9.96224C9.47729 9.81513 9.57403 9.59784 9.56789 9.37117Z"])'
  8606. )
  8607. );
  8608. if (DouYinRouter.isSearch()) {
  8609. result.push(
  8610. CommonUtil.addBlockCSS(
  8611. // 2024.8.12
  8612. 'div[id^="douyin-header-menu"] > div > div > div:has(span.semi-icon path[d="M9.10335 4.79386C8.86882 4.64984 8.57425 4.64585 8.3359 4.78346C8.09755 4.92108 7.95372 5.17818 7.96117 5.4533L8.05873 9.05336L5.31808 11.3898C5.10864 11.5683 5.01381 11.8473 5.07104 12.1165C5.12826 12.3857 5.32833 12.6019 5.59229 12.6798L9.0463 13.6995L10.4215 17.028C10.5266 17.2824 10.7625 17.4588 11.0362 17.4875C11.3099 17.5163 11.5774 17.3929 11.7331 17.1659L13.3237 14.8471L16.4638 19.3577L17.6949 18.5007L14.6505 14.1276L17.3608 13.9168C17.6352 13.8954 17.8758 13.7255 17.9878 13.4741C18.0997 13.2226 18.065 12.9301 17.8972 12.7119L15.7022 9.85673L16.5462 6.35562C16.6107 6.08806 16.5234 5.80667 16.3189 5.62251C16.1144 5.43835 15.8254 5.38101 15.566 5.47312L12.1723 6.67838L9.10335 4.79386ZM9.56789 9.37117L9.49812 6.79649L11.693 8.14425C11.8862 8.26291 12.1227 8.28777 12.3364 8.21188L14.7635 7.34991L14.16 9.85382C14.1068 10.0743 14.1563 10.3069 14.2945 10.4867L15.8643 12.5286L13.2964 12.7284C13.0704 12.746 12.8644 12.8649 12.7361 13.0519L11.2792 15.1758L10.2957 12.7954C10.2091 12.5858 10.0324 12.4267 9.81491 12.3624L7.34469 11.6332L9.30473 9.96224C9.47729 9.81513 9.57403 9.59784 9.56789 9.37117Z"])'
  8613. )
  8614. );
  8615. } else if (DouYinRouter.isLive()) {
  8616. result.push(
  8617. CommonUtil.addBlockCSS(
  8618. '#douyin-header header div[id^="douyin-header-menu"] pace-island[id^="island_"] .dy-tip-container:has(span.semi-icon)',
  8619. '#douyin-header pace-island[id^="island"] > div[class] span:has(.semi-icon)'
  8620. )
  8621. );
  8622. }
  8623. return result;
  8624. },
  8625. /**
  8626. * 屏蔽底部问题按钮
  8627. */
  8628. shieldBottomQuestionButton() {
  8629. log.info("屏蔽底部问题按钮");
  8630. return CommonUtil.addBlockCSS([
  8631. "#douyin-sidebar",
  8632. /* 推荐视频右下角的?按钮 */
  8633. "#douyin-temp-sidebar"
  8634. ]);
  8635. },
  8636. /**
  8637. * 【屏蔽】右侧菜单栏
  8638. */
  8639. shieldRightMenu() {
  8640. log.info(`【屏蔽】右侧菜单栏`);
  8641. return CommonUtil.addBlockCSS(`div[id^="douyin-header-menu"]`);
  8642. },
  8643. /**
  8644. * 【屏蔽】更多
  8645. */
  8646. shieldRightMenuMore() {
  8647. log.info(`【屏蔽】更多`);
  8648. return CommonUtil.addBlockCSS(
  8649. `#douyin-header header div[id^="douyin-header-menu"] pace-island > div > div:has(path[d="M17 8.75H7V7.25H17V8.75ZM17 12.75H7V11.25H17V12.75ZM7 16.75H17V15.25H7V16.75Z"])`
  8650. );
  8651. },
  8652. /**
  8653. * 【屏蔽】登录(不可用)头像
  8654. */
  8655. shieldRightMenuLoginAvatar() {
  8656. log.info(`【屏蔽】登录(不可用)头像`);
  8657. return CommonUtil.addBlockCSS(
  8658. // 未登录(不可用)
  8659. `#douyin-header header div[id^="douyin-header-menu"] pace-island > div > div:has(path[d="M6.484 43.177c4.765-5.408 11.743-8.821 19.517-8.821 7.775 0 14.753 3.413 19.517 8.821C40.754 48.587 33.776 52 26.001 52c-7.774 0-14.752-3.413-19.517-8.822zM35.287 21.356a9.286 9.286 0 1 1-18.571 0 9.286 9.286 0 0 1 18.571 0z"])`,
  8660. // 已登录(不可用)
  8661. `#douyin-header header div[id^="douyin-header-menu"] pace-island > div > div:has([data-e2e="live-avatar"])`
  8662. );
  8663. }
  8664. };
  8665. const BlockSearchFrame = {
  8666. init() {
  8667. PopsPanel.execMenuOnce("shieldSearch", () => {
  8668. return this.shieldSearch();
  8669. });
  8670. PopsPanel.execMenuOnce("shieldSearchPlaceholder", () => {
  8671. return this.shieldSearchPlaceholder();
  8672. });
  8673. PopsPanel.execMenuOnce("shieldSearchGuessYouWantToSearch", () => {
  8674. return this.shieldSearchGuessYouWantToSearch();
  8675. });
  8676. PopsPanel.execMenuOnce("shieldSearchTiktokHotspot", () => {
  8677. return this.shieldSearchTiktokHotspot();
  8678. });
  8679. },
  8680. /**
  8681. * 【屏蔽】搜索框
  8682. */
  8683. shieldSearch() {
  8684. log.info("【屏蔽】搜索框");
  8685. return CommonUtil.addBlockCSS(
  8686. '#douyin-header div[data-click="doubleClick"] > div[data-click="doubleClick"] > div:has(input[data-e2e="searchbar-input"])'
  8687. );
  8688. },
  8689. /**
  8690. * 【屏蔽】搜索框的提示
  8691. */
  8692. shieldSearchPlaceholder() {
  8693. log.info("【屏蔽】搜索框的提示");
  8694. let result = [];
  8695. result.push(
  8696. CommonUtil.addBlockCSS(
  8697. '#douyin-header div[data-click="doubleClick"] > div[data-click="doubleClick"] > div div:has( + input[data-e2e="searchbar-input"])'
  8698. )
  8699. );
  8700. result.push(
  8701. addStyle(
  8702. /*css*/
  8703. `
  8704. #douyin-header div[data-click="doubleClick"] > div[data-click="doubleClick"] > div input[data-e2e="searchbar-input"]::placeholder{
  8705. color: transparent;
  8706. }`
  8707. )
  8708. );
  8709. return result;
  8710. },
  8711. /**
  8712. * 【屏蔽】搜索-猜你想搜
  8713. */
  8714. shieldSearchGuessYouWantToSearch() {
  8715. log.info("【屏蔽】搜索-猜你想搜");
  8716. return CommonUtil.addBlockCSS(
  8717. 'button[data-e2e="searchbar-button"] + div div:has( + div[data-e2e="search-guess-container"])',
  8718. 'button[data-e2e="searchbar-button"] + div div[data-e2e="search-guess-container"]'
  8719. );
  8720. },
  8721. /**
  8722. * 【屏蔽】搜索-抖音热点
  8723. */
  8724. shieldSearchTiktokHotspot() {
  8725. log.info("【屏蔽】搜索-抖音热点");
  8726. return CommonUtil.addBlockCSS(
  8727. 'button[data-e2e="searchbar-button"] + div div:has( + div[data-e2e="search-hot-container"])',
  8728. 'button[data-e2e="searchbar-button"] + div div[data-e2e="search-hot-container"]'
  8729. );
  8730. }
  8731. };
  8732. const Hook = {
  8733. $data: {
  8734. document_addEventListener: [],
  8735. element_addEventListener: [],
  8736. setTimeout: [],
  8737. setInterval: [],
  8738. function_apply: [],
  8739. function_call: [],
  8740. defineProperty: []
  8741. },
  8742. /**
  8743. * 劫持 document.addEventListener
  8744. * @param handler
  8745. */
  8746. document_addEventListener(handler) {
  8747. this.$data.document_addEventListener.push(handler);
  8748. log.info("document.addEventListener hook新增劫持判断回调");
  8749. if (this.$data.document_addEventListener.length > 1) {
  8750. return;
  8751. }
  8752. const that = this;
  8753. let weakMap = /* @__PURE__ */ new WeakMap();
  8754. const originAddEventListener = _unsafeWindow.document.addEventListener;
  8755. const originRemoveEventListener = _unsafeWindow.document.removeEventListener;
  8756. _unsafeWindow.document.addEventListener = function(...args) {
  8757. let target = this;
  8758. let eventName = args[0];
  8759. let listener = args[1];
  8760. let options = args[2];
  8761. for (let index = 0; index < that.$data.document_addEventListener.length; index++) {
  8762. const callback = that.$data.document_addEventListener[index];
  8763. const result = Reflect.apply(callback, this, [
  8764. target,
  8765. eventName,
  8766. listener,
  8767. options
  8768. ]);
  8769. if (typeof result === "function") {
  8770. args[1] = result;
  8771. weakMap.set(listener, {
  8772. eventName,
  8773. fn: result,
  8774. options
  8775. });
  8776. break;
  8777. } else if (typeof result === "boolean" && !result) {
  8778. return;
  8779. }
  8780. }
  8781. return Reflect.apply(originAddEventListener, this, args);
  8782. };
  8783. _unsafeWindow.document.removeEventListener = function(...args) {
  8784. let eventName = args[0];
  8785. let listener = args[1];
  8786. let options = args[2];
  8787. if (weakMap.has(listener)) {
  8788. const {
  8789. eventName: __eventName__,
  8790. fn: __listener__,
  8791. options: __options__
  8792. } = weakMap.get(listener);
  8793. let flag = false;
  8794. if (eventName === __eventName__) {
  8795. if (typeof options === "boolean" && options === __options__) {
  8796. flag = true;
  8797. } else if (typeof options === "object" && typeof __options__ === "object" && options["capture"] === __options__["capture"]) {
  8798. flag = true;
  8799. } else if (options == options) {
  8800. flag = true;
  8801. }
  8802. }
  8803. if (flag) {
  8804. args[1] = __listener__;
  8805. }
  8806. }
  8807. return Reflect.apply(originRemoveEventListener, this, args);
  8808. };
  8809. },
  8810. /**
  8811. * 劫持 Element.property.addEventListener
  8812. * @param handler
  8813. */
  8814. element_addEventListener(handler) {
  8815. this.$data.element_addEventListener.push(handler);
  8816. log.info("Element.prototype.addEventListener hook新增劫持判断回调");
  8817. if (this.$data.element_addEventListener.length > 1) {
  8818. return;
  8819. }
  8820. const that = this;
  8821. let weakMap = /* @__PURE__ */ new WeakMap();
  8822. const originAddEventListener = _unsafeWindow.Element.prototype.addEventListener;
  8823. const originRemoveEventListener = _unsafeWindow.Element.prototype.removeEventListener;
  8824. _unsafeWindow.Element.prototype.addEventListener = function(...args) {
  8825. let target = this;
  8826. let eventName = args[0];
  8827. let listener = args[1];
  8828. let options = args[2];
  8829. for (let index = 0; index < that.$data.element_addEventListener.length; index++) {
  8830. const callback = that.$data.element_addEventListener[index];
  8831. const result = Reflect.apply(callback, this, [
  8832. target,
  8833. eventName,
  8834. listener,
  8835. options
  8836. ]);
  8837. if (typeof result === "function") {
  8838. args[1] = result;
  8839. weakMap.set(listener, {
  8840. eventName,
  8841. fn: result,
  8842. options
  8843. });
  8844. break;
  8845. } else if (typeof result === "boolean" && !result) {
  8846. return;
  8847. }
  8848. }
  8849. return Reflect.apply(originAddEventListener, this, args);
  8850. };
  8851. _unsafeWindow.Element.prototype.removeEventListener = function(...args) {
  8852. let eventName = args[0];
  8853. let listener = args[1];
  8854. let options = args[2];
  8855. if (weakMap.has(listener)) {
  8856. const {
  8857. eventName: __eventName__,
  8858. fn: __listener__,
  8859. options: __options__
  8860. } = weakMap.get(listener);
  8861. let flag = false;
  8862. if (__eventName__ === eventName) {
  8863. if (typeof options === "boolean" && options === __options__) {
  8864. flag = true;
  8865. } else if (typeof options === "object" && typeof __options__ === "object" && options["capture"] === __options__["capture"]) {
  8866. flag = true;
  8867. } else if (options == __options__) {
  8868. flag = true;
  8869. }
  8870. }
  8871. if (flag) {
  8872. args[1] = __listener__;
  8873. }
  8874. }
  8875. return Reflect.apply(originRemoveEventListener, this, args);
  8876. };
  8877. },
  8878. /**
  8879. * 劫持 window.setTimeout
  8880. *
  8881. * @param handler
  8882. */
  8883. setTimeout(handler) {
  8884. this.$data.setTimeout.push(handler);
  8885. log.info("window.setTimeout hook新增劫持");
  8886. if (this.$data.setTimeout.length > 1) {
  8887. return;
  8888. }
  8889. const that = this;
  8890. let originSetTimeout = _unsafeWindow.setTimeout;
  8891. _unsafeWindow.setTimeout = function(...args) {
  8892. let fn = args[0];
  8893. let timeout = args[1];
  8894. for (let index = 0; index < that.$data.setTimeout.length; index++) {
  8895. const item = that.$data.setTimeout[index];
  8896. const result = item(fn, timeout);
  8897. if (typeof result === "boolean" && !result) {
  8898. return;
  8899. }
  8900. }
  8901. return Reflect.apply(originSetTimeout, this, args);
  8902. };
  8903. },
  8904. /**
  8905. * 劫持 window.setInterval
  8906. * @param handler
  8907. */
  8908. setInterval(handler) {
  8909. this.$data.setInterval.push(handler);
  8910. log.info("window.setInterval hook新增劫持");
  8911. if (this.$data.setInterval.length > 1) {
  8912. return;
  8913. }
  8914. const that = this;
  8915. let originSetInterval = _unsafeWindow.setInterval;
  8916. _unsafeWindow.setInterval = function(...args) {
  8917. let fn = args[0];
  8918. let timeout = args[1];
  8919. for (let index = 0; index < that.$data.setInterval.length; index++) {
  8920. const item = that.$data.setInterval[index];
  8921. const result = item(fn, timeout);
  8922. if (typeof result === "boolean" && !result) {
  8923. return;
  8924. }
  8925. }
  8926. return Reflect.apply(originSetInterval, this, args);
  8927. };
  8928. },
  8929. /**
  8930. * 劫持 Function.prototype.apply
  8931. * @param handler
  8932. */
  8933. function_apply(handler) {
  8934. this.$data.function_apply.push(handler);
  8935. log.info("Function.prototype.apply hook新增劫持");
  8936. if (this.$data.function_apply.length > 1) {
  8937. return;
  8938. }
  8939. const that = this;
  8940. let originFunctionApply = _unsafeWindow.Function.prototype.apply;
  8941. _unsafeWindow.Function.prototype.apply = function(...args) {
  8942. let thisArg = args[0];
  8943. let argArray = args[1];
  8944. let context = this;
  8945. for (let index = 0; index < that.$data.function_apply.length; index++) {
  8946. const item = that.$data.function_apply[index];
  8947. const result = item(context, thisArg, argArray);
  8948. if (result != null) {
  8949. args[0] = result.thisArg;
  8950. args[1] = result.argArray;
  8951. context = result.context;
  8952. break;
  8953. }
  8954. }
  8955. return Reflect.apply(originFunctionApply, context, args);
  8956. };
  8957. },
  8958. /**
  8959. * 劫持 Function.prototype.call
  8960. * @param handler
  8961. */
  8962. function_call(handler) {
  8963. this.$data.function_call.push(handler);
  8964. log.info("Function.prototype.call hook新增劫持");
  8965. if (this.$data.function_call.length > 1) {
  8966. return;
  8967. }
  8968. const that = this;
  8969. let originFunctionCall = _unsafeWindow.Function.prototype.call;
  8970. _unsafeWindow.Function.prototype.call = function(...args) {
  8971. let thisArg = args[0];
  8972. let argArray = args.slice(1);
  8973. let context = this;
  8974. for (let index = 0; index < that.$data.function_call.length; index++) {
  8975. const item = that.$data.function_call[index];
  8976. const result = item(context, thisArg, argArray);
  8977. if (result != null) {
  8978. args[0] = result.thisArg;
  8979. args.splice(1, argArray.length, ...result.argArray);
  8980. context = result.context;
  8981. break;
  8982. }
  8983. }
  8984. return Reflect.apply(originFunctionCall, context, args);
  8985. };
  8986. },
  8987. /**
  8988. * 劫持 Object.defineProperty
  8989. * @package handler
  8990. */
  8991. defineProperty(handler) {
  8992. this.$data.defineProperty.push(handler);
  8993. log.info("Object.defineProperty hook新增劫持");
  8994. if (this.$data.defineProperty.length > 1) {
  8995. return;
  8996. }
  8997. const that = this;
  8998. let originDefineProperty = _unsafeWindow.Object.defineProperty;
  8999. _unsafeWindow.Object.defineProperty = function(...args) {
  9000. let target = args[0];
  9001. let key = args[1];
  9002. let attributes = args[2];
  9003. for (let index = 0; index < that.$data.defineProperty.length; index++) {
  9004. const item = that.$data.defineProperty[index];
  9005. const result = item(target, key, attributes);
  9006. if (result != null) {
  9007. args[0] = result.target;
  9008. args[1] = result.key;
  9009. args[2] = result.attributes;
  9010. break;
  9011. }
  9012. }
  9013. return Reflect.apply(originDefineProperty, this, args);
  9014. };
  9015. },
  9016. /**
  9017. * 劫持webpack
  9018. * @param webpackName 当前全局变量的webpack名
  9019. * @param mainCoreData 需要劫持的webpack的顶部core
  9020. * 例如:(window.webpackJsonp = window.webpackJsonp || []).push([["core:0"],{}])
  9021. * 此时mainCoreData是["core:0"]
  9022. * @param handler 如果mainCoreData匹配上,则调用此回调函数,替换的话把传入的值进行处理后再返回它就行
  9023. */
  9024. window_webpack(webpackName = "webpackJsonp", mainCoreData, handler) {
  9025. let originObject = void 0;
  9026. _unsafeWindow.Object.defineProperty(_unsafeWindow, webpackName, {
  9027. get() {
  9028. return originObject;
  9029. },
  9030. set(newValue) {
  9031. log.success("成功劫持webpack,当前webpack名:" + webpackName);
  9032. originObject = newValue;
  9033. const originPush = originObject.push;
  9034. originObject.push = function(...args) {
  9035. let _mainCoreData = args[0][0];
  9036. if (mainCoreData == _mainCoreData || Array.isArray(mainCoreData) && Array.isArray(_mainCoreData) && JSON.stringify(mainCoreData) === JSON.stringify(_mainCoreData)) {
  9037. Object.keys(args[0][1]).forEach((keyName) => {
  9038. let originSwitchFunc = args[0][1][keyName];
  9039. args[0][1][keyName] = function(..._args) {
  9040. let result = originSwitchFunc.call(this, ..._args);
  9041. _args[0] = handler(_args[0]);
  9042. return result;
  9043. };
  9044. });
  9045. }
  9046. return Reflect.apply(originPush, this, args);
  9047. };
  9048. }
  9049. });
  9050. }
  9051. };
  9052. const DouYinHook = {
  9053. $data: {
  9054. hookElementAddEventListener: []
  9055. },
  9056. init() {
  9057. PopsPanel.onceExec("hookKeyboard", () => {
  9058. DouYinHook.disableShortCut();
  9059. });
  9060. PopsPanel.execMenu("dy-cookie-remove__ac__", () => {
  9061. this.removeCookie();
  9062. });
  9063. if (DouYinRouter.isIndex()) {
  9064. PopsPanel.execMenuOnce("dy-video-disableDoubleClickLike", () => {
  9065. DouYinHook.disableDoubleClickLike();
  9066. });
  9067. } else if (DouYinRouter.isLive()) {
  9068. PopsPanel.execMenuOnce("dy-live-disableDoubleClickLike", () => {
  9069. DouYinHook.disableDoubleClickLike();
  9070. });
  9071. }
  9072. },
  9073. /**
  9074. * 移除环境检测
  9075. */
  9076. removeEnvCheck() {
  9077. log.info("移除环境检测");
  9078. let originalSetInterval = _unsafeWindow.setInterval;
  9079. _unsafeWindow.setInterval = function(callback, time) {
  9080. let funcStr = callback.toString().trim();
  9081. if (funcStr.includes("debugger")) {
  9082. log.success(["拦截→", [funcStr]]);
  9083. return;
  9084. }
  9085. if (funcStr.includes("checkEXp")) {
  9086. log.success(["拦截→", [funcStr]]);
  9087. return;
  9088. }
  9089. return originalSetInterval.call(this, callback, time);
  9090. };
  9091. },
  9092. /**
  9093. * 移除Cookie
  9094. */
  9095. removeCookie() {
  9096. let cookieHandler = new utils.GM_Cookie();
  9097. let cookieNameList = ["__ac_signature", "__ac_referer", "__ac_nonce"];
  9098. cookieNameList.forEach((cookieName) => {
  9099. cookieHandler.delete(
  9100. {
  9101. name: cookieName,
  9102. firstPartyDomain: ""
  9103. },
  9104. (error) => {
  9105. if (error) {
  9106. log.error(`移除Cookie失败 ==> ${cookieName}`, error);
  9107. } else {
  9108. log.success(`移除Cookie成功 ==> ${cookieName}`);
  9109. }
  9110. }
  9111. );
  9112. });
  9113. },
  9114. /**
  9115. * 禁用快捷键
  9116. */
  9117. disableShortCut() {
  9118. Hook.document_addEventListener((target, eventName, listener, option) => {
  9119. if (["keydown", "keypress", "keyup"].includes(eventName) && typeof listener === "function") {
  9120. return function(...eventArgs) {
  9121. let event = eventArgs[0];
  9122. event.key;
  9123. let code = event.code;
  9124. event.charCode || event.keyCode || event.which;
  9125. let otherCodeList = [];
  9126. if (event.ctrlKey) {
  9127. otherCodeList.push("ctrl");
  9128. }
  9129. if (event.altKey) {
  9130. otherCodeList.push("alt");
  9131. }
  9132. if (event.metaKey) {
  9133. otherCodeList.push("meta");
  9134. }
  9135. if (event.shiftKey) {
  9136. otherCodeList.push("shift");
  9137. }
  9138. let keyboardConfigList = [
  9139. {
  9140. enableKey: "dy-keyboard-hook-likeOrDislike",
  9141. code: ["KeyZ"]
  9142. },
  9143. {
  9144. enableKey: "dy-keyboard-hook-comment",
  9145. code: ["KeyX"]
  9146. },
  9147. {
  9148. enableKey: "dy-keyboard-hook-danmaku-enable",
  9149. code: ["KeyB"]
  9150. },
  9151. {
  9152. enableKey: "dy-keyboard-hook-collect-enable",
  9153. code: ["KeyC"]
  9154. },
  9155. {
  9156. enableKey: "dy-keyboard-hook-copyShareLink",
  9157. code: ["KeyV"]
  9158. },
  9159. {
  9160. enableKey: "dy-keyboard-hook-clearScreen",
  9161. code: ["KeyJ"]
  9162. },
  9163. {
  9164. enableKey: "dy-keyboard-hook-automaticBroadcast",
  9165. code: ["KeyK"]
  9166. },
  9167. {
  9168. enableKey: "dy-keyboard-hook-videoInfo",
  9169. code: ["KeyI"]
  9170. },
  9171. {
  9172. enableKey: "dy-keyboard-hook-notInterested",
  9173. code: ["KeyR"]
  9174. },
  9175. {
  9176. enableKey: "dy-keyboard-hook-enterAuthorHomePage",
  9177. code: ["KeyF"]
  9178. },
  9179. {
  9180. enableKey: "dy-keyboard-hook-follow",
  9181. code: ["KeyG"]
  9182. },
  9183. {
  9184. enableKey: "dy-keyboard-hook-search",
  9185. code: ["KeyF"],
  9186. otherCodeList: ["shift"]
  9187. },
  9188. {
  9189. enableKey: "dy-keyboard-hook-closeTheCurrentPageWithOneClick",
  9190. code: ["KeyQ"],
  9191. otherCodeList: ["shift"]
  9192. },
  9193. {
  9194. enableKey: "dy-keyboard-hook-pageUpAndDown",
  9195. code: ["ArrowUp", "ArrowDown"]
  9196. },
  9197. {
  9198. enableKey: "dy-keyboard-hook-fastForwardAndFastBack",
  9199. code: ["ArrowLeft", "ArrowRight"]
  9200. },
  9201. {
  9202. enableKey: "dy-keyboard-hook-pause",
  9203. code: ["Space"]
  9204. },
  9205. {
  9206. enableKey: "dy-keyboard-hook-fullScreenInsideThePage",
  9207. code: ["KeyY"]
  9208. },
  9209. {
  9210. enableKey: "dy-keyboard-hook-fullScreen",
  9211. code: ["KeyH"]
  9212. },
  9213. {
  9214. enableKey: "dy-keyboard-hook-watchItOutLater",
  9215. code: ["KeyL"]
  9216. },
  9217. {
  9218. enableKey: "dy-keyboard-hook-volumeAdjustment",
  9219. code: ["Minus"],
  9220. otherCodeList: ["shift"]
  9221. },
  9222. {
  9223. enableKey: "dy-keyboard-hook-listOfCallShortcutKeys",
  9224. code: ["Slash"],
  9225. otherCodeList: ["shift"]
  9226. },
  9227. {
  9228. enableKey: "dy-keyboard-hook-closeTheShortcutKeyList",
  9229. code: ["Escape"]
  9230. },
  9231. {
  9232. enableKey: "dy-keyboard-hook-relevantRecommendation",
  9233. code: ["KeyN"]
  9234. }
  9235. ];
  9236. if (DouYinRouter.isIndex()) {
  9237. keyboardConfigList.push(
  9238. {
  9239. enableKey: "dy-keyboard-hook-arrowUp-w",
  9240. code: ["KeyW"]
  9241. },
  9242. {
  9243. enableKey: "dy-keyboard-hook-arrowDown-s",
  9244. code: ["KeyS"]
  9245. },
  9246. {
  9247. enableKey: "dy-keyboard-hook-videoRewind",
  9248. code: ["KeyA"]
  9249. },
  9250. {
  9251. enableKey: "dy-keyboard-hook-videoFastForward",
  9252. code: ["KeyD"]
  9253. }
  9254. );
  9255. } else if (DouYinRouter.isLive()) {
  9256. keyboardConfigList.push(
  9257. {
  9258. enableKey: "dy-live-refresh",
  9259. code: ["KeyE"]
  9260. },
  9261. {
  9262. enableKey: "dy-live-screenRotation",
  9263. code: ["KeyD"]
  9264. },
  9265. {
  9266. enableKey: "dy-live-enableSmallWindowMode",
  9267. code: ["KeyU"]
  9268. }
  9269. );
  9270. }
  9271. for (let index = 0; index < keyboardConfigList.length; index++) {
  9272. const keyboardConfig = keyboardConfigList[index];
  9273. if (keyboardConfig.code.includes(code)) {
  9274. if (Array.isArray(keyboardConfig.otherCodeList)) {
  9275. let findValue = keyboardConfig.otherCodeList.find(
  9276. (item) => !otherCodeList.includes(item)
  9277. );
  9278. if (findValue) {
  9279. continue;
  9280. }
  9281. }
  9282. if (!PopsPanel.getValue(keyboardConfig.enableKey)) {
  9283. continue;
  9284. }
  9285. return;
  9286. }
  9287. }
  9288. return Reflect.apply(listener, this, eventArgs);
  9289. };
  9290. }
  9291. });
  9292. },
  9293. /**
  9294. * 禁用双击点赞
  9295. */
  9296. disableDoubleClickLike() {
  9297. let latestClickTime = Date.now();
  9298. Hook.element_addEventListener((target, eventName, listener, option) => {
  9299. var _a2;
  9300. const listenerStr = listener.toString();
  9301. if (eventName === "click" && target instanceof HTMLElement && ((_a2 = target == null ? void 0 : target.classList) == null ? void 0 : _a2.contains("xgplayer")) && listenerStr.match(/video|innerContainer|video.__canvas|mouse/)) {
  9302. return function(...eventArgs) {
  9303. let currentClickTime = Date.now();
  9304. if (currentClickTime - latestClickTime <= 288) {
  9305. latestClickTime = currentClickTime;
  9306. log.success("阻止触发双击点赞");
  9307. return;
  9308. }
  9309. latestClickTime = currentClickTime;
  9310. Reflect.apply(listener, this, eventArgs);
  9311. };
  9312. }
  9313. });
  9314. }
  9315. };
  9316. const DouYinElement = {
  9317. /**
  9318. * 观察 #slidelist的加载每条视频
  9319. * @param callback
  9320. */
  9321. watchFeedVideoListChange(callback) {
  9322. let $os = null;
  9323. domUtils.ready(() => {
  9324. utils.waitAnyNode([
  9325. "#slidelist",
  9326. // 搜索页面的↓搜索结果列表
  9327. '#search-content-area ul[data-e2e="scroll-list"]'
  9328. ]).then(($ele) => {
  9329. log.info(`启用观察器观察加载的视频`);
  9330. let lockFn = new utils.LockFunction((observer) => {
  9331. $os = $os || this.getOSElement();
  9332. if (!$os) {
  9333. log.error("watchVideDataListChange:获取osElement失败");
  9334. return;
  9335. }
  9336. callback($os, observer);
  9337. }, 50);
  9338. utils.mutationObserver(document, {
  9339. config: {
  9340. childList: true,
  9341. subtree: true
  9342. },
  9343. immediate: true,
  9344. callback: (mutations, observer) => {
  9345. lockFn.run(observer);
  9346. }
  9347. });
  9348. });
  9349. });
  9350. },
  9351. getOSElement() {
  9352. return $("#root div[class*='-os']") || $("#douyin-right-container");
  9353. }
  9354. };
  9355. const DouYinAccount = {
  9356. /**
  9357. * 伪装登录(不可用)
  9358. */
  9359. disguiseLogin() {
  9360. log.info("伪装登录(不可用)");
  9361. const WAIT_TIME = 2e4;
  9362. let uid = 114514;
  9363. let info = {
  9364. uid,
  9365. secUid: "",
  9366. shortId: "",
  9367. realName: "",
  9368. nickname: "乌萨奇",
  9369. // 昵称
  9370. desc: "除草证3级",
  9371. // 描述
  9372. gender: 0,
  9373. // 性别
  9374. avatarUrl: "https://www.z4a.net/images/2025/02/28/008DOnfHgy1hxpz9zshl4g30hs0hsnpj.gif",
  9375. // 头像
  9376. avatar300Url: "https://www.z4a.net/images/2025/02/28/008DOnfHgy1hxpz9zshl4g30hs0hsnpj.gif",
  9377. followStatus: 0,
  9378. followerStatus: 0,
  9379. awemeCount: 0,
  9380. // 作品数量
  9381. watchLaterCount: 0,
  9382. // 稍后再看数量
  9383. followingCount: 0,
  9384. // 关注
  9385. followerCount: 0,
  9386. followerCountStr: "",
  9387. mplatformFollowersCount: 9999999,
  9388. // 粉丝数量
  9389. favoritingCount: 0,
  9390. // 我的喜欢的数量
  9391. totalFavorited: 9999999,
  9392. // 获赞
  9393. userCollectCount: {
  9394. logPb: {
  9395. impr_id: ""
  9396. },
  9397. collectCountList: [],
  9398. statusCode: 0,
  9399. extra: {
  9400. fatal_item_ids: [],
  9401. logid: "",
  9402. now: Date.now()
  9403. }
  9404. },
  9405. uniqueId: "",
  9406. customVerify: "",
  9407. generalPermission: {
  9408. is_hit_active_fans_grayed: false
  9409. },
  9410. age: (/* @__PURE__ */ new Date()).getFullYear() - 2019,
  9411. // 年龄
  9412. country: "",
  9413. province: "",
  9414. city: "",
  9415. district: "",
  9416. school: "chiikawa",
  9417. // 学校
  9418. schoolVisible: 1,
  9419. // 控制学校显示
  9420. enterpriseVerifyReason: "",
  9421. secret: 1,
  9422. userCanceled: false,
  9423. roomData: {},
  9424. shareQrcodeUrl: "",
  9425. shareInfo: void 0,
  9426. coverAndHeadImageInfo: {
  9427. profileCoverList: []
  9428. },
  9429. roomId: 0,
  9430. favoritePermission: 1,
  9431. viewHistoryPermission: true,
  9432. isGovMediaVip: false,
  9433. isStar: false,
  9434. hideLocation: false,
  9435. needSpecialShowFollowerCount: false,
  9436. continuationState: 0,
  9437. im_role_ids: [],
  9438. accountCertInfo: {},
  9439. close_consecutive_chat: 0
  9440. };
  9441. function getUserInfo(element) {
  9442. var _a2, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A, _B, _C, _D;
  9443. let userInfoList = [];
  9444. let reactInstance = utils.getReactObj(element);
  9445. let reactFiber = reactInstance == null ? void 0 : reactInstance.reactFiber;
  9446. reactInstance == null ? void 0 : reactInstance.reactProps;
  9447. if ((_c = (_b = (_a2 = reactFiber == null ? void 0 : reactFiber.alternate) == null ? void 0 : _a2.return) == null ? void 0 : _b.memoizedProps) == null ? void 0 : _c.userInfo) {
  9448. userInfoList.push(
  9449. (_f = (_e = (_d = reactFiber == null ? void 0 : reactFiber.alternate) == null ? void 0 : _d.return) == null ? void 0 : _e.memoizedProps) == null ? void 0 : _f.userInfo
  9450. );
  9451. }
  9452. if ((_j = (_i = (_h = (_g = reactFiber == null ? void 0 : reactFiber.alternate) == null ? void 0 : _g.return) == null ? void 0 : _h.memoizedProps) == null ? void 0 : _i.userInfo) == null ? void 0 : _j.userInfo) {
  9453. userInfoList.push(
  9454. (_m = (_l = (_k = reactFiber == null ? void 0 : reactFiber.alternate) == null ? void 0 : _k.return) == null ? void 0 : _l.memoizedProps) == null ? void 0 : _m.userInfo.userInfo
  9455. );
  9456. }
  9457. if ((_q = (_p = (_o = (_n = reactFiber == null ? void 0 : reactFiber.alternate) == null ? void 0 : _n.return) == null ? void 0 : _o.return) == null ? void 0 : _p.memoizedProps) == null ? void 0 : _q.userInfo) {
  9458. userInfoList.push(
  9459. (_u = (_t = (_s = (_r = reactFiber == null ? void 0 : reactFiber.alternate) == null ? void 0 : _r.return) == null ? void 0 : _s.return) == null ? void 0 : _t.memoizedProps) == null ? void 0 : _u.userInfo
  9460. );
  9461. }
  9462. if ((_z = (_y = (_x = (_w = (_v = reactFiber == null ? void 0 : reactFiber.alternate) == null ? void 0 : _v.return) == null ? void 0 : _w.return) == null ? void 0 : _x.memoizedProps) == null ? void 0 : _y.userInfo) == null ? void 0 : _z.userInfo) {
  9463. userInfoList.push(
  9464. (_D = (_C = (_B = (_A = reactFiber == null ? void 0 : reactFiber.alternate) == null ? void 0 : _A.return) == null ? void 0 : _B.return) == null ? void 0 : _C.memoizedProps) == null ? void 0 : _D.userInfo.userInfo
  9465. );
  9466. }
  9467. return userInfoList;
  9468. }
  9469. function setLogin(element) {
  9470. getUserInfo(element).forEach((userInfo) => {
  9471. if (!userInfo.isLogin) {
  9472. userInfo.info = info;
  9473. userInfo.isLogin = true;
  9474. userInfo.statusCode = 0;
  9475. }
  9476. });
  9477. }
  9478. DouYinElement.watchFeedVideoListChange(($os) => {
  9479. setLogin($os);
  9480. });
  9481. utils.waitNode("#root div[class*='-os']", WAIT_TIME).then(() => {
  9482. let lockFn = new utils.LockFunction(() => {
  9483. let $os = DouYinElement.getOSElement();
  9484. if (!$os) {
  9485. return;
  9486. }
  9487. setLogin($os);
  9488. }, 70);
  9489. utils.mutationObserver(document.body, {
  9490. config: {
  9491. subtree: true,
  9492. childList: true
  9493. },
  9494. immediate: true,
  9495. callback: () => {
  9496. lockFn.run();
  9497. }
  9498. });
  9499. }).catch((err) => {
  9500. });
  9501. if (DouYinRouter.isLive()) {
  9502. log.info("伪装登录(不可用):live");
  9503. utils.waitNode(
  9504. `[id^="douyin-header"] div:has(.dy-tip-container)`,
  9505. WAIT_TIME
  9506. ).then(() => {
  9507. let lockFn = new utils.LockFunction(() => {
  9508. setLogin($(`[id^="douyin-header"]`));
  9509. }, 70);
  9510. utils.mutationObserver(document.body, {
  9511. config: {
  9512. subtree: true,
  9513. childList: true
  9514. },
  9515. callback: () => {
  9516. lockFn.run();
  9517. }
  9518. });
  9519. });
  9520. } else if (DouYinRouter.isSearch()) {
  9521. let setUserInfoBySearch = function($ele) {
  9522. var _a2, _b, _c, _d, _e, _f, _g;
  9523. let $react = utils.getReactObj($ele);
  9524. $react == null ? void 0 : $react.reactFiber;
  9525. let reactProps = $react == null ? void 0 : $react.reactProps;
  9526. if (typeof ((_d = (_c = (_b = (_a2 = reactProps == null ? void 0 : reactProps.children) == null ? void 0 : _a2[1]) == null ? void 0 : _b.props) == null ? void 0 : _c.userInfo) == null ? void 0 : _d.isLogin) === "boolean") {
  9527. Reflect.set(reactProps.children[1].props.userInfo, "isLogin", true);
  9528. }
  9529. if (typeof ((_g = (_f = (_e = reactProps == null ? void 0 : reactProps.children) == null ? void 0 : _e[1]) == null ? void 0 : _f.props) == null ? void 0 : _g.isClient) === "boolean") {
  9530. Reflect.set(reactProps.children[1].props, "isClient", true);
  9531. }
  9532. };
  9533. log.info("伪装登录(不可用):search");
  9534. utils.waitNode("#root > div", WAIT_TIME).then(($rootDiv) => {
  9535. if (!$rootDiv) {
  9536. log.error("#root > div获取失败");
  9537. return;
  9538. }
  9539. let lockFn = new utils.LockFunction(() => {
  9540. setUserInfoBySearch($rootDiv);
  9541. }, 70);
  9542. utils.mutationObserver(document, {
  9543. config: {
  9544. subtree: true,
  9545. childList: true
  9546. },
  9547. callback: () => {
  9548. lockFn.run();
  9549. }
  9550. });
  9551. });
  9552. }
  9553. },
  9554. /**
  9555. * 关闭登录(不可用)弹窗
  9556. */
  9557. watchLoginDialogToClose() {
  9558. log.info("监听登录(不可用)弹窗并关闭");
  9559. let result = [
  9560. CommonUtil.addBlockCSS('body > div[id^="login-full-panel-"]')
  9561. ];
  9562. utils.waitNode("body").then(() => {
  9563. utils.mutationObserver(document.body, {
  9564. config: {
  9565. subtree: true,
  9566. childList: true
  9567. },
  9568. callback() {
  9569. var _a2;
  9570. if (!PopsPanel.getValue("watchLoginDialogToClose")) {
  9571. return;
  9572. }
  9573. let $loginDialog = $(
  9574. 'body > div[id^="login-full-panel-"]'
  9575. );
  9576. if ($loginDialog) {
  9577. let $loginDialogCloseBtn = $loginDialog.querySelector(".dy-account-close") || $loginDialog.querySelector(
  9578. 'div:has(>svg path[d="M12.7929 22.2426C12.4024 22.6331 12.4024 23.2663 12.7929 23.6568C13.1834 24.0474 13.8166 24.0474 14.2071 23.6568L18.5 19.3639L22.7929 23.6568C23.1834 24.0474 23.8166 24.0474 24.2071 23.6568C24.5976 23.2663 24.5976 22.6331 24.2071 22.2426L19.9142 17.9497L24.1066 13.7573C24.4971 13.3668 24.4971 12.7336 24.1066 12.3431C23.7161 11.9526 23.0829 11.9526 22.6924 12.3431L18.5 16.5355L14.3076 12.3431C13.9171 11.9526 13.2839 11.9526 12.8934 12.3431C12.5029 12.7336 12.5029 13.3668 12.8934 13.7573L17.0858 17.9497L12.7929 22.2426Z"])'
  9579. );
  9580. if ($loginDialogCloseBtn) {
  9581. let reactInstance = utils.getReactObj($loginDialogCloseBtn);
  9582. let onClick = (_a2 = reactInstance == null ? void 0 : reactInstance.reactProps) == null ? void 0 : _a2.onClick;
  9583. if (typeof onClick === "function") {
  9584. onClick(new Event("click"));
  9585. } else {
  9586. log.error("监听到登录(不可用)弹窗但是关闭失败,未获取到onClick函数");
  9587. }
  9588. } else {
  9589. log.error(
  9590. "未找到登录(不可用)弹出的关闭按钮,此时键盘被聚焦在登录(不可用)弹窗上从而导致'快捷键'失效",
  9591. $loginDialog
  9592. );
  9593. }
  9594. }
  9595. }
  9596. });
  9597. });
  9598. return result;
  9599. }
  9600. };
  9601. const DouYinRedirect = {
  9602. init() {
  9603. PopsPanel.execMenu("douyin-redirect-url-home-to-root", () => {
  9604. this.redirectUrlHomeToRoot();
  9605. });
  9606. },
  9607. /**
  9608. * 从首页到根目录
  9609. */
  9610. redirectUrlHomeToRoot() {
  9611. if (window.location.pathname === "/home") {
  9612. log.info("从首页跳转到根目录");
  9613. window.location.href = window.location.origin + "/?is_from_mobile_home=1&recommend=1";
  9614. }
  9615. }
  9616. };
  9617. const MobileCSS = '/* 去除顶部的padding距离 */\r\n#douyin-right-container {\r\n padding-top: 0;\r\n}\r\n/* 放大放大顶部的综合、视频、用户等header的宽度 */\r\n#search-content-area > div > div:nth-child(1) > div:nth-child(1) {\r\n width: 100vw;\r\n}\r\n/* 放大顶部的综合、视频、用户等header */\r\n#search-content-area > div > div:nth-child(1) > div:nth-child(1) > div {\r\n transform: scale(0.8);\r\n}\r\n/* 视频宽度 */\r\nul[data-e2e="scroll-list"] {\r\n padding: 0px 10px;\r\n}\r\n#sliderVideo {\r\n width: -webkit-fill-available;\r\n}\r\n/* 距离是顶部导航栏的高度 */\r\n#search-content-area {\r\n margin-top: 65px;\r\n}\r\n/* 从其它页面进入搜索页面,例如路径是/root/search,会出现返回按钮 */\r\n#douyin-header header{\r\n flex-direction: row-reverse !important;\r\n}\r\n#douyin-header header > div:nth-child(2) {\r\n position: unset !important;\r\n}\r\n/* 调整视频列表的宽度 */\r\n@media screen and (max-width: 550px) {\r\n #sliderVideo {\r\n width: 100%;\r\n }\r\n /* 调整顶部搜索框的宽度 */\r\n #component-header\r\n div[data-click="doubleClick"]\r\n > div[data-click="doubleClick"]\r\n > div:has(input[data-e2e="searchbar-input"]) {\r\n width: -webkit-fill-available;\r\n padding-right: 0;\r\n }\r\n}\r\n';
  9618. const DouYinSearchHideElement = {
  9619. init() {
  9620. PopsPanel.execMenuOnce("douyin-search-shieldReleatedSearches", () => {
  9621. return this.shieldReleatedSearches();
  9622. });
  9623. },
  9624. /**
  9625. * 【屏蔽】相关搜索
  9626. */
  9627. shieldReleatedSearches() {
  9628. log.info("【屏蔽】相关搜索");
  9629. return [
  9630. CommonUtil.addBlockCSS("#search-content-area > div > div:nth-child(2)"),
  9631. addStyle(
  9632. /*css*/
  9633. `
  9634. #search-content-area > div > div:nth-child(1) > div:nth-child(1){
  9635. width: 100vw;
  9636. }`
  9637. )
  9638. ];
  9639. }
  9640. };
  9641. const DouYinSearch = {
  9642. init() {
  9643. DouYinSearchHideElement.init();
  9644. PopsPanel.execMenuOnce("mobileMode", () => {
  9645. return this.mobileMode();
  9646. });
  9647. PopsPanel.execMenuOnce("dy-search-disableClickToEnterFullScreen", () => {
  9648. this.disableClickToEnterFullScreen();
  9649. });
  9650. PopsPanel.execMenuOnce(
  9651. "live-setSearchResultFilterWithVideoStyle",
  9652. (value) => {
  9653. return this.setSearchResultFilterWithVideoStyle(value);
  9654. }
  9655. );
  9656. },
  9657. /**
  9658. * 手机模式
  9659. * (由通用统一调用,勿放在本函数的init内)
  9660. */
  9661. mobileMode() {
  9662. log.info("搜索-手机模式");
  9663. let result = [];
  9664. result.push(addStyle(MobileCSS));
  9665. result.push(
  9666. addStyle(
  9667. /*css*/
  9668. `
  9669. @media screen and (max-width: 550px){
  9670. div#search-body-container {
  9671. display: flex;
  9672. }
  9673. div#search-body-container #component-Navigation {
  9674. flex: 0;
  9675. }
  9676. div#search-body-container #douyin-right-container {
  9677. flex: 1 auto;
  9678. }
  9679. div#search-body-container #douyin-right-container #search-content-area > div {
  9680. width: 100% !important;
  9681. }
  9682. div#search-body-container #douyin-right-container #search-content-area > div > div > div {
  9683. width: 100% !important;
  9684. margin-left: 0px;
  9685. margin-right: 0px;
  9686. padding-left: 0px;
  9687. padding-right: 0px;
  9688. }
  9689. /* 上面的搜索结果筛选 */
  9690. #search-content-area > div >div> div:first-child > div:first-child > div:last-child{
  9691. overflow: auto;
  9692. text-wrap: nowrap;
  9693. height: auto;
  9694. }
  9695. /* 视频右侧的TA的作品↓ */
  9696. #searchSideCard{
  9697. width: unset !important;
  9698. }
  9699. #searchSideCard > div{
  9700. padding: 0px !important;
  9701. }
  9702. #searchSideCard > div:has(>div+svg),
  9703. #searchSideCard ul[data-e2e="scroll-list"]{
  9704. padding: 0px 10px !important;
  9705. }
  9706. #searchSideCard ul[data-e2e="scroll-list"] .video-playing-item > div{
  9707. width: auto;
  9708. }
  9709. /* 视频右侧的TA的作品↑ */
  9710. /* 悬浮的筛选 */
  9711. #douyin-right-container #douyin-header{
  9712. background-color: var(--color-bg-b0);
  9713. }
  9714. xg-right-grid{
  9715. margin: auto !important;
  9716. }
  9717. }
  9718. `
  9719. )
  9720. );
  9721. utils.waitNode("#relatedVideoCard").then(($relatedVideoCard) => {
  9722. log.info("评论区展开的className:" + $relatedVideoCard.className);
  9723. result.push(
  9724. addStyle(
  9725. /*css*/
  9726. `
  9727. html[data-vertical-screen]
  9728. #sliderVideo[data-e2e="feed-active-video"]
  9729. #videoSideBar:has(#relatedVideoCard[class="${$relatedVideoCard.className}"]) {
  9730. width: 100vw !important;
  9731. }`
  9732. )
  9733. );
  9734. });
  9735. return result;
  9736. },
  9737. /**
  9738. * 禁止点击视频区域进入全屏
  9739. */
  9740. disableClickToEnterFullScreen() {
  9741. log.info("搜索-禁止点击视频区域进入全屏");
  9742. domUtils.on(
  9743. document,
  9744. "click",
  9745. ".focusPanel",
  9746. (event) => {
  9747. var _a2;
  9748. utils.preventEvent(event);
  9749. let $click = event.target;
  9750. let $parent = (_a2 = $click.parentElement) == null ? void 0 : _a2.parentElement;
  9751. let $video = $parent.querySelector("video");
  9752. if ($video) {
  9753. if ($video.paused) {
  9754. log.info(".focusPanel:播放视频");
  9755. $video.play();
  9756. } else {
  9757. log.info(".focusPanel:视频暂停");
  9758. $video.pause();
  9759. }
  9760. } else {
  9761. log.error(".focusPanel未找到<video>标签");
  9762. Qmsg.error(".focusPanel未找到<video>标签", {
  9763. isHTML: false
  9764. });
  9765. }
  9766. },
  9767. {
  9768. capture: true
  9769. }
  9770. );
  9771. domUtils.on(
  9772. document,
  9773. "click",
  9774. "xg-video-container",
  9775. (event) => {
  9776. utils.preventEvent(event);
  9777. let $click = event.target;
  9778. let $video = $click.querySelector("video");
  9779. if ($video) {
  9780. if ($video.paused) {
  9781. log.info("xg-video-container:播放视频");
  9782. $video.play();
  9783. } else {
  9784. log.info("xg-video-container:视频暂停");
  9785. $video.pause();
  9786. }
  9787. } else {
  9788. log.error("xg-video-container未找到<video>标签");
  9789. Qmsg.error("xg-video-container未找到<video>标签", {
  9790. isHTML: false
  9791. });
  9792. }
  9793. },
  9794. {
  9795. capture: true
  9796. }
  9797. );
  9798. },
  9799. /**
  9800. * 设置搜索结果-按视频过滤的显示样式
  9801. * @param lineMode 单列/双列
  9802. */
  9803. setSearchResultFilterWithVideoStyle(lineMode = "one") {
  9804. log.info(`设置搜索结果-按视频过滤的显示样式:${lineMode}`);
  9805. if (lineMode === "one") {
  9806. return addStyle(
  9807. /*css*/
  9808. `
  9809. @media screen and (max-width: 800px){
  9810. .search-horizontal-new-layout ul[data-e2e="scroll-list"] li{
  9811. width: calc(100% - 21px);
  9812. }
  9813. }
  9814. `
  9815. );
  9816. } else if (lineMode === "double") {
  9817. return addStyle(
  9818. /*css*/
  9819. `
  9820. @media screen and (max-width: 800px){
  9821. .search-horizontal-new-layout ul[data-e2e="scroll-list"] li{
  9822. width: calc(50% - 21px);
  9823. }
  9824. }
  9825. `
  9826. );
  9827. }
  9828. }
  9829. };
  9830. const BlockLeftNavigator = {
  9831. init() {
  9832. PopsPanel.execInheritMenuOnce(
  9833. "shieldLeftNavigator",
  9834. "search-shieldLeftNavigator",
  9835. () => {
  9836. return this.shieldLeftNavigator();
  9837. },
  9838. (mainValue, childValue) => {
  9839. if (DouYinRouter.isSearch()) {
  9840. if (childValue == 1) {
  9841. return true;
  9842. } else if (childValue == 0) {
  9843. return false;
  9844. } else ;
  9845. }
  9846. }
  9847. );
  9848. PopsPanel.execMenuOnce("shieldLeftNavigator-tab-home", () => {
  9849. return this.block_tab_home();
  9850. });
  9851. PopsPanel.execMenuOnce("shieldLeftNavigator-tab-recommend", () => {
  9852. return this.block_tab_recommend();
  9853. });
  9854. PopsPanel.execMenuOnce("shieldLeftNavigator-tab-follow", () => {
  9855. return this.block_tab_follow();
  9856. });
  9857. PopsPanel.execMenuOnce("shieldLeftNavigator-tab-friend", () => {
  9858. return this.block_tab_friend();
  9859. });
  9860. PopsPanel.execMenuOnce("shieldLeftNavigator-tab-user_self", () => {
  9861. return this.block_tab_user_self();
  9862. });
  9863. PopsPanel.execMenuOnce("shieldLeftNavigator-tab-user_self_like", () => {
  9864. return this.block_tab_user_self_like();
  9865. });
  9866. PopsPanel.execMenuOnce(
  9867. "shieldLeftNavigator-tab-user_self_collection",
  9868. () => {
  9869. return this.block_tab_user_self_collection();
  9870. }
  9871. );
  9872. PopsPanel.execMenuOnce("shieldLeftNavigator-tab-user_self_record", () => {
  9873. return this.block_tab_user_self_record();
  9874. });
  9875. PopsPanel.execMenuOnce("shieldLeftNavigator-tab-olympics", () => {
  9876. return this.block_tab_olympics();
  9877. });
  9878. PopsPanel.execMenuOnce("shieldLeftNavigator-tab-live", () => {
  9879. return this.block_tab_live();
  9880. });
  9881. PopsPanel.execMenuOnce("shieldLeftNavigator-tab-vs", () => {
  9882. return this.block_tab_vs();
  9883. });
  9884. PopsPanel.execMenuOnce("shieldLeftNavigator-tab-series", () => {
  9885. return this.block_tab_series();
  9886. });
  9887. PopsPanel.execMenuOnce("shieldLeftNavigator-tab-channel_300203", () => {
  9888. return this.block_tab_channel_300203();
  9889. });
  9890. PopsPanel.execMenuOnce("shieldLeftNavigator-tab-channel_300205", () => {
  9891. return this.block_tab_channel_300205();
  9892. });
  9893. PopsPanel.execMenuOnce("shieldLeftNavigator-tab-channel_300206", () => {
  9894. return this.block_tab_channel_300206();
  9895. });
  9896. PopsPanel.execMenuOnce("shieldLeftNavigator-tab-channel_300209", () => {
  9897. return this.block_tab_channel_300209();
  9898. });
  9899. PopsPanel.execMenuOnce("shieldLeftNavigator-tab-channel_300204", () => {
  9900. return this.block_tab_channel_300204();
  9901. });
  9902. PopsPanel.execMenuOnce("shieldLeftNavigator-tab-activity_2644292", () => {
  9903. return this.block_tab_activity_2644292();
  9904. });
  9905. PopsPanel.execMenuOnce("shieldLeftNavigator-tab-activity_2643710", () => {
  9906. return this.block_tab_activity_2643710();
  9907. });
  9908. },
  9909. /**
  9910. * 【屏蔽】左侧导航栏
  9911. */
  9912. shieldLeftNavigator() {
  9913. log.info("【屏蔽】左侧导航栏");
  9914. let result = [];
  9915. result.push(CommonUtil.addBlockCSS("#douyin-navigation"));
  9916. result.push(
  9917. addStyle(
  9918. /*css*/
  9919. `
  9920. /* 修复顶部导航栏的宽度 */
  9921. #douyin-header{
  9922. width: 100%;
  9923. }`
  9924. )
  9925. );
  9926. return result;
  9927. },
  9928. /**
  9929. * 【屏蔽】首页
  9930. */
  9931. block_tab_home() {
  9932. log.info("【屏蔽】首页");
  9933. return CommonUtil.addBlockCSS(
  9934. 'div[data-e2e="douyin-navigation"] > div > div > div > div:has(.tab-discover)'
  9935. );
  9936. },
  9937. /**
  9938. * 【屏蔽】推荐
  9939. */
  9940. block_tab_recommend() {
  9941. log.info("【屏蔽】推荐");
  9942. return CommonUtil.addBlockCSS(
  9943. 'div[data-e2e="douyin-navigation"] > div > div > div > div:has(.tab-recommend)'
  9944. );
  9945. },
  9946. /**
  9947. * 【屏蔽】关注
  9948. */
  9949. block_tab_follow() {
  9950. log.info("【屏蔽】关注");
  9951. return CommonUtil.addBlockCSS(
  9952. 'div[data-e2e="douyin-navigation"] > div > div > div > div:has(.tab-follow)'
  9953. );
  9954. },
  9955. /**
  9956. * 【屏蔽】朋友
  9957. */
  9958. block_tab_friend() {
  9959. log.info("【屏蔽】朋友");
  9960. return CommonUtil.addBlockCSS(
  9961. 'div[data-e2e="douyin-navigation"] > div > div > div > div:has(.tab-friend)'
  9962. );
  9963. },
  9964. /**
  9965. * 【屏蔽】我的
  9966. */
  9967. block_tab_user_self() {
  9968. log.info("【屏蔽】我的");
  9969. return CommonUtil.addBlockCSS(
  9970. 'div[data-e2e="douyin-navigation"] > div > div > div > div > div:has(.tab-user_self)'
  9971. );
  9972. },
  9973. /**
  9974. * 【屏蔽】喜欢
  9975. */
  9976. block_tab_user_self_like() {
  9977. log.info("【屏蔽】喜欢");
  9978. return CommonUtil.addBlockCSS(
  9979. 'div[data-e2e="douyin-navigation"] > div > div > div > div > div:has(.tab-user_self_like)'
  9980. );
  9981. },
  9982. /**
  9983. * 【屏蔽】收藏
  9984. */
  9985. block_tab_user_self_collection() {
  9986. log.info("【屏蔽】收藏");
  9987. return CommonUtil.addBlockCSS(
  9988. 'div[data-e2e="douyin-navigation"] > div > div > div > div > div:has(.tab-user_self_collection)'
  9989. );
  9990. },
  9991. /**
  9992. * 【屏蔽】观看历史
  9993. */
  9994. block_tab_user_self_record() {
  9995. log.info("【屏蔽】观看历史");
  9996. return CommonUtil.addBlockCSS(
  9997. 'div[data-e2e="douyin-navigation"] > div > div > div > div > div:has(.tab-user_self_record)'
  9998. );
  9999. },
  10000. /**
  10001. * 【屏蔽】看奥运
  10002. */
  10003. block_tab_olympics() {
  10004. log.info("【屏蔽】看奥运");
  10005. return CommonUtil.addBlockCSS(
  10006. 'div[data-e2e="douyin-navigation"] > div > div > div > div:has(.tab-olympics)'
  10007. );
  10008. },
  10009. /**
  10010. * 【屏蔽】直播
  10011. */
  10012. block_tab_live() {
  10013. log.info("【屏蔽】直播");
  10014. return CommonUtil.addBlockCSS(
  10015. 'div[data-e2e="douyin-navigation"] > div > div > div > div:has(.tab-live)'
  10016. );
  10017. },
  10018. /**
  10019. * 【屏蔽】放映厅
  10020. */
  10021. block_tab_vs() {
  10022. log.info("【屏蔽】放映厅");
  10023. return CommonUtil.addBlockCSS(
  10024. 'div[data-e2e="douyin-navigation"] > div > div > div > div:has(.tab-vs)'
  10025. );
  10026. },
  10027. /**
  10028. * 【屏蔽】短剧
  10029. */
  10030. block_tab_series() {
  10031. log.info(`短剧`);
  10032. return CommonUtil.addBlockCSS(
  10033. 'div[data-e2e="douyin-navigation"] > div > div > div > div:has(.tab-series)'
  10034. );
  10035. },
  10036. /**
  10037. * 【屏蔽】知识
  10038. */
  10039. block_tab_channel_300203() {
  10040. log.info("【屏蔽】知识");
  10041. return CommonUtil.addBlockCSS(
  10042. 'div[data-e2e="douyin-navigation"] > div > div > div > div:has(.tab-channel_300203)'
  10043. );
  10044. },
  10045. /**
  10046. * 【屏蔽】游戏
  10047. */
  10048. block_tab_channel_300205() {
  10049. log.info("【屏蔽】游戏");
  10050. return CommonUtil.addBlockCSS(
  10051. 'div[data-e2e="douyin-navigation"] > div > div > div > div:has(.tab-channel_300205)'
  10052. );
  10053. },
  10054. /**
  10055. * 【屏蔽】二次元
  10056. */
  10057. block_tab_channel_300206() {
  10058. log.info("【屏蔽】二次元");
  10059. return CommonUtil.addBlockCSS(
  10060. 'div[data-e2e="douyin-navigation"] > div > div > div > div:has(.tab-channel_300206)'
  10061. );
  10062. },
  10063. /**
  10064. * 【屏蔽】音乐
  10065. */
  10066. block_tab_channel_300209() {
  10067. log.info("【屏蔽】音乐");
  10068. return CommonUtil.addBlockCSS(
  10069. 'div[data-e2e="douyin-navigation"] > div > div > div > div:has(.tab-channel_300209)'
  10070. );
  10071. },
  10072. /**
  10073. * 【屏蔽】美食
  10074. */
  10075. block_tab_channel_300204() {
  10076. log.info("【屏蔽】美食");
  10077. return CommonUtil.addBlockCSS(
  10078. 'div[data-e2e="douyin-navigation"] > div > div > div > div:has(.tab-channel_300204)'
  10079. );
  10080. },
  10081. /**
  10082. * 【屏蔽】美好跨年季
  10083. */
  10084. block_tab_activity_2644292() {
  10085. log.info(`【屏蔽】美好跨年季`);
  10086. return CommonUtil.addBlockCSS(
  10087. 'div[data-e2e="douyin-navigation"] > div > div > div > div:has([class^="tab-activity_"] img[alt="抖音美好跨年季"])'
  10088. );
  10089. },
  10090. /**
  10091. * 【屏蔽】2025新春环游记
  10092. */
  10093. block_tab_activity_2643710() {
  10094. log.info(`【屏蔽】2025新春环游记`);
  10095. return CommonUtil.addBlockCSS(
  10096. 'div[data-e2e="douyin-navigation"] > div > div > div > div:has([class^="tab-activity_"] img[alt="2025新春环游记"])'
  10097. );
  10098. }
  10099. };
  10100. const blockCSS$8 = '/* 从顶部往下弹出的下载抖音电脑版的drawer提示 */\r\n#douyin-web-download-guide-container\r\n/* 视频信息区域的 及时接收作品更新提醒 下载电脑客户端 */\r\n/* 但是这个CSS又会屏蔽右键菜单 */\r\n/*.basePlayerContainer xg-bar.xg-right-bar + div:not(:has(>svg))*/ ,\r\n/* 下载客户端,使用壁纸 */\r\ndiv:has(+#wallpaper-modal),\r\n/* 下载客户端,实时接收消息通知 */\r\n/* 下载客户端,实时接收好友消息 */\r\ndiv:has(> a[download*="douyin-downloade"]):has(+.popShadowAnimation),\r\ndiv:has(> a[download*="douyin-downloade"]):has(+div>[data-e2e="listDlgTest-container"]),\r\n/* 客户端登录(不可用)访问更便捷 */\r\ndiv:has(> a[download*="douyin-downloade"]):has(+.userMenuPanelShadowAnimation),\r\n/* 前往电脑客户端,即享下载视频 */\r\n[data-e2e="video-share-container"] div:has(>div>div> a[download*="douyin-downloader"]):first-child {\r\n display: none !important;\r\n}\r\n';
  10101. const blockCSS$7 = '/* 资料右边的 下载桌面客户端,桌面快捷访问 */\r\ndiv[data-e2e="user-detail"] div:has(> div > a[href*="douyin-pc"]) {\r\n display: none !important;\r\n}\r\n';
  10102. const DouYinUser = {
  10103. init() {
  10104. addStyle(blockCSS$7);
  10105. domUtils.ready(() => {
  10106. PopsPanel.execMenu("dy-user-addShowUserUID", () => {
  10107. this.addShowUserUID();
  10108. });
  10109. });
  10110. },
  10111. /**
  10112. * 显示UID
  10113. */
  10114. addShowUserUID() {
  10115. ReactUtils.waitReactPropsToSet(
  10116. `[data-e2e="user-detail"] [data-e2e="user-info"]`,
  10117. "reactFiber",
  10118. {
  10119. msg: "显示UID",
  10120. check(reactInstance) {
  10121. var _a2, _b, _c;
  10122. return typeof ((_c = (_b = (_a2 = reactInstance == null ? void 0 : reactInstance.return) == null ? void 0 : _a2.memoizedProps) == null ? void 0 : _b.userInfo) == null ? void 0 : _c.uid) === "string";
  10123. },
  10124. set(reactInstance, $target) {
  10125. var _a2, _b, _c;
  10126. let uid = (_c = (_b = (_a2 = reactInstance == null ? void 0 : reactInstance.return) == null ? void 0 : _a2.memoizedProps) == null ? void 0 : _b.userInfo) == null ? void 0 : _c.uid;
  10127. domUtils.remove(
  10128. $target.querySelectorAll(".gm-user-uid")
  10129. );
  10130. let $userUID = domUtils.createElement(
  10131. "p",
  10132. {
  10133. className: "gm-user-uid",
  10134. innerHTML: (
  10135. /*html*/
  10136. `
  10137. <span>UID${uid}</span>
  10138. `
  10139. )
  10140. },
  10141. {
  10142. style: "color: var(--color-text-t3);margin-right: 20px;font-size: 12px;line-height: 20px;cursor: pointer;"
  10143. }
  10144. );
  10145. domUtils.on($userUID, "click", (event) => {
  10146. utils.preventEvent(event);
  10147. utils.setClip(uid);
  10148. Qmsg.success("复制成功");
  10149. });
  10150. $target.appendChild($userUID);
  10151. }
  10152. }
  10153. );
  10154. }
  10155. };
  10156. const blockCSS$6 = '/* 单个视频页面右侧的 下载客户端,桌面快捷访问 */\r\ndiv[data-e2e="video-detail"]\r\n div\r\n > :has(> div:last-child > a[href*="douyin-pc-web"]) {\r\n display: none !important;\r\n}\r\n';
  10157. const DouYinVideo = {
  10158. init() {
  10159. addStyle(blockCSS$6);
  10160. }
  10161. };
  10162. const blockCSS$5 = '/* 右侧视频信息里的 下载客户端,桌面快捷访问 */\r\n[data-e2e="note-detail"]\r\n div:has(> [data-e2e="user-info"])\r\n > div:has(a[download*="douyin-downloader"]) {\r\n display: none !important;\r\n}\r\n';
  10163. const DouYinNote = {
  10164. init() {
  10165. addStyle(blockCSS$5);
  10166. }
  10167. };
  10168. const DouYin = {
  10169. init() {
  10170. PopsPanel.onceExec("dy-global-block-css", () => {
  10171. addStyle(blockCSS$8);
  10172. });
  10173. DouYinGestureBackClearHash();
  10174. DouYinHook.init();
  10175. DouYinVideoFilter.init();
  10176. DouYinRedirect.init();
  10177. PopsPanel.execMenuOnce("watchLoginDialogToClose", () => {
  10178. DouYinAccount.watchLoginDialogToClose();
  10179. });
  10180. PopsPanel.execMenuOnce("disguiseLogin", () => {
  10181. DouYinAccount.disguiseLogin();
  10182. });
  10183. PopsPanel.execMenuOnce("dy-initialScale", () => {
  10184. this.initialScale();
  10185. });
  10186. PopsPanel.execMenu("dy-apple-removeMetaAppleItunesApp", () => {
  10187. this.removeMetaAppleItunesApp();
  10188. });
  10189. BlockLeftNavigator.init();
  10190. BlockTopNavigator.init();
  10191. BlockSearchFrame.init();
  10192. PopsPanel.execMenuOnce("dy-common-listenRouterChange", () => {
  10193. this.listenRouterChange();
  10194. });
  10195. if (DouYinRouter.isLive()) {
  10196. log.info("Router: 直播");
  10197. DouYinLive.init();
  10198. } else if (DouYinRouter.isIndex()) {
  10199. DouYinVideoPlayer.init();
  10200. if (DouYinRouter.isSearch()) {
  10201. log.info("Router: 搜索");
  10202. DouYinSearch.init();
  10203. } else if (DouYinRouter.isUser()) {
  10204. log.info(`Router: 用户页面`);
  10205. DouYinUser.init();
  10206. } else if (DouYinRouter.isVideo()) {
  10207. log.info(`Router: 单个视频页面`);
  10208. DouYinVideo.init();
  10209. } else if (DouYinRouter.isChannel()) {
  10210. log.info(`Router: Channel页面`);
  10211. } else if (DouYinRouter.isNote()) {
  10212. log.info(`Router: 笔记页面`);
  10213. DouYinNote.init();
  10214. } else {
  10215. log.error("未适配router: " + window.location.pathname);
  10216. }
  10217. } else {
  10218. log.error("未适配router: " + window.location.href);
  10219. }
  10220. },
  10221. /**
  10222. * 固定meta viewport缩放倍率为1
  10223. */
  10224. initialScale() {
  10225. log.info("设置<meta>的viewport固定缩放倍率为1并移除页面原有的<meta>");
  10226. domUtils.ready(() => {
  10227. let meta = domUtils.createElement(
  10228. "meta",
  10229. {},
  10230. {
  10231. name: "viewport",
  10232. content: "width=device-width,initial-scale=1,user-scalable=no,viewport-fit=cover"
  10233. }
  10234. );
  10235. domUtils.remove("meta[name='viewport']");
  10236. utils.waitNode("head").then(() => {
  10237. document.head.appendChild(meta);
  10238. });
  10239. });
  10240. },
  10241. /**
  10242. * 移除<meta>标签name="apple-itunes-app"
  10243. */
  10244. removeMetaAppleItunesApp() {
  10245. utils.waitNodeList(
  10246. ['meta[name="apple-itunes-app"]'],
  10247. 1e4
  10248. ).then(($metaList) => {
  10249. if (!$metaList) {
  10250. return;
  10251. }
  10252. $metaList.forEach(($meta) => {
  10253. $meta.remove();
  10254. });
  10255. });
  10256. },
  10257. /**
  10258. * 监听Router重载
  10259. */
  10260. listenRouterChange() {
  10261. log.info(`监听Router重载`);
  10262. domUtils.on(window, "wb_url_change", (event) => {
  10263. log.info(`Router Change`);
  10264. this.init();
  10265. });
  10266. }
  10267. };
  10268. const MDouYinRouter = {
  10269. /**
  10270. * 是否是移动端抖音
  10271. */
  10272. isMDouYin() {
  10273. return window.location.hostname === "m.douyin.com" || window.location.hostname === "www.iesdouyin.com";
  10274. },
  10275. /**
  10276. * 用户主页
  10277. */
  10278. isShareUser() {
  10279. return this.isMDouYin() && window.location.pathname.startsWith("/share/user/");
  10280. },
  10281. /**
  10282. * 分享的视频
  10283. */
  10284. isShareVideo() {
  10285. return this.isMDouYin() && (window.location.pathname.startsWith("/share/video/") || window.location.pathname.startsWith("/shipin/"));
  10286. },
  10287. /**
  10288. * 笔记
  10289. */
  10290. isShareNote() {
  10291. return this.isMDouYin() && window.location.pathname.startsWith("/share/note/");
  10292. },
  10293. /**
  10294. * 音乐
  10295. */
  10296. isShareMusic() {
  10297. return this.isMDouYin() && window.location.pathname.startsWith("/share/music/");
  10298. },
  10299. /**
  10300. * 话题
  10301. */
  10302. isShareChallenge() {
  10303. return this.isMDouYin() && window.location.pathname.startsWith("/share/challenge/");
  10304. }
  10305. };
  10306. const blockCSS$4 = "/* 顶部 打开看看 登录(不可用) */\r\n.adapt-login-header,\r\n/* 上面屏蔽后的空白区域 */\r\n.user-card .nav-bar-placeholder,\r\n/* 视频区域底部的【打开抖音App看更多内容】 */\r\n.select-list .img-button{\r\n display: none !important;\r\n}";
  10307. const DouYinUrlUtils = {
  10308. /**
  10309. * 获取视频链接
  10310. * @param videoId 视频id
  10311. */
  10312. getVideoUrl(videoId) {
  10313. return "https://www.douyin.com/video/" + videoId;
  10314. },
  10315. /**
  10316. * 获取视频合集链接
  10317. * @param collectionId 合集id
  10318. */
  10319. getCollectionUrl(collectionId) {
  10320. return "https://www.douyin.com/collection/" + collectionId;
  10321. },
  10322. /**
  10323. * 获取笔记链接
  10324. * @param noteId 笔记id
  10325. */
  10326. getNoteUrl(noteId) {
  10327. return "https://www.douyin.com/note/" + noteId;
  10328. },
  10329. /**
  10330. * 获取话题链接
  10331. * @param hashTagId 话题id
  10332. */
  10333. getHashTagUrl(hashTagId) {
  10334. return "https://www.douyin.com/hashtag/" + hashTagId;
  10335. },
  10336. /**
  10337. * 获取用户主页链接
  10338. * @param sec_uid
  10339. */
  10340. getUserHomeUrl(sec_uid) {
  10341. return "https://www.douyin.com/user/" + sec_uid;
  10342. },
  10343. /**
  10344. * 获取音乐链接
  10345. * @param musicId 音乐id
  10346. */
  10347. getMusicUrl(musicId) {
  10348. return "https://www.douyin.com/music/" + musicId;
  10349. }
  10350. };
  10351. const MDouYinShareUser = {
  10352. init() {
  10353. addStyle(blockCSS$4);
  10354. PopsPanel.execMenuOnce("m-dy-share-user-coverPlayletList", () => {
  10355. this.coverPlayletList();
  10356. });
  10357. PopsPanel.execMenuOnce("m-dy-share-user-coverPostListContainer", () => {
  10358. this.coverPostListContainer();
  10359. });
  10360. },
  10361. /**
  10362. * 覆盖视频合集点击事件
  10363. */
  10364. coverPlayletList() {
  10365. domUtils.on(
  10366. document,
  10367. "click",
  10368. ".user-playlet-list .playlet-item",
  10369. (event) => {
  10370. var _a2, _b, _c, _d;
  10371. utils.preventEvent(event);
  10372. let $click = event.target;
  10373. let reactFiber = (_a2 = utils.getReactObj($click)) == null ? void 0 : _a2.reactFiber;
  10374. let key = reactFiber == null ? void 0 : reactFiber.key;
  10375. if (key == null) {
  10376. Qmsg.error("获取视频合集key失败");
  10377. return;
  10378. }
  10379. let index = reactFiber == null ? void 0 : reactFiber.index;
  10380. if (index == null) {
  10381. Qmsg.error("获取视频合集index失败");
  10382. return;
  10383. }
  10384. let playletList = (_d = (_c = (_b = reactFiber == null ? void 0 : reactFiber.return) == null ? void 0 : _b.return) == null ? void 0 : _c.pendingProps) == null ? void 0 : _d.playletList;
  10385. if (playletList == null) {
  10386. Qmsg.error("获取视频合集playletList失败");
  10387. return;
  10388. }
  10389. let currentPlaylet = playletList[index];
  10390. let url = DouYinUrlUtils.getCollectionUrl(currentPlaylet["mix_id"]);
  10391. window.open(url, "_blank");
  10392. },
  10393. {
  10394. capture: true
  10395. }
  10396. );
  10397. },
  10398. /**
  10399. * 覆盖视频列表点击事件
  10400. */
  10401. coverPostListContainer() {
  10402. domUtils.on(
  10403. document,
  10404. "click",
  10405. ".post-list-container .user-post-cover",
  10406. (event) => {
  10407. var _a2, _b, _c, _d, _e;
  10408. utils.preventEvent(event);
  10409. let $click = event.target;
  10410. let reactFiber = (_a2 = utils.getReactObj($click)) == null ? void 0 : _a2.reactFiber;
  10411. if ((_c = (_b = reactFiber == null ? void 0 : reactFiber.return) == null ? void 0 : _b.memoizedProps) == null ? void 0 : _c.productionUrl) {
  10412. let url = (_e = (_d = reactFiber == null ? void 0 : reactFiber.return) == null ? void 0 : _d.memoizedProps) == null ? void 0 : _e.productionUrl;
  10413. window.open(url, "_blank");
  10414. } else {
  10415. Qmsg.error("获取视频链接失败");
  10416. }
  10417. },
  10418. {
  10419. capture: true
  10420. }
  10421. );
  10422. }
  10423. };
  10424. const blockCSS$3 = "/* 顶部 打开看看 登录(不可用) */\r\n.adapt-login-header,\r\n/* 视频描述信息区域中的 打开抖音看精彩视频 */\r\n.footer .img-button,\r\n/* 登录(不可用)页面 */\r\n.login-page ,\r\n/* 底部左下角 打开抖音看精彩视频 */\r\n.footer .bottom-btn-con-new,\r\n/* 合集 打开抖音看精彩视频 */\r\n.container .end-page-info-button {\r\n display: none !important;\r\n}\r\n";
  10425. const beautifyCSS = ".video-container {\r\n height: 100% !important;\r\n margin-top: 0 !important;\r\n}\r\n.footer {\r\n bottom: 50px !important;\r\n}\r\n.mix-info {\r\n bottom: 0px !important;\r\n}\r\n";
  10426. const MDouYinShareVideo = {
  10427. init() {
  10428. addStyle(blockCSS$3);
  10429. addStyle(beautifyCSS);
  10430. PopsPanel.execMenuOnce("m-dy-share-video-coverGlobalClick", () => {
  10431. this.coverGlobalClick();
  10432. });
  10433. },
  10434. /**
  10435. * 阻止全局点击,会跳转
  10436. */
  10437. coverGlobalClick() {
  10438. let selectorList = [".right-con", ".footer", ".mix-info"];
  10439. selectorList.forEach((selector) => {
  10440. DOMUtils.on(
  10441. document,
  10442. "click",
  10443. selector,
  10444. (event) => {
  10445. return utils.preventEvent(event);
  10446. },
  10447. {
  10448. capture: true
  10449. }
  10450. );
  10451. });
  10452. }
  10453. };
  10454. const blockCSS$2 = "/* 顶部 打开看看 登录(不可用) */\r\n.container .adapt-login-header,\r\n/* 底部中间的 App内打开 */\r\n.container .float-button-con {\r\n display: none !important;\r\n}\r\n\r\n.gallery-container {\r\n margin-top: 10px !important;\r\n}\r\n";
  10455. const MDouYinShareNote = {
  10456. init() {
  10457. addStyle(blockCSS$2);
  10458. PopsPanel.execMenuOnce("m-dy-share-note-blockRecommend", () => {
  10459. return this.blockRecommend();
  10460. });
  10461. PopsPanel.execMenuOnce("m-dy-share-note-blockComment", () => {
  10462. return this.blockComment();
  10463. });
  10464. PopsPanel.execMenuOnce("m-dy-share-note-blockFooterToobar", () => {
  10465. return this.blockFooterToobar();
  10466. });
  10467. PopsPanel.execMenuOnce("m-dy-share-note-coverUser", () => {
  10468. this.coverUser();
  10469. });
  10470. PopsPanel.execMenuOnce("m-dy-share-note-coverHashTag", () => {
  10471. this.coverHashTag();
  10472. });
  10473. PopsPanel.execMenuOnce("m-dy-share-note-coverMusic", () => {
  10474. this.coverMusic();
  10475. });
  10476. PopsPanel.execMenuOnce("m-dy-share-note-coverRecommend", () => {
  10477. this.coverRecommend();
  10478. });
  10479. PopsPanel.execMenuOnce(
  10480. "m-dy-share-note-coverExcitingGraphicsAndText",
  10481. () => {
  10482. this.coverExcitingGraphicsAndText();
  10483. }
  10484. );
  10485. },
  10486. /**
  10487. * 【屏蔽】相关推荐
  10488. */
  10489. blockRecommend() {
  10490. log.info("【屏蔽】相关推荐");
  10491. return CommonUtil.addBlockCSS(".recommend-con");
  10492. },
  10493. /**
  10494. * 【屏蔽】评论
  10495. */
  10496. blockComment() {
  10497. log.info("【屏蔽】评论");
  10498. return CommonUtil.addBlockCSS(".comment-con");
  10499. },
  10500. /**
  10501. * 【屏蔽】底部工具栏
  10502. */
  10503. blockFooterToobar() {
  10504. log.info("【屏蔽】底部工具栏");
  10505. return CommonUtil.addBlockCSS(".footer-con");
  10506. },
  10507. /**
  10508. * 覆盖相关推荐的点击事件
  10509. */
  10510. coverRecommend() {
  10511. log.info("覆盖相关推荐的点击事件");
  10512. domUtils.on(
  10513. document,
  10514. "click",
  10515. "#masonry .card",
  10516. (event) => {
  10517. utils.preventEvent(event);
  10518. let $click = event.target;
  10519. let rectFiber = utils.getReactObj($click).reactFiber;
  10520. if (!rectFiber) {
  10521. log.error("获取reactFiber失败");
  10522. Qmsg.error("获取reactFiber失败");
  10523. return;
  10524. }
  10525. let awemeId = rectFiber.return.memoizedProps.awemeId;
  10526. let url = DouYinUrlUtils.getNoteUrl(awemeId);
  10527. window.open(url, "_blank");
  10528. },
  10529. { capture: true }
  10530. );
  10531. },
  10532. /**
  10533. * 覆盖用户点击事件
  10534. */
  10535. coverUser() {
  10536. log.info("覆盖用户点击事件");
  10537. domUtils.on(
  10538. document,
  10539. "click",
  10540. ".message-con__top",
  10541. (event) => {
  10542. utils.preventEvent(event);
  10543. let $click = event.target;
  10544. let rectFiber = utils.getReactObj($click).reactFiber;
  10545. if (!rectFiber) {
  10546. log.error("获取reactFiber失败");
  10547. Qmsg.error("获取reactFiber失败");
  10548. return;
  10549. }
  10550. let sec_id = rectFiber.return.return.memoizedProps.video.authorInfo.sec_uid;
  10551. let url = DouYinUrlUtils.getUserHomeUrl(sec_id);
  10552. window.open(url, "_blank");
  10553. },
  10554. { capture: true }
  10555. );
  10556. },
  10557. /**
  10558. * 覆盖话题点击事件
  10559. */
  10560. coverHashTag() {
  10561. log.info("覆盖话题点击事件");
  10562. domUtils.on(
  10563. document,
  10564. "click",
  10565. ".message-con__content__body .message-con__content__body-text",
  10566. (event) => {
  10567. utils.preventEvent(event);
  10568. let $click = event.target;
  10569. let rectFiber = utils.getReactObj($click).reactFiber;
  10570. if (!rectFiber) {
  10571. log.error("获取reactFiber失败");
  10572. Qmsg.error("获取reactFiber失败");
  10573. return;
  10574. }
  10575. let index = rectFiber.index;
  10576. let splitStrArr = rectFiber.return.return.return.return.memoizedProps.video.splitStrArr;
  10577. let currentSplitStr = splitStrArr[index];
  10578. let hashtagId = currentSplitStr["hashtagId"];
  10579. let url = DouYinUrlUtils.getHashTagUrl(hashtagId);
  10580. window.open(url, "_blank");
  10581. },
  10582. { capture: true }
  10583. );
  10584. },
  10585. /**
  10586. * 覆盖音乐点击事件
  10587. */
  10588. coverMusic() {
  10589. log.info("覆盖音乐点击事件");
  10590. domUtils.on(
  10591. document,
  10592. "click",
  10593. ".message-con__footer",
  10594. (event) => {
  10595. utils.preventEvent(event);
  10596. let $click = event.target;
  10597. let rectFiber = utils.getReactObj($click).reactFiber;
  10598. if (!rectFiber) {
  10599. log.error("获取reactFiber失败");
  10600. Qmsg.error("获取reactFiber失败");
  10601. return;
  10602. }
  10603. let musicId = rectFiber.return.return.memoizedProps.video.musicId;
  10604. let url = DouYinUrlUtils.getMusicUrl(musicId);
  10605. window.open(url, "_blank");
  10606. },
  10607. { capture: true }
  10608. );
  10609. },
  10610. /**
  10611. * 覆盖精彩图文点击事件
  10612. */
  10613. coverExcitingGraphicsAndText() {
  10614. log.info("覆盖精彩图文点击事件");
  10615. domUtils.on(
  10616. document,
  10617. "click",
  10618. ".container .related-list-con .related-note-item",
  10619. (event) => {
  10620. utils.preventEvent(event);
  10621. let $click = event.target;
  10622. let rectFiber = utils.getReactObj($click).reactFiber;
  10623. if (!rectFiber) {
  10624. log.error("获取reactFiber失败");
  10625. Qmsg.error("获取reactFiber失败");
  10626. return;
  10627. }
  10628. let itemData = rectFiber.return.memoizedProps.itemData;
  10629. let awemeId = itemData["awemeId"];
  10630. let url = DouYinUrlUtils.getNoteUrl(awemeId);
  10631. window.open(url, "_blank");
  10632. },
  10633. { capture: true }
  10634. );
  10635. domUtils.on(
  10636. document,
  10637. "click",
  10638. ".related-title-con",
  10639. (event) => utils.preventEvent(event),
  10640. { capture: true }
  10641. );
  10642. }
  10643. };
  10644. const blockCSS$1 = "/* 顶部 打开看看 登录(不可用) */\r\n.page-reflow-challenge .header,\r\n/* 底部的 打开抖音App看更多内容 */\r\n.page-reflow-challenge .bottom-btn__con {\r\n display: none !important;\r\n}\r\n\r\n.page-reflow-challenge {\r\n padding-top: 0 !important;\r\n}\r\n";
  10645. const MDouYinShareChallenge = {
  10646. init() {
  10647. addStyle(blockCSS$1);
  10648. PopsPanel.onceExec("m-dy-share-challenge-coverTopJump", () => {
  10649. this.coverTopJump();
  10650. });
  10651. PopsPanel.execMenuOnce("m-dy-share-challenge-coverVideoCard", () => {
  10652. this.coverVideoCard();
  10653. });
  10654. },
  10655. /**
  10656. * 阻止上面区域点击跳转至下载页面
  10657. */
  10658. coverTopJump() {
  10659. log.info("阻止上面区域点击跳转至下载页面");
  10660. domUtils.on(
  10661. document,
  10662. "click",
  10663. ".challenge-body",
  10664. (event) => {
  10665. utils.preventEvent(event);
  10666. },
  10667. {
  10668. capture: true
  10669. }
  10670. );
  10671. },
  10672. /**
  10673. * 覆盖视频卡片点击事件
  10674. */
  10675. coverVideoCard() {
  10676. log.info("覆盖视频卡片点击事件");
  10677. domUtils.on(
  10678. document,
  10679. "click",
  10680. "#pagelet-worklist li.item",
  10681. (event) => {
  10682. utils.preventEvent(event);
  10683. let $clikc = event.target;
  10684. let rectFiber = utils.getReactObj($clikc).reactFiber;
  10685. if (!rectFiber) {
  10686. log.error("获取reactFiber失败");
  10687. Qmsg.error("获取reactFiber失败");
  10688. return;
  10689. }
  10690. let listData = rectFiber.return.return.return.memoizedProps.listData;
  10691. let index = rectFiber.index;
  10692. let currentList = listData[index];
  10693. let url = DouYinUrlUtils.getVideoUrl(currentList["aweme_id"]);
  10694. window.open(url, "_blank");
  10695. },
  10696. {
  10697. capture: true
  10698. }
  10699. );
  10700. }
  10701. };
  10702. const blockCSS = "/* 顶部 打开App,发现更多内容 */\r\n.page-reflow-music .header,\r\n/* ↑屏蔽后的 顶部空白区域 */\r\n.page-reflow-music .banner-placeholder ,\r\n/* 底部 打开抖音App看更多内容 */\r\n.page-reflow-music .bottom-btn__con {\r\n display: none !important;\r\n}\r\n";
  10703. const MDouYinShareMusic = {
  10704. init() {
  10705. addStyle(blockCSS);
  10706. PopsPanel.execMenuOnce("m-dy-share-music-coverVideoCard", () => {
  10707. this.coverVideoCard();
  10708. });
  10709. },
  10710. /**
  10711. * 覆盖视频卡片点击事件
  10712. */
  10713. coverVideoCard() {
  10714. log.info("覆盖视频卡片点击事件");
  10715. domUtils.on(
  10716. document,
  10717. "click",
  10718. "#pagelet-worklist li.item",
  10719. (event) => {
  10720. utils.preventEvent(event);
  10721. let $clikc = event.target;
  10722. let rectFiber = utils.getReactObj($clikc).reactFiber;
  10723. if (!rectFiber) {
  10724. log.error("获取reactFiber失败");
  10725. Qmsg.error("获取reactFiber失败");
  10726. return;
  10727. }
  10728. let listData = rectFiber.return.return.return.memoizedProps.listData;
  10729. let index = rectFiber.index;
  10730. let currentList = listData[index];
  10731. let url = DouYinUrlUtils.getVideoUrl(currentList["aweme_id"]);
  10732. window.open(url, "_blank");
  10733. },
  10734. {
  10735. capture: true
  10736. }
  10737. );
  10738. }
  10739. };
  10740. const MDouYin = {
  10741. init() {
  10742. if (MDouYinRouter.isShareUser()) {
  10743. log.info("M-Router: 分享用户");
  10744. MDouYinShareUser.init();
  10745. } else if (MDouYinRouter.isShareVideo()) {
  10746. log.info("M-Router: 分享视频");
  10747. MDouYinShareVideo.init();
  10748. } else if (MDouYinRouter.isShareNote()) {
  10749. log.info("M-Router: 分享笔记");
  10750. MDouYinShareNote.init();
  10751. } else if (MDouYinRouter.isShareChallenge()) {
  10752. log.info("M-Router: 分享话题");
  10753. MDouYinShareChallenge.init();
  10754. } else if (MDouYinRouter.isShareMusic()) {
  10755. log.info("M-Router: 分享音乐");
  10756. MDouYinShareMusic.init();
  10757. } else {
  10758. log.error("未知M-router: " + window.location.hostname);
  10759. }
  10760. }
  10761. };
  10762. PopsPanel.init();
  10763. if (MDouYinRouter.isMDouYin()) {
  10764. MDouYin.init();
  10765. } else {
  10766. DouYin.init();
  10767. }
  10768.  
  10769. })(Qmsg, Utils, DOMUtils, pops);

QingJ © 2025

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