Greasy Fork is available in English.

听歌小助手

这个脚本主要为Hifini(音乐磁场)网站提供了一个歌曲管理页面(可添加、删除、列表播放歌曲等)

// ==UserScript==
// @name         听歌小助手
// @namespace    https://github.com/fan0530
// @version      1.1.2
// @author       fanxq
// @description  这个脚本主要为Hifini(音乐磁场)网站提供了一个歌曲管理页面(可添加、删除、列表播放歌曲等)
// @icon         https://cdn.jsdelivr.net/gh/fan0530/music-player/favicon.ico
// @match        https://hifini.com/*
// @match        https://www.hifini.com/*
// @require      https://unpkg.com/vue@3.4.19/dist/vue.global.prod.js
// @require      data:application/javascript,window.Vue%3DVue%3B
// @require      https://unpkg.com/element-plus@2.5.6/dist/index.full.min.js
// @require      https://unpkg.com/idb-keyval@6.2.1/dist/umd.js
// @resource     element-plus/dist/index.css  https://unpkg.com/element-plus@2.5.6/dist/index.css
// @resource     player.html                  https://cdn.jsdelivr.net/gh/fan0530/music-player@main/index.v2024122201.html
// @connect      hifini.com
// @connect      gitee.com
// @grant        GM_addStyle
// @grant        GM_getResourceText
// @grant        GM_xmlhttpRequest
// @grant        unsafeWindow
// @grant        window.focus
// @run-at       document-end
// ==/UserScript==

(e=>{if(typeof GM_addStyle=="function"){GM_addStyle(e);return}const t=document.createElement("style");t.textContent=e,document.head.append(t)})(" .my-help-dialog{--el-dialog-width: 92% !important;max-width:none;min-width:none}.my-help-dialog .img-wrapper{margin:10px 0;padding:20px;background:#f7f7f7}@media screen and (min-width: 500px){.my-help-dialog{--el-dialog-width: 55% !important;max-width:800px;min-width:500px}}.loading-toast{--el-dialog-width: 140px !important;--el-dialog-border-radius: 10px !important}.loading-toast .el-dialog__header{display:none}.btn-container[data-v-3beef0e1]{position:fixed;bottom:100px;right:20px}.menu-popper .menu-list[data-v-3beef0e1]{list-style:none;margin:0;padding:0}.menu-popper .menu-list li[data-v-3beef0e1]{display:flex;flex-direction:column}.menu-popper .menu-list li .menu-item[data-v-3beef0e1]{display:flex;align-items:center;justify-content:center;color:#666}.menu-popper .menu-list li .menu-item[data-v-3beef0e1]:active{background-color:#80808033} ");

(function (vue, elementPlus, idbKeyval) {
  'use strict';

  var _GM_getResourceText = /* @__PURE__ */ (() => typeof GM_getResourceText != "undefined" ? GM_getResourceText : void 0)();
  var _GM_xmlhttpRequest = /* @__PURE__ */ (() => typeof GM_xmlhttpRequest != "undefined" ? GM_xmlhttpRequest : void 0)();
  var _unsafeWindow = /* @__PURE__ */ (() => typeof unsafeWindow != "undefined" ? unsafeWindow : void 0)();
  var DEFAULT_ICON_CONFIGS = {
    size: "1em",
    strokeWidth: 4,
    strokeLinecap: "round",
    strokeLinejoin: "round",
    rtl: false,
    theme: "outline",
    colors: {
      outline: {
        fill: "#333",
        background: "transparent"
      },
      filled: {
        fill: "#333",
        background: "#FFF"
      },
      twoTone: {
        fill: "#333",
        twoTone: "#2F88FF"
      },
      multiColor: {
        outStrokeColor: "#333",
        outFillColor: "#2F88FF",
        innerStrokeColor: "#FFF",
        innerFillColor: "#43CCF8"
      }
    },
    prefix: "i"
  };
  function guid() {
    return "icon-" + ((1 + Math.random()) * 4294967296 | 0).toString(16).substring(1);
  }
  function IconConverter(id, icon, config) {
    var fill = typeof icon.fill === "string" ? [icon.fill] : icon.fill || [];
    var colors = [];
    var theme = icon.theme || config.theme;
    switch (theme) {
      case "outline":
        colors.push(typeof fill[0] === "string" ? fill[0] : "currentColor");
        colors.push("none");
        colors.push(typeof fill[0] === "string" ? fill[0] : "currentColor");
        colors.push("none");
        break;
      case "filled":
        colors.push(typeof fill[0] === "string" ? fill[0] : "currentColor");
        colors.push(typeof fill[0] === "string" ? fill[0] : "currentColor");
        colors.push("#FFF");
        colors.push("#FFF");
        break;
      case "two-tone":
        colors.push(typeof fill[0] === "string" ? fill[0] : "currentColor");
        colors.push(typeof fill[1] === "string" ? fill[1] : config.colors.twoTone.twoTone);
        colors.push(typeof fill[0] === "string" ? fill[0] : "currentColor");
        colors.push(typeof fill[1] === "string" ? fill[1] : config.colors.twoTone.twoTone);
        break;
      case "multi-color":
        colors.push(typeof fill[0] === "string" ? fill[0] : "currentColor");
        colors.push(typeof fill[1] === "string" ? fill[1] : config.colors.multiColor.outFillColor);
        colors.push(typeof fill[2] === "string" ? fill[2] : config.colors.multiColor.innerStrokeColor);
        colors.push(typeof fill[3] === "string" ? fill[3] : config.colors.multiColor.innerFillColor);
        break;
    }
    return {
      size: icon.size || config.size,
      strokeWidth: icon.strokeWidth || config.strokeWidth,
      strokeLinecap: icon.strokeLinecap || config.strokeLinecap,
      strokeLinejoin: icon.strokeLinejoin || config.strokeLinejoin,
      colors,
      id
    };
  }
  var IconContext = Symbol("icon-context");
  function IconWrapper(name, rtl, render) {
    var options = {
      name: "icon-" + name,
      props: ["size", "strokeWidth", "strokeLinecap", "strokeLinejoin", "theme", "fill", "spin"],
      setup: function setup(props) {
        var id = guid();
        var ICON_CONFIGS = vue.inject(IconContext, DEFAULT_ICON_CONFIGS);
        return function() {
          var size = props.size, strokeWidth = props.strokeWidth, strokeLinecap = props.strokeLinecap, strokeLinejoin = props.strokeLinejoin, theme = props.theme, fill = props.fill, spin = props.spin;
          var svgProps = IconConverter(id, {
            size,
            strokeWidth,
            strokeLinecap,
            strokeLinejoin,
            theme,
            fill
          }, ICON_CONFIGS);
          var cls = [ICON_CONFIGS.prefix + "-icon"];
          cls.push(ICON_CONFIGS.prefix + "-icon-" + name);
          if (rtl && ICON_CONFIGS.rtl) {
            cls.push(ICON_CONFIGS.prefix + "-icon-rtl");
          }
          if (spin) {
            cls.push(ICON_CONFIGS.prefix + "-icon-spin");
          }
          return vue.createVNode("span", {
            "class": cls.join(" ")
          }, [render(svgProps)]);
        };
      }
    };
    return options;
  }
  const AddMusic = IconWrapper("add-music", true, function(props) {
    return vue.createVNode("svg", {
      "width": props.size,
      "height": props.size,
      "viewBox": "0 0 48 48",
      "fill": "none"
    }, [vue.createVNode("path", {
      "d": "M24 44C12.9543 44 4 35.0457 4 24C4 12.9543 12.9543 4 24 4C35.0457 4 44 12.9543 44 24",
      "stroke": props.colors[0],
      "stroke-width": props.strokeWidth,
      "stroke-linecap": props.strokeLinecap,
      "stroke-linejoin": props.strokeLinejoin
    }, null), vue.createVNode("path", {
      "d": "M20 24V17.0718L26 20.5359L32 24L26 27.4641L20 30.9282V24Z",
      "fill": props.colors[1],
      "stroke": props.colors[0],
      "stroke-width": props.strokeWidth,
      "stroke-linejoin": props.strokeLinejoin
    }, null), vue.createVNode("path", {
      "d": "M37.0508 32L37.0508 42",
      "stroke": props.colors[0],
      "stroke-width": props.strokeWidth,
      "stroke-linecap": props.strokeLinecap,
      "stroke-linejoin": props.strokeLinejoin
    }, null), vue.createVNode("path", {
      "d": "M42 36.9497L32 36.9497",
      "stroke": props.colors[0],
      "stroke-width": props.strokeWidth,
      "stroke-linecap": props.strokeLinecap,
      "stroke-linejoin": props.strokeLinejoin
    }, null)]);
  });
  const Down = IconWrapper("down", false, function(props) {
    return vue.createVNode("svg", {
      "width": props.size,
      "height": props.size,
      "viewBox": "0 0 48 48",
      "fill": "none"
    }, [vue.createVNode("path", {
      "d": "M36 18L24 30L12 18",
      "stroke": props.colors[0],
      "stroke-width": props.strokeWidth,
      "stroke-linecap": props.strokeLinecap,
      "stroke-linejoin": props.strokeLinejoin
    }, null)]);
  });
  const Help = IconWrapper("help", true, function(props) {
    return vue.createVNode("svg", {
      "width": props.size,
      "height": props.size,
      "viewBox": "0 0 48 48",
      "fill": "none"
    }, [vue.createVNode("path", {
      "d": "M24 44C29.5228 44 34.5228 41.7614 38.1421 38.1421C41.7614 34.5228 44 29.5228 44 24C44 18.4772 41.7614 13.4772 38.1421 9.85786C34.5228 6.23858 29.5228 4 24 4C18.4772 4 13.4772 6.23858 9.85786 9.85786C6.23858 13.4772 4 18.4772 4 24C4 29.5228 6.23858 34.5228 9.85786 38.1421C13.4772 41.7614 18.4772 44 24 44Z",
      "fill": props.colors[1],
      "stroke": props.colors[0],
      "stroke-width": props.strokeWidth,
      "stroke-linejoin": props.strokeLinejoin
    }, null), vue.createVNode("path", {
      "d": "M24 28.6248V24.6248C27.3137 24.6248 30 21.9385 30 18.6248C30 15.3111 27.3137 12.6248 24 12.6248C20.6863 12.6248 18 15.3111 18 18.6248",
      "stroke": props.colors[2],
      "stroke-width": props.strokeWidth,
      "stroke-linecap": props.strokeLinecap,
      "stroke-linejoin": props.strokeLinejoin
    }, null), vue.createVNode("path", {
      "fill-rule": "evenodd",
      "clip-rule": "evenodd",
      "d": "M24 37.6248C25.3807 37.6248 26.5 36.5055 26.5 35.1248C26.5 33.7441 25.3807 32.6248 24 32.6248C22.6193 32.6248 21.5 33.7441 21.5 35.1248C21.5 36.5055 22.6193 37.6248 24 37.6248Z",
      "fill": props.colors[2]
    }, null)]);
  });
  const Record = IconWrapper("record", true, function(props) {
    return vue.createVNode("svg", {
      "width": props.size,
      "height": props.size,
      "viewBox": "0 0 48 48",
      "fill": "none"
    }, [vue.createVNode("rect", {
      "x": "5",
      "y": "18",
      "width": "38",
      "height": "24",
      "rx": "2",
      "fill": props.colors[1],
      "stroke": props.colors[0],
      "stroke-width": props.strokeWidth,
      "stroke-linecap": props.strokeLinecap,
      "stroke-linejoin": props.strokeLinejoin
    }, null), vue.createVNode("path", {
      "d": "M8 12H40",
      "stroke": props.colors[0],
      "stroke-width": props.strokeWidth,
      "stroke-linecap": props.strokeLinecap,
      "stroke-linejoin": props.strokeLinejoin
    }, null), vue.createVNode("path", {
      "d": "M15 6L33 6",
      "stroke": props.colors[0],
      "stroke-width": props.strokeWidth,
      "stroke-linecap": props.strokeLinecap,
      "stroke-linejoin": props.strokeLinejoin
    }, null), vue.createVNode("path", {
      "d": "M26 24V30",
      "stroke": props.colors[2],
      "stroke-width": props.strokeWidth,
      "stroke-linecap": props.strokeLinecap,
      "stroke-linejoin": props.strokeLinejoin
    }, null), vue.createVNode("path", {
      "d": "M18 32.7491C18 31.2308 19.2894 30 20.88 30H26V33.2509C26 34.7692 24.7106 36 23.12 36H20.88C19.2894 36 18 34.7692 18 33.2509V32.7491Z",
      "stroke": props.colors[2],
      "stroke-width": props.strokeWidth,
      "stroke-linejoin": props.strokeLinejoin
    }, null), vue.createVNode("path", {
      "d": "M31 25L26 24",
      "stroke": props.colors[2],
      "stroke-width": props.strokeWidth,
      "stroke-linecap": props.strokeLinecap,
      "stroke-linejoin": props.strokeLinejoin
    }, null)]);
  });
  const RecordPlayer = IconWrapper("record-player", true, function(props) {
    return vue.createVNode("svg", {
      "width": props.size,
      "height": props.size,
      "viewBox": "0 0 48 48",
      "fill": "none"
    }, [vue.createVNode("rect", {
      "x": "5",
      "y": "8",
      "width": "38",
      "height": "32",
      "rx": "2",
      "stroke": props.colors[0],
      "stroke-width": props.strokeWidth
    }, null), vue.createVNode("path", {
      "d": "M13 8V40",
      "stroke": props.colors[0],
      "stroke-width": props.strokeWidth,
      "stroke-linecap": props.strokeLinecap,
      "stroke-linejoin": props.strokeLinejoin
    }, null), vue.createVNode("circle", {
      "cx": "28",
      "cy": "24",
      "r": "9",
      "fill": props.colors[1],
      "stroke": props.colors[0],
      "stroke-width": props.strokeWidth
    }, null), vue.createVNode("circle", {
      "cx": "28",
      "cy": "24",
      "r": "3",
      "fill": props.colors[2]
    }, null), vue.createVNode("path", {
      "d": "M5 16H13",
      "stroke": props.colors[0],
      "stroke-width": props.strokeWidth,
      "stroke-linecap": props.strokeLinecap,
      "stroke-linejoin": props.strokeLinejoin
    }, null), vue.createVNode("path", {
      "d": "M5 24H13",
      "stroke": props.colors[0],
      "stroke-width": props.strokeWidth,
      "stroke-linecap": props.strokeLinecap,
      "stroke-linejoin": props.strokeLinejoin
    }, null), vue.createVNode("path", {
      "d": "M5 32H13",
      "stroke": props.colors[0],
      "stroke-width": props.strokeWidth,
      "stroke-linecap": props.strokeLinecap,
      "stroke-linejoin": props.strokeLinejoin
    }, null)]);
  });
  const _hoisted_1$2 = ["id"];
  const _hoisted_2$1 = /* @__PURE__ */ vue.createElementVNode("p", null, [
    /* @__PURE__ */ vue.createTextVNode("具体使用说明请查看这篇文章: "),
    /* @__PURE__ */ vue.createElementVNode("a", {
      target: "_blank",
      href: "https://mp.weixin.qq.com/s?__biz=Mzg2NDgzMjU1OA==&mid=2247483945&idx=1&sn=cdf6194c875eeeb143c51a7a9cf8f520&chksm=ce621f68f915967e712d03de99b81e6f64f3e28ca4b970815e78d388b4ad76d04d6fd40e69e2#rd"
    }, " 有用这颗“黑凤梨”听歌的朋友吗?我写了个油猴脚本送给你 ")
  ], -1);
  const _hoisted_3$1 = /* @__PURE__ */ vue.createElementVNode("p", null, [
    /* @__PURE__ */ vue.createTextVNode("如果在使用该脚本的过程遇到了问题,或者觉得有需要改善的地方,可以在 "),
    /* @__PURE__ */ vue.createElementVNode("a", {
      target: "_blank",
      href: "https://greasyfork.org/zh-CN/scripts/497671-%E5%90%AC%E6%AD%8C%E5%B0%8F%E5%8A%A9%E6%89%8B/feedback"
    }, " 此处(https://greasyfork.org/zh-CN/scripts/497671-%E5%90%AC%E6%AD%8C%E5%B0%8F%E5%8A%A9%E6%89%8B/feedback) "),
    /* @__PURE__ */ vue.createTextVNode(" 反馈。 ")
  ], -1);
  const _hoisted_4$1 = /* @__PURE__ */ vue.createElementVNode("p", null, "最后,如果这个脚本对你有点帮助的话,可以考虑请我喝瓶快乐水,你的支持将给我动力持续去维护好这个脚本。", -1);
  const _hoisted_5$1 = /* @__PURE__ */ vue.createElementVNode("p", null, [
    /* @__PURE__ */ vue.createElementVNode("img", {
      src: "https://gitee.com/fanxiqian/music-player/raw/master/code.jpg",
      alt: "赞赏码",
      style: { "display": "block", "margin": "0 auto", "width": "100%", "max-width": "320px", "height": "auto" }
    })
  ], -1);
  const _sfc_main$2 = {
    __name: "HelpDialog",
    props: {
      show: {
        type: Boolean,
        default: false
      }
    },
    emits: ["update:show"],
    setup(__props, { emit: __emit }) {
      const emits = __emit;
      const updateShow = (visible) => {
        emits("update:show", visible);
      };
      return (_ctx, _cache) => {
        return vue.openBlock(), vue.createBlock(vue.unref(elementPlus.ElDialog), {
          "model-value": __props.show,
          onClose: _cache[0] || (_cache[0] = ($event) => updateShow(false)),
          class: "my-help-dialog",
          "align-center": ""
        }, {
          header: vue.withCtx(({ titleId, titleClass }) => [
            vue.createElementVNode("h4", {
              id: titleId,
              class: vue.normalizeClass(titleClass)
            }, "使用说明", 10, _hoisted_1$2)
          ]),
          default: vue.withCtx(() => [
            vue.createVNode(vue.unref(elementPlus.ElScrollbar), { height: "70vh" }, {
              default: vue.withCtx(() => [
                _hoisted_2$1,
                _hoisted_3$1,
                _hoisted_4$1,
                _hoisted_5$1
              ]),
              _: 1
            })
          ]),
          _: 1
        }, 8, ["model-value"]);
      };
    }
  };
  const _hoisted_1$1 = {
    "element-loading-text": "正在打开...",
    style: { "height": "108px" }
  };
  const _sfc_main$1 = {
    __name: "LoadingToast",
    props: {
      show: {
        type: Boolean,
        default: false
      }
    },
    emits: ["update:show"],
    setup(__props, { emit: __emit }) {
      const emits = __emit;
      const updateShow = (visible) => {
        emits("update:show", visible);
      };
      return (_ctx, _cache) => {
        const _directive_loading = vue.resolveDirective("loading");
        return vue.openBlock(), vue.createBlock(vue.unref(elementPlus.ElDialog), {
          "model-value": __props.show,
          onClose: _cache[0] || (_cache[0] = ($event) => updateShow(false)),
          "align-center": "",
          "show-close": false,
          modal: false,
          "close-on-click-modal": false,
          "close-on-press-escape": false,
          class: "loading-toast"
        }, {
          default: vue.withCtx(() => [
            vue.withDirectives(vue.createElementVNode("div", _hoisted_1$1, null, 512), [
              [_directive_loading, true]
            ])
          ]),
          _: 1
        }, 8, ["model-value"]);
      };
    }
  };
  const _export_sfc = (sfc, props) => {
    const target = sfc.__vccOpts || sfc;
    for (const [key, val] of props) {
      target[key] = val;
    }
    return target;
  };
  const _withScopeId = (n) => (vue.pushScopeId("data-v-3beef0e1"), n = n(), vue.popScopeId(), n);
  const _hoisted_1 = { style: { "position": "absolute", "top": "5px", "right": "5px" } };
  const _hoisted_2 = { style: { "display": "flex", "align-items": "center", "justify-content": "center" } };
  const _hoisted_3 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ vue.createElementVNode("span", { style: { "margin-left": "8px" } }, "添加至歌单", -1));
  const _hoisted_4 = { style: { "display": "flex", "align-items": "center", "justify-content": "center" } };
  const _hoisted_5 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ vue.createElementVNode("span", { style: { "margin-left": "8px" } }, "添加至", -1));
  const _hoisted_6 = { class: "btn-container" };
  const _hoisted_7 = { class: "menu-list" };
  const _hoisted_8 = ["onClick"];
  const _hoisted_9 = { style: { "margin-left": "12px" } };
  const _sfc_main = {
    __name: "App",
    setup(__props) {
      const isPlayerExisted = vue.ref(false);
      const showHelpDialog = vue.ref(false);
      const isAdding = vue.ref(false);
      const showLoading = vue.ref(false);
      let targetWindow = null;
      const msgHub = [];
      const menus = vue.ref([
        {
          name: "usage",
          title: "使用说明",
          icon: vue.markRaw(Help),
          handler: () => {
            showHelpDialog.value = true;
          }
        },
        {
          name: "player",
          title: "打开歌单",
          icon: vue.markRaw(RecordPlayer),
          handler: () => {
            if (!showLoading.value) {
              showLoading.value = true;
            }
            openPlayer();
          }
        }
      ]);
      const requestAudioBlobData = (url) => {
        return new Promise((resolve, reject) => {
          _GM_xmlhttpRequest({
            method: "GET",
            url,
            headers: {
              "referer": "https://hifini.com/"
            },
            responseType: "blob",
            onload: function(res) {
              if (!(res == null ? void 0 : res.response)) {
                resolve(null);
              }
              resolve(res.response);
            },
            onerror: function(err) {
              resolve(null);
            }
          });
        });
      };
      const getAudioData = async () => {
        var _a;
        let audioItem = null;
        const targetScript = Array.from(_unsafeWindow.document.querySelectorAll("script")).filter((x) => x.innerHTML).find((x) => x.innerHTML.indexOf("APlayer") !== -1);
        if (targetScript && targetScript.innerHTML) {
          const code = targetScript.innerHTML;
          const matches = code.match(/\[([\s\S]*)\]/igm);
          if (matches && matches.length) {
            const musicInfo = matches[0];
            const func = new Function(`let a = ${musicInfo}; return a;`);
            const audioList = func();
            if (audioList && audioList.length) {
              audioItem = audioList[0];
            }
          }
        }
        if (audioItem) {
          audioItem.id = getId();
          audioItem.page = _unsafeWindow.location.href;
          if (/[\u4E00-\u9FFF]+/ig.test(audioItem.url) && ((_a = audioItem.url) == null ? void 0 : _a.startsWith("https"))) {
            const res = await requestAudioBlobData(audioItem.url);
            if (res) {
              audioItem.url = URL.createObjectURL(res);
              audioItem.storeKey = `no.${audioItem.id}`;
              idbKeyval.set(audioItem.storeKey, res).catch((err) => {
              });
            }
          }
        }
        return audioItem;
      };
      const getCacheId = () => {
        let id = null;
        return () => {
          if (!id) {
            id = Date.now();
            const result = /(\d+)/.exec(_unsafeWindow.location.pathname);
            if (result) {
              id = result[0];
            }
          }
          return id;
        };
      };
      const getId = getCacheId();
      const requestPlayerHtmlContent = () => {
        return new Promise((resolve, reject) => {
          _GM_xmlhttpRequest({
            method: "GET",
            // url: 'https://gitee.com/fanxiqian/music-player/raw/master/index.txt',
            url: "https://gitee.com/fanxiqian/music-player/raw/master/home",
            onload: function(response) {
              resolve(response.responseText);
            },
            onerror: function(err) {
              resolve();
            }
          });
        });
      };
      const openPlayerPage = async () => {
        const playerHtmlFromResource = _GM_getResourceText("player.html");
        let fileContent = [];
        if (playerHtmlFromResource) {
          fileContent = [playerHtmlFromResource];
        } else {
          const content = await requestPlayerHtmlContent();
          if (!content) {
            elementPlus.ElNotification({
              title: "提示",
              message: "获取播放列表页面出错了,请刷新页面重试",
              type: "error"
            });
            return;
          }
          fileContent = [content];
        }
        const playerBlob = new Blob(fileContent, { type: "text/html" });
        const url = URL.createObjectURL(playerBlob);
        if (!targetWindow) {
          targetWindow = _unsafeWindow.open(url);
        } else {
          if (targetWindow.closed) {
            targetWindow = _unsafeWindow.open(url);
          }
        }
        targetWindow.focus();
      };
      const openPlayer = () => {
        var _a;
        if (((_a = _unsafeWindow.document.cookie) == null ? void 0 : _a.indexOf("bbs_token")) === -1) {
          elementPlus.ElNotification({
            title: "提示",
            message: "请先登录 Hifini",
            type: "warning"
          });
          showLoading.value = false;
          return;
        }
        const channelName = _unsafeWindow.localStorage.getItem("channel");
        if (channelName) {
          if (targetWindow) {
            targetWindow.focus();
            showLoading.value = false;
            return;
          }
          if (!channel) {
            channel = new BroadcastChannel(channelName);
            channel.addEventListener("message", (e) => {
              if (e.data && e.data.from === "player") {
                msgHub.push(e.data);
                if (e.data.msg === "heartbeat") {
                  return;
                }
                elementPlus.ElNotification({
                  title: "提示",
                  message: e.data.code === 200 ? "歌曲添加成功" : e.data.msg,
                  type: e.data.code === 200 ? "success" : "warning"
                });
              }
            });
          }
          const msgId = Date.now();
          channel.postMessage({ msg: "heartbeat", msgId });
          setTimeout(() => {
            const idx = msgHub.findIndex((x) => x.msgId == msgId);
            if (idx !== -1) {
              msgHub.splice(idx, 1);
              elementPlus.ElNotification({
                title: "提示",
                message: `歌单页面已存在,请在浏览器标签页或者窗口中查找看看`,
                type: "info"
              });
            } else {
              _unsafeWindow.localStorage.removeItem("channel");
              channel.close();
              channel = null;
              openPlayer();
            }
            showLoading.value = false;
          }, 3e3);
          return;
        }
        openPlayerPage();
        showLoading.value = false;
      };
      const postAudioDataToPlayer = async (playlistId) => {
        await openPlayerPage();
        if (!targetWindow) {
          return;
        }
        const audioData = await getAudioData();
        setTimeout(() => {
          isAdding.value = false;
          targetWindow.postMessage({ ...audioData, msgId: Date.now(), playlistId }, "*");
        }, 1200);
      };
      const isSongExisted = (id, playlistId) => {
        var _a, _b;
        let isExisted = false;
        try {
          const storeData = JSON.parse(_unsafeWindow.localStorage.getItem("hifini-helper"));
          const tPlaylist = (_a = storeData == null ? void 0 : storeData.customPlaylist) == null ? void 0 : _a.find((x) => x.id === playlistId);
          if ((_b = tPlaylist == null ? void 0 : tPlaylist.songIdList) == null ? void 0 : _b.includes(id)) {
            isExisted = true;
          }
        } catch (error) {
        }
        return isExisted;
      };
      let channel = null;
      const addSong = async (playlistId) => {
        const id = getId();
        if (isSongExisted(id, playlistId)) {
          elementPlus.ElNotification({
            title: "提示",
            message: "歌曲已存在,请勿重复添加!",
            type: "warning"
          });
          return;
        }
        const channelName = _unsafeWindow.localStorage.getItem("channel");
        if (channelName) {
          isAdding.value = true;
          if (!channel) {
            channel = new BroadcastChannel(channelName);
            channel.addEventListener("message", (e) => {
              if (e.data && e.data.from === "player") {
                isAdding.value = false;
                msgHub.push(e.data);
                if (e.data.msg === "heartbeat") {
                  return;
                }
                elementPlus.ElNotification({
                  title: "提示",
                  message: e.data.code === 200 ? "歌曲添加成功" : e.data.msg,
                  type: e.data.code === 200 ? "success" : "warning"
                });
              }
            });
          }
          const audioData2 = await getAudioData();
          const msgId = Date.now();
          channel.postMessage({ ...audioData2, msgId, playlistId });
          setTimeout(() => {
            isAdding.value = false;
            const idx = msgHub.findIndex((x) => x.msgId == msgId);
            if (idx !== -1) {
              msgHub.splice(idx, 1);
            } else {
              _unsafeWindow.localStorage.removeItem("channel");
              channel.close();
              channel = null;
              postAudioDataToPlayer(playlistId);
            }
          }, 3e3);
          return;
        }
        await openPlayerPage();
        if (!targetWindow) {
          return;
        }
        const audioData = await getAudioData();
        setTimeout(() => {
          isAdding.value = false;
          targetWindow.postMessage({ ...audioData, msgId: Date.now(), playlistId }, "*");
        }, 1200);
      };
      _unsafeWindow.addEventListener("message", (e) => {
        if (e.data === "focus") {
          window.focus();
        }
      });
      const customPlaylist = vue.ref([]);
      const setCustomPlaylist = () => {
        var _a;
        const storeData = JSON.parse(_unsafeWindow.localStorage.getItem("hifini-helper"));
        const tCustomPlaylist = (_a = storeData == null ? void 0 : storeData.customPlaylist) == null ? void 0 : _a.map((x) => ({ id: x.id, name: x.name }));
        if ((tCustomPlaylist == null ? void 0 : tCustomPlaylist.length) > 1) {
          customPlaylist.value = tCustomPlaylist;
        } else {
          customPlaylist.value = [];
        }
      };
      const main = () => {
        const aplayerElement = document.querySelector(".aplayer");
        if (aplayerElement) {
          isPlayerExisted.value = true;
          aplayerElement.style.position = "relative";
        }
        setCustomPlaylist();
        _unsafeWindow.addEventListener("storage", (event) => {
          var _a;
          if ((_a = event.url) == null ? void 0 : _a.startsWith("blob:")) {
            setCustomPlaylist();
          }
        });
      };
      main();
      return (_ctx, _cache) => {
        var _a;
        return vue.openBlock(), vue.createElementBlock(vue.Fragment, null, [
          isPlayerExisted.value ? (vue.openBlock(), vue.createBlock(vue.Teleport, {
            key: 0,
            to: ".aplayer"
          }, [
            vue.createElementVNode("div", _hoisted_1, [
              !((_a = customPlaylist.value) == null ? void 0 : _a.length) ? (vue.openBlock(), vue.createBlock(vue.unref(elementPlus.ElButton), {
                key: 0,
                style: { "color": "#515151" },
                color: "#ffd448",
                onClick: _cache[0] || (_cache[0] = ($event) => addSong("default")),
                loading: isAdding.value
              }, {
                default: vue.withCtx(() => [
                  vue.createElementVNode("div", _hoisted_2, [
                    vue.createVNode(vue.unref(AddMusic), {
                      theme: "outline",
                      size: "22",
                      fill: "#666"
                    }),
                    _hoisted_3
                  ])
                ]),
                _: 1
              }, 8, ["loading"])) : (vue.openBlock(), vue.createBlock(vue.unref(elementPlus.ElDropdown), {
                key: 1,
                onCommand: addSong
              }, {
                dropdown: vue.withCtx(() => [
                  vue.createVNode(vue.unref(elementPlus.ElDropdownMenu), null, {
                    default: vue.withCtx(() => [
                      (vue.openBlock(true), vue.createElementBlock(vue.Fragment, null, vue.renderList(customPlaylist.value, (item, index) => {
                        return vue.openBlock(), vue.createBlock(vue.unref(elementPlus.ElDropdownItem), {
                          key: index,
                          command: item.id
                        }, {
                          default: vue.withCtx(() => [
                            vue.createTextVNode(vue.toDisplayString(item.name), 1)
                          ]),
                          _: 2
                        }, 1032, ["command"]);
                      }), 128))
                    ]),
                    _: 1
                  })
                ]),
                default: vue.withCtx(() => [
                  vue.createVNode(vue.unref(elementPlus.ElButton), {
                    style: { "color": "#515151" },
                    color: "#ffd448",
                    loading: isAdding.value
                  }, {
                    default: vue.withCtx(() => [
                      vue.createElementVNode("div", _hoisted_4, [
                        vue.createVNode(vue.unref(AddMusic), {
                          theme: "outline",
                          size: "22",
                          fill: "#666"
                        }),
                        _hoisted_5,
                        vue.createVNode(vue.unref(Down), {
                          style: { "margin-left": "8px" },
                          theme: "outline",
                          size: "22",
                          fill: "#666"
                        })
                      ])
                    ]),
                    _: 1
                  }, 8, ["loading"])
                ]),
                _: 1
              }))
            ])
          ])) : vue.createCommentVNode("", true),
          vue.createElementVNode("div", _hoisted_6, [
            vue.createVNode(vue.unref(elementPlus.ElPopover), {
              placement: "top-end",
              trigger: "click",
              "popper-class": "menu-popper"
            }, {
              reference: vue.withCtx(() => [
                vue.createVNode(vue.unref(elementPlus.ElButton), {
                  style: { "width": "40px", "height": "40px" },
                  circle: ""
                }, {
                  default: vue.withCtx(() => [
                    vue.createVNode(vue.unref(Record), {
                      theme: "two-tone",
                      size: "30",
                      fill: ["#409c3f", "#ffd448"],
                      strokeWidth: 3
                    })
                  ]),
                  _: 1
                })
              ]),
              default: vue.withCtx(() => [
                vue.createElementVNode("ul", _hoisted_7, [
                  (vue.openBlock(true), vue.createElementBlock(vue.Fragment, null, vue.renderList(menus.value, (item, index) => {
                    return vue.openBlock(), vue.createElementBlock("li", {
                      key: item.name
                    }, [
                      vue.createElementVNode("a", {
                        class: "menu-item",
                        onClick: () => item.handler(),
                        href: "javascript: void 0;"
                      }, [
                        (vue.openBlock(), vue.createBlock(vue.resolveDynamicComponent(item.icon), {
                          size: "22",
                          strokeWidth: 3,
                          fill: "#666",
                          style: { "line-height": "1" }
                        })),
                        vue.createElementVNode("span", _hoisted_9, vue.toDisplayString(item.title), 1)
                      ], 8, _hoisted_8),
                      index !== menus.value.length - 1 ? (vue.openBlock(), vue.createBlock(vue.unref(elementPlus.ElDivider), {
                        key: 0,
                        style: { "margin": "10px 0" }
                      })) : vue.createCommentVNode("", true)
                    ]);
                  }), 128))
                ])
              ]),
              _: 1
            })
          ]),
          vue.createVNode(_sfc_main$2, {
            show: showHelpDialog.value,
            "onUpdate:show": _cache[1] || (_cache[1] = ($event) => showHelpDialog.value = $event)
          }, null, 8, ["show"]),
          vue.createVNode(_sfc_main$1, {
            show: showLoading.value,
            "onUpdate:show": _cache[2] || (_cache[2] = ($event) => showLoading.value = $event)
          }, null, 8, ["show"])
        ], 64);
      };
    }
  };
  const App = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-3beef0e1"]]);
  const cssLoader = (e) => {
    const t = GM_getResourceText(e);
    return GM_addStyle(t), t;
  };
  cssLoader("element-plus/dist/index.css");
  vue.createApp(App).use(elementPlus.ElLoading).mount(
    (() => {
      const app = document.createElement("div");
      document.body.append(app);
      return app;
    })()
  );

})(Vue, ElementPlus, idbKeyval);