DMHY download helper

A userscript for share.dmhy.org.

作者のサイトでサポートを受ける。または、このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください。
// ==UserScript==
// @name               DMHY download helper
// @name:zh-CN         动漫花园下载助手
// @description        A userscript for share.dmhy.org.
// @description:zh-CN  动漫花园(share.dmhy.org)扩展,提供批量选择、列表下载种子文件及详情页树状展示等功能。
// @author             Xingwang Liao
// @namespace          https://github.com/kuoruan
// @homepage           https://github.com/kuoruan/dmhy-download-helper#readme
// @supportURL         https://github.com/kuoruan/dmhy-download-helper/issues
// @match              *://dmhy.org/
// @match              *://dmhy.org/topics/list/*
// @match              *://dmhy.org/topics/list?*
// @match              *://dmhy.org/topics/view/*
// @match              *://www.dmhy.org/
// @match              *://www.dmhy.org/topics/list/*
// @match              *://www.dmhy.org/topics/list?*
// @match              *://www.dmhy.org/topics/view/*
// @match              *://share.dmhy.org/
// @match              *://share.dmhy.org/topics/list/*
// @match              *://share.dmhy.org/topics/list?*
// @match              *://share.dmhy.org/topics/view/*
// @match              *://dmhy.b168.net/
// @match              *://dmhy.b168.net/topics/list/*
// @match              *://dmhy.b168.net/topics/list?*
// @match              *://dmhy.b168.net/topics/view/*
// @require            https://cdn.jsdelivr.net/npm/vue@2.7.16/dist/vue.min.js
// @require            https://cdn.jsdelivr.net/npm/xbytes@1.9.1/dist/index.min.js
// @connect            dmhy.org
// @connect            b168.net
// @grant              GM_addStyle
// @grant              GM_setClipboard
// @grant              GM_xmlhttpRequest
// @source             https://github.com/kuoruan/dmhy-download-helper.git
// @license            MIT
// @run-at             document-end
// @version            1.6.2
// @icon               https://share.dmhy.org/favicon.ico
// ==/UserScript==

(function() {
  "use strict";
  GM_addStyle('body{position:relative}#topic_list .odd:hover,#topic_list .even:hover,#topic_list .odd:hover td,#topic_list .even:hover td{background-color:#0eb9e7}.toast[data-v-e92e135b]{position:fixed;bottom:30px;left:50%;z-index:99;display:none;padding:10px 20px;border-radius:2px;background-color:#333;transform:translate(-50%)}.toast.show[data-v-e92e135b]{display:block;animation:fadein-e92e135b .5s,fadeout-e92e135b .5s 2.5s}.toast .text[data-v-e92e135b]{color:#fff;font-size:14px;line-height:1.5}@-moz-keyframes fadein-e92e135b{0%{bottom:0;opacity:0}to{bottom:30px;opacity:1}}@-webkit-keyframes fadein-e92e135b{0%{bottom:0;opacity:0}to{bottom:30px;opacity:1}}@-o-keyframes fadein-e92e135b{0%{bottom:0;opacity:0}to{bottom:30px;opacity:1}}@keyframes fadein-e92e135b{0%{bottom:0;opacity:0}to{bottom:30px;opacity:1}}@-moz-keyframes fadeout-e92e135b{0%{bottom:30px;opacity:1}to{bottom:0;opacity:0}}@-webkit-keyframes fadeout-e92e135b{0%{bottom:30px;opacity:1}to{bottom:0;opacity:0}}@-o-keyframes fadeout-e92e135b{0%{bottom:30px;opacity:1}to{bottom:0;opacity:0}}@keyframes fadeout-e92e135b{0%{bottom:30px;opacity:1}to{bottom:0;opacity:0}}.select-all[data-v-2f21319e],.select[data-v-6070ffc2]{cursor:pointer;width:14px;height:14px}.overlay[data-v-99f90792]{position:fixed;top:0;right:0;bottom:0;left:0;background-color:#0000004d;text-align:center}.overlay[data-v-99f90792]:after{display:inline-block;width:0;height:100%;content:"";vertical-align:middle}.overlay .popup[data-v-99f90792]{display:inline-block;overflow:hidden;padding:2px;border:1px solid #247;background-color:#fff;box-shadow:0 2px 12px #0000001a;text-align:left;backface-visibility:hidden}.overlay .popup.middle[data-v-99f90792]{vertical-align:middle}.btn[data-v-272d8422]{padding:2px 5px;outline:none;border:1px solid #247;background-color:#fff;color:#247}.popup-header[data-v-272d8422]{display:flex;flex-direction:row;align-items:center;padding:5px;background-color:#247}.popup-header h4[data-v-272d8422]{flex:1 1 0;color:#fff;text-align:left;font-weight:400;font-size:14px;line-height:1.5}.popup-body[data-v-272d8422]{padding:5px;background-color:#fff}.popup-body .links-box[data-v-272d8422]{overflow:auto;padding:4px 8px;border:1px solid #247;background-color:#eef;background-image:none;color:#333;font-size:12px;line-height:1.5;resize:none;cursor:text}.popup-footer[data-v-272d8422]{display:flex;flex-direction:row;align-items:center;padding:5px;background-color:#cdf}.popup-footer .btn[data-v-272d8422]:not(:first-child){margin-left:10px}.popup-footer p[data-v-272d8422]{flex:1 1 0;margin:0;text-align:right}.tool-bar[data-v-0aeef348]{display:none;background-color:#247;color:#fff}.tool-bar.top[data-v-0aeef348]{border-bottom:1px solid #fff}.tool-bar.bottom[data-v-0aeef348]{border-top:1px solid #fff}.tool-bar.visible[data-v-0aeef348]{display:block}.wrapper[data-v-0aeef348]{display:flex;flex-direction:row;align-items:center;height:auto}.wrapper label[data-v-0aeef348]{margin-left:10px}.wrapper .title[data-v-0aeef348]{padding:8px 15px;border-right:2px solid #fff}.wrapper .checkbox[data-v-0aeef348]{width:14px;height:14px;vertical-align:middle}.wrapper .btn-wrapper[data-v-0aeef348]{margin-left:10px}.wrapper .btn-wrapper .btn[data-v-0aeef348]{margin:0 5px;padding:2px 5px;outline:none;border:1px solid #247;background-color:#fff;color:#247}.loading[data-v-f5ce47f3]{display:inline-block;border:2px solid #dbdfe4;border-radius:50%;border-top:2px solid #075ea4;width:12px;height:12px;animation:spinner-f5ce47f3 2s linear infinite}@-moz-keyframes spinner-f5ce47f3{0%{transform:rotate(0)}to{transform:rotate(360deg)}}@-webkit-keyframes spinner-f5ce47f3{0%{transform:rotate(0)}to{transform:rotate(360deg)}}@-o-keyframes spinner-f5ce47f3{0%{transform:rotate(0)}to{transform:rotate(360deg)}}@keyframes spinner-f5ce47f3{0%{transform:rotate(0)}to{transform:rotate(360deg)}}li.tree-item[data-v-3ab763b5]{margin:0;padding:2px 0 2px 16px;background:url(data:image/gif;base64,R0lGODlhEADwBvABAICAgAAAACH5BAUAAAEALAAAAAAQAPAGQAL/jI+py+0Po5y02ouz3rz7D4biSJbmiabqyrbuC8fyTNf2jef6zvf+DwwKh8Si8YhMKpfMpvMJjUqn1Kr1is1qt9yu9wsOi8fksvmMTqvX7Lb7DY/L5/S6/Y7P6/f8vv8PGCg4SFhoeIiYqLjI2Oj4CBkpOUlZaXmJmam5ydnp+QkaKjpKWmp6ipqqusp6ATDxKhEbMQvxehuAq1D7wOvg2wDMILxAvAuLLJtMu2zb3Pv8Gx08PVxdfH2svM3M7ewNDS4tTk1ubY6Nrt3N/t0e/j4eXz5/Xp9+v+6+D88v708PoD2B+Ajq64fwX8KACwc2LPjwoMKJDL0YS3ARQcYD/xsNdMyVDWNIjSM5lvR4EqQ6kStJtjT5EmVMlflY1nR5E2ZOmTtpGrT5E2dQnUN5FvUZEWhSoUuJNjX6FClFh1MhVpVYMStVrVa5Yt0KtmvYr2LLkj2r9Gpar2vHtjX7Fi1TtXPZ1nV7F25euU7p9rX7F29gvYP5QvV7GHBiwYsJNzYsVXFkxpMdV4b8MXNKzTM59/R8FHRU0Ygvxz29F3Vh1Y9Zm04Ne3Xs1rNfy75NG7ft3Lx3+yYtGThl4ZaJY96MvHPyz8tDNx/9vLRx19NrV9d9vXf239GDdx/+vXj448rLMzfvHD109dLHU3dvHT52+drpc2fvHT94/eL5k/8/D2B6Aa43YHv+vXdgfAnOt2B9Dd5XYH4R7jdhfxX+J2CGBGpo4IUIeqggiAyK6CCJEHIoIYoUqmghixhuCGOHLn44Y4g1jnhjiTmeGGOKPa74Y4tBvijjkDQaaSOSOCqpI5M8Fgmlj1ECOaWQVRIpZZZUamkll1huCWaXYX4pZplknnnklWl6ueaYbZr5JppJqjknm3W6eSececq5JJ192vknnoHqOSifTfp5KKCJCroooY0a+qSikTI6qaOVQrpdpvZp+iCnJnq6I6hOioropXGeuieqhar6KKumpgrrqrG2Ouurst5KK6625srrrr6SKimwlAprKbGYbopsp8knfrpsqM2O+mypxro6ba3V6nptr9n+Gm2w3Q77bbHhHqtsuczicksBADs=) 0 0 no-repeat}li.tree-item.collection[data-v-3ab763b5]{background-position:0 -176px}li.tree-item.last[data-v-3ab763b5]{background-position:0 -1766px}li.tree-item .hitarea[data-v-3ab763b5]{float:left;margin-left:-16px;width:16px;height:16px;background:url(data:image/gif;base64,R0lGODlhYACFAPACAAAAAICAgCH5BAUAAAIALAAAAABgAIUAAAL/lI+py+0PBZjA0Yiz3qjyD4aaJ5amSZ7qyrbuGsSy/NZmsOD2/unS4eMJIz5ScIhk4CgTAe6ZjCKKQKl1ejBet0dDdzucicHkstl5Tqul37X7fWrDUfNkqlqPXCzNPOjOIeeXNVhoeAgmNoNY8iXICOEIGSI52SPBhAZliVHJqeH52ZkjCqoYU5pq9aja+sLqGqsCqwoo+2BLW7fXwOtqCyoKfEtcbFx3inqMl6BbSuW1zKwljcZUAeU8CY1Wza39GXqcDF5tfo6ero4xXN3OmdnbF/u+nGtef3y/zu+UbC7O2DeAhKJ5w3Qh27iC3aQNPKignCVy/SpavCgtnzGNWsU4IvK1AGStih5vlcSIMhZFiM0IRnTZ8uA1TQ2JBSx20yYpb/9S+vwJdNBJeiSJiewwL6griUohMW06aKXDnVNfssQiM5NCgVSX5bz1VZZUqGTLmj2LNu2tAgA7) -48px -47px no-repeat;cursor:pointer}li.tree-item .hitarea.last-hitarea[data-v-3ab763b5]{background-color:#fff;background-position:-32px -69px}li.tree-item .hitarea.collapsable-hitarea[data-v-3ab763b5]{background-position:-16px -91px}li.tree-item .hitarea.collapsable-hitarea.last-hitarea[data-v-3ab763b5]{background-position:0 -113px}li.tree-item .title[data-v-3ab763b5]{position:relative;display:flex;flex-direction:row;box-sizing:border-box;padding-left:18px;width:100%;height:16px;line-height:16px}li.tree-item .title h5[data-v-3ab763b5]{flex:1 1 0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-weight:400;font-size:12px}li.tree-item .title[data-v-3ab763b5]:before{position:absolute;top:0;left:0;display:block;width:16px;height:16px;background-color:#fff;background-repeat:no-repeat;content:""}li.tree-item .title.folder-close[data-v-3ab763b5]:before{background-image:url(data:image/gif;base64,R0lGODdhEAAOAPIHAK1zLeC6eP/insOHNP/////Sg59oJJdaHywAAAAAEAAOAEADM0i63AoiygnYkCWEwXkzYCg6RDeQyzWtB3MUcCy3aL1Ud33qDLRSDINsWDC4AMQYzUZKAAA7)}li.tree-item .title.folder-open[data-v-3ab763b5]:before{background-image:url(data:image/gif;base64,R0lGODlhEAAOAIIAAZdaH+C6eP/inq1zLf/////Sg59oJMOHNCwAAAAAEAAOAAIDN0i63P7vyAPXYBcewUsIk8RQFalsXKoCmDq8cEwMKa2mxWUUOe//GUDvB1wABsQiw8BsOitQQgIAOw==)}li.tree-item .title.document[data-v-3ab763b5]:before{background-image:url(data:image/gif;base64,R0lGODdhDwAOAPIHANS9fIy3oamMQX98XzmJdP////9tcGpsYiwAAAAADwAOAEADOlgl3KxQjWJMGefIKGi1UAdNUUQqYgQAKVoE8KQtZgHAAUFsYWm3rtKKxfH1jsbWybdclpqDqHSqSAAAOw==)}li.tree-item .title.video[data-v-3ab763b5]:before{background-image:url(/images/icon/mkv.gif)}li.tree-item .title.audio[data-v-3ab763b5]:before{background-image:url(/images/icon/mp3.gif)}li.tree-item .title.image[data-v-3ab763b5]:before{background-image:url(/images/icon/jpg.gif)}li.tree-item .title.archive[data-v-3ab763b5]:before{background-image:url(/images/icon/rar.gif)}li.tree-item .title.subtitle[data-v-3ab763b5]:before{background-image:url(/images/icon/txt.gif)}li.tree-item .title.unknown[data-v-3ab763b5]:before{background-image:url(/images/icon/unknown.gif)}li.tree-item .title .size[data-v-3ab763b5]{display:block;flex:0;color:gray;white-space:nowrap}li.tree-item:nth-child(2n)>.title[data-v-3ab763b5]{background-color:#cdf}li.tree-item::nth-child(odd)>.title[data-v-3ab763b5]{background-color:#fff}li.tree-item ul[data-v-3ab763b5]{margin:4px 0 0;padding:0;list-style:none}');
})();
(function(Vue2, XBytes) {
  "use strict";
  var render$9 = function render2() {
    var _vm = this, _c = _vm._self._c;
    return _c("div", { staticClass: "toast", class: { "show": _vm.show } }, [_c("span", { staticClass: "text" }, [_vm._v(_vm._s(_vm.text))])]);
  };
  var staticRenderFns$9 = [];
  function normalizeComponent(scriptExports, render2, staticRenderFns2, functionalTemplate, injectStyles, scopeId, moduleIdentifier, shadowMode) {
    var options = typeof scriptExports === "function" ? scriptExports.options : scriptExports;
    if (render2) {
      options.render = render2;
      options.staticRenderFns = staticRenderFns2;
      options._compiled = true;
    }
    if (scopeId) {
      options._scopeId = "data-v-" + scopeId;
    }
    return {
      exports: scriptExports,
      options
    };
  }
  const _sfc_main$9 = {
    name: "ToastItem",
    data() {
      return {
        text: "",
        show: false,
        timer: 0
      };
    },
    methods: {
      display(text) {
        if (this.timer) {
          window.clearTimeout(this.timer);
          this.timer = 0;
        }
        this.text = text;
        this.show = true;
        const _self = this;
        this.timer = window.setTimeout(function() {
          _self.show = false;
          _self.text = "";
          _self.timer = 0;
        }, 3e3);
      }
    }
  };
  var __component__$9 = /* @__PURE__ */ normalizeComponent(
    _sfc_main$9,
    render$9,
    staticRenderFns$9,
    false,
    null,
    "e92e135b"
  );
  const ToastItem = __component__$9.exports;
  var render$8 = function render2() {
    var _vm = this, _c = _vm._self._c;
    return _c("th", { staticClass: "{sorter: false}", attrs: { "width": "40" } }, [_c("input", { directives: [{ name: "model", rawName: "v-model", value: _vm.checked, expression: "checked" }], staticClass: "select-all", attrs: { "type": "checkbox" }, domProps: { "checked": Array.isArray(_vm.checked) ? _vm._i(_vm.checked, null) > -1 : _vm.checked }, on: { "change": [function($event) {
      var $$a = _vm.checked, $$el = $event.target, $$c = $$el.checked ? true : false;
      if (Array.isArray($$a)) {
        var $$v = null, $$i = _vm._i($$a, $$v);
        if ($$el.checked) {
          $$i < 0 && (_vm.checked = $$a.concat([$$v]));
        } else {
          $$i > -1 && (_vm.checked = $$a.slice(0, $$i).concat($$a.slice($$i + 1)));
        }
      } else {
        _vm.checked = $$c;
      }
    }, function($event) {
      return _vm.$emit("change", $event.target.checked);
    }] } })]);
  };
  var staticRenderFns$8 = [];
  const _sfc_main$8 = {
    name: "CheckboxHeader",
    data() {
      return {
        checked: false
      };
    }
  };
  var __component__$8 = /* @__PURE__ */ normalizeComponent(
    _sfc_main$8,
    render$8,
    staticRenderFns$8,
    false,
    null,
    "2f21319e"
  );
  const CheckboxHeader = __component__$8.exports;
  var render$7 = function render2() {
    var _vm = this, _c = _vm._self._c;
    return _c("td", { attrs: { "width": "40" } }, [_c("input", { directives: [{ name: "model", rawName: "v-model", value: _vm.checked, expression: "checked" }], staticClass: "select", attrs: { "type": "checkbox", "data-index": _vm.index }, domProps: { "checked": Array.isArray(_vm.checked) ? _vm._i(_vm.checked, null) > -1 : _vm.checked }, on: { "change": [function($event) {
      var $$a = _vm.checked, $$el = $event.target, $$c = $$el.checked ? true : false;
      if (Array.isArray($$a)) {
        var $$v = null, $$i = _vm._i($$a, $$v);
        if ($$el.checked) {
          $$i < 0 && (_vm.checked = $$a.concat([$$v]));
        } else {
          $$i > -1 && (_vm.checked = $$a.slice(0, $$i).concat($$a.slice($$i + 1)));
        }
      } else {
        _vm.checked = $$c;
      }
    }, function($event) {
      return _vm.$emit("change", $event.target.checked);
    }] } })]);
  };
  var staticRenderFns$7 = [];
  const _sfc_main$7 = {
    name: "CheckboxItem",
    props: {
      index: {
        type: Number,
        default: 0
      },
      magnet: {
        type: String,
        default: ""
      }
    },
    data() {
      return {
        checked: false
      };
    }
  };
  var __component__$7 = /* @__PURE__ */ normalizeComponent(
    _sfc_main$7,
    render$7,
    staticRenderFns$7,
    false,
    null,
    "6070ffc2"
  );
  const CheckboxItem = __component__$7.exports;
  var render$6 = function render2() {
    var _vm = this, _c = _vm._self._c;
    return _c("div", { staticClass: "overlay", style: { "z-index": _vm.zIndex }, on: { "click": function($event) {
      if ($event.target !== $event.currentTarget)
        return null;
      return _vm.$emit("overlay-click");
    }, "touchmove": _vm.onScroll, "mousewheel": _vm.onScroll } }, [_c("div", { staticClass: "popup", class: { "middle": _vm.middle }, style: { "margin-top": `${_vm.marginTop}px` } }, [_vm._t("default")], 2)]);
  };
  var staticRenderFns$6 = [];
  const _sfc_main$6 = {
    name: "PopupWrapper",
    props: {
      zIndex: {
        type: Number,
        default: 10
      },
      middle: {
        type: Boolean,
        default: false
      },
      marginTop: {
        type: Number,
        default: 0
      }
    },
    methods: {
      onScroll(evt) {
        const el = evt.target;
        const { overflow, overflowY, overflowX } = window.getComputedStyle(el);
        const { scrollTop, scrollHeight, clientHeight } = el;
        const isAutoOrScroll = /(auto|scroll)/.test(
          overflow + overflowX + overflowY
        );
        const scroll = scrollTop === 0 && evt.deltaY < 0 || Math.abs(scrollTop - (scrollHeight - clientHeight)) <= 1 && evt.deltaY > 0;
        if (!isAutoOrScroll || scroll) {
          evt.preventDefault();
        }
      }
    }
  };
  var __component__$6 = /* @__PURE__ */ normalizeComponent(
    _sfc_main$6,
    render$6,
    staticRenderFns$6,
    false,
    null,
    "99f90792"
  );
  const PopupWrapper = __component__$6.exports;
  function magnetLinksWithOptions(magnetLinks, opts) {
    if (!magnetLinks || magnetLinks.length <= 0) {
      return [];
    }
    if (opts.clean) {
      return magnetLinks.map((l) => l.substring(0, l.indexOf("&")));
    }
    return [...magnetLinks];
  }
  function getDefaultLinebreak() {
    let linebreak = "\n";
    if (navigator.userAgent.indexOf("Windows") > -1) {
      linebreak = "\r\n";
    }
    return linebreak;
  }
  function hashCode(str) {
    let hash = 0;
    if (!str || str.length <= 0)
      return hash;
    for (let i = 0, len = str.length; i < len; i++) {
      let chr = str.charCodeAt(i);
      hash = (hash << 5) - hash + chr;
      hash |= 0;
    }
    return hash;
  }
  const TORRENT_LINK_TAG_REGEX = /<a(?:.+)href=["']((?:https?:)?\/\/[^"']+\.torrent)["'](?:.*)>(.+)?<\/a>/;
  function getTorrentLinkFromHTML(html) {
    const matches = html.match(TORRENT_LINK_TAG_REGEX);
    if (matches && matches.length === 3) {
      return { href: matches[1].replace(/\n/g, ""), title: matches[2] };
    }
    return null;
  }
  var render$5 = function render2() {
    var _vm = this, _c = _vm._self._c;
    return _c("popup-wrapper", { attrs: { "z-index": _vm.zIndex, "middle": false, "margin-top": 100 }, on: { "overlay-click": function($event) {
      return _vm.$emit("close");
    } } }, [_c("div", { staticClass: "popup-header" }, [_c("h4", [_vm._v("查看链接")]), _c("button", { staticClass: "btn", on: { "click": function($event) {
      return _vm.$emit("close");
    } } }, [_vm._v("关闭")])]), _c("div", { staticClass: "popup-body" }, [_c("textarea", { directives: [{ name: "model", rawName: "v-model", value: _vm.content, expression: "content" }], ref: "textarea", staticClass: "links-box", style: _vm.textStyle, attrs: { "rows": "10", "cols": "80" }, domProps: { "value": _vm.content }, on: { "input": function($event) {
      if ($event.target.composing)
        return;
      _vm.content = $event.target.value;
    } } })]), _c("div", { staticClass: "popup-footer" }, [_c("button", { staticClass: "btn", on: { "click": _vm.resetContent } }, [_vm._v("重置")]), _c("button", { staticClass: "btn", on: { "click": _vm.selectAll } }, [_vm._v("全选")]), _c("button", { staticClass: "btn", on: { "click": _vm.copySelected } }, [_vm._v("复制选中")]), _c("button", { staticClass: "btn", on: { "click": _vm.copyAll } }, [_vm._v("复制全部")]), _c("p", [_vm._v("共 " + _vm._s(_vm.links.length) + " 条链接")])])]);
  };
  var staticRenderFns$5 = [];
  const _sfc_main$5 = {
    name: "LinksPopup",
    components: {
      PopupWrapper
    },
    props: {
      zIndex: {
        type: Number,
        default: 10
      },
      links: {
        type: Array,
        default() {
          return [];
        }
      },
      options: {
        type: Object,
        default() {
          const linebreak = getDefaultLinebreak();
          return {
            separator: linebreak
          };
        }
      }
    },
    data() {
      return {
        content: ""
      };
    },
    computed: {
      textStyle() {
        if (["\n", "\r\n"].indexOf(this.options.separator) > -1 && this.links.length > 1) {
          return {
            "white-space": "nowrap",
            "word-break": "normal"
          };
        } else {
          return {
            "white-space": "pre-line",
            "word-break": "break-all"
          };
        }
      }
    },
    watch: {
      links() {
        this.resetContent();
      }
    },
    created() {
      this.resetContent();
    },
    methods: {
      resetContent() {
        this.content = this.links.join(this.options.separator);
      },
      copySelected() {
        const target = this.$refs["textarea"];
        if (target) {
          const start = target.selectionStart;
          const finish = target.selectionEnd;
          if (start < 0 || finish <= start) {
            this.$toast.display("所选内容为空!");
            return;
          }
          try {
            const text = this.content.substring(start, finish);
            GM_setClipboard(text, "{ type: 'text', mimetype: 'text/plain'}");
            this.$toast.display("复制成功!");
          } catch (e) {
            this.$toast.display("复制失败。");
          }
        } else {
          this.$toast.display("获取文本框失败!");
        }
      },
      copyAll() {
        if (!this.content) {
          this.$toast.display("文本框内容为空!");
          return;
        }
        try {
          GM_setClipboard(
            this.content,
            "{ type: 'text', mimetype: 'text/plain'}"
          );
          this.$toast.display("复制成功!");
        } catch (e) {
          this.$toast.display("复制失败。");
        }
      },
      selectAll() {
        const target = this.$refs["textarea"];
        if (target) {
          target.select();
        } else {
          this.$toast.display("获取文本框失败!");
        }
      }
    }
  };
  var __component__$5 = /* @__PURE__ */ normalizeComponent(
    _sfc_main$5,
    render$5,
    staticRenderFns$5,
    false,
    null,
    "272d8422"
  );
  const LinksPopup = __component__$5.exports;
  var render$4 = function render2() {
    var _vm = this, _c = _vm._self._c;
    return _c("div", { staticClass: "tool-bar", class: [_vm.position, { "visible": _vm.visible }] }, [_c("div", { staticClass: "wrapper" }, [_c("span", { staticClass: "title" }, [_vm._v("下载助手")]), _c("label", { attrs: { "for": `clean-${_vm.position}` } }, [_vm._v("清理链接:")]), _c("input", { directives: [{ name: "model", rawName: "v-model", value: _vm.opts.clean, expression: "opts.clean" }], staticClass: "checkbox", attrs: { "id": `clean-${_vm.position}`, "type": "checkbox" }, domProps: { "checked": Array.isArray(_vm.opts.clean) ? _vm._i(_vm.opts.clean, null) > -1 : _vm.opts.clean }, on: { "change": function($event) {
      var $$a = _vm.opts.clean, $$el = $event.target, $$c = $$el.checked ? true : false;
      if (Array.isArray($$a)) {
        var $$v = null, $$i = _vm._i($$a, $$v);
        if ($$el.checked) {
          $$i < 0 && _vm.$set(_vm.opts, "clean", $$a.concat([$$v]));
        } else {
          $$i > -1 && _vm.$set(_vm.opts, "clean", $$a.slice(0, $$i).concat($$a.slice($$i + 1)));
        }
      } else {
        _vm.$set(_vm.opts, "clean", $$c);
      }
    } } }), _c("label", { attrs: { "for": `separator-${_vm.position}` } }, [_vm._v("分隔符:")]), _c("select", { directives: [{ name: "model", rawName: "v-model", value: _vm.opts.separator, expression: "opts.separator" }], attrs: { "id": `separator-${_vm.position}` }, on: { "change": function($event) {
      var $$selectedVal = Array.prototype.filter.call($event.target.options, function(o) {
        return o.selected;
      }).map(function(o) {
        var val = "_value" in o ? o._value : o.value;
        return val;
      });
      _vm.$set(_vm.opts, "separator", $event.target.multiple ? $$selectedVal : $$selectedVal[0]);
    } } }, [_c("option", { attrs: { "value": "\n" } }, [_vm._v("\\n")]), _c("option", { attrs: { "value": "\r\n" } }, [_vm._v("\\r\\n")]), _c("option", { attrs: { "value": "	" } }, [_vm._v("\\t")]), _c("option", { attrs: { "value": " " } }, [_vm._v("空格")]), _c("option", { attrs: { "value": "," } }, [_vm._v(",")])]), _c("div", { staticClass: "btn-wrapper" }, [_c("button", { staticClass: "btn", on: { "click": function($event) {
      return _vm.$emit("copy", _vm.opts);
    } } }, [_vm._v("复制")]), _c("button", { staticClass: "btn", on: { "click": function($event) {
      return _vm.$emit("show", _vm.opts);
    } } }, [_vm._v("查看")])])])]);
  };
  var staticRenderFns$4 = [];
  const _sfc_main$4 = {
    name: "ToolBar",
    props: {
      position: {
        type: String,
        default: "top"
      }
    },
    data() {
      let linebreak = getDefaultLinebreak();
      return {
        visible: false,
        opts: {
          clean: true,
          separator: linebreak
        }
      };
    }
  };
  var __component__$4 = /* @__PURE__ */ normalizeComponent(
    _sfc_main$4,
    render$4,
    staticRenderFns$4,
    false,
    null,
    "0aeef348"
  );
  const ToolBar = __component__$4.exports;
  var render$3 = function render2() {
    var _vm = this;
    _vm._self._c;
    return _vm._m(0);
  };
  var staticRenderFns$3 = [function() {
    var _vm = this, _c = _vm._self._c;
    return _c("th", { staticClass: "{sorter: false}", attrs: { "width": "5%", "nowrap": "nowrap" } }, [_c("span", { staticClass: "title" }, [_vm._v("Torrent")])]);
  }];
  const _sfc_main$3 = {
    name: "TorrentDownloadHeader"
  };
  var __component__$3 = /* @__PURE__ */ normalizeComponent(
    _sfc_main$3,
    render$3,
    staticRenderFns$3,
    false,
    null,
    null
  );
  const TorrentDownloadHeader = __component__$3.exports;
  var render$2 = function render2() {
    var _vm = this, _c = _vm._self._c;
    return _c("td", { attrs: { "nowrap": "nowrap", "align": "center" } }, [_vm.loading ? _c("span", { staticClass: "loading" }) : _c("a", { staticClass: "download-arrow arrow-torrent", attrs: { "title": "Torrent 下载", "href": "javascript:void(0);", "data-index": _vm.index }, on: { "click": _vm.getAndDownloadTorrent } })]);
  };
  var staticRenderFns$2 = [];
  const _sfc_main$2 = {
    name: "TorrentDownloadItem",
    props: {
      index: {
        type: Number,
        default: 0
      },
      detailLink: {
        type: String,
        default: ""
      },
      title: {
        type: String,
        default: ""
      }
    },
    data() {
      return {
        loading: false
      };
    },
    methods: {
      getTorrentUrl(pageLink, pageTitle) {
        return new Promise((resolve, reject) => {
          GM_xmlhttpRequest({
            method: "GET",
            url: pageLink,
            timeout: 15e3,
            context: { pageTitle },
            ontimeout: () => {
              reject(new Error("下载超时,请重试!"));
            },
            onerror: () => {
              reject(new Error("下载失败,请重试!"));
            },
            onload: ({ context = {}, responseText = "" }) => {
              let torrent;
              if (responseText && (torrent = getTorrentLinkFromHTML(responseText))) {
                let url = torrent.href;
                if (url.indexOf("//") === 0) {
                  url = window.location.protocol + url;
                }
                resolve({
                  url,
                  filename: `${torrent.title || context.pageTitle}.torrent`
                });
              } else {
                reject(new Error("获取下载链接失败!"));
              }
            }
          });
        });
      },
      async downloadTorrent(torrentUrl, torrentName) {
        const blob = await new Promise((resolve, reject) => {
          GM_xmlhttpRequest({
            method: "GET",
            url: torrentUrl,
            responseType: "blob",
            timeout: 15e3,
            ontimeout: () => {
              reject(new Error("下载超时,请重试!"));
            },
            onerror: () => {
              reject(new Error("下载失败,请重试!"));
            },
            onload: ({ response }) => {
              resolve(response);
            }
          });
        });
        const herf = URL.createObjectURL(blob);
        const anchor = document.createElement("a");
        anchor.href = herf;
        anchor.style.display = "none";
        anchor.download = torrentName;
        this.$el.appendChild(anchor);
        anchor.click();
        setTimeout(() => {
          this.$el.removeChild(anchor);
          URL.revokeObjectURL(herf);
        }, 0);
      },
      async getAndDownloadTorrent() {
        if (!this.detailLink) {
          this.$toast.display("无法获取下载链接。");
          return;
        }
        try {
          this.loading = true;
          const { url, filename } = await this.getTorrentUrl(
            this.detailLink,
            this.title
          );
          await this.downloadTorrent(url, filename);
        } catch (e) {
          this.$toast.display(e.message);
        } finally {
          this.loading = false;
        }
      }
    }
  };
  var __component__$2 = /* @__PURE__ */ normalizeComponent(
    _sfc_main$2,
    render$2,
    staticRenderFns$2,
    false,
    null,
    "f5ce47f3"
  );
  const TorrentDownloadItem = __component__$2.exports;
  const CheckboxHeaderVM = Vue2.extend(CheckboxHeader);
  const CheckboxItemVM = Vue2.extend(CheckboxItem);
  const TorrentDownloadHeaderVM = Vue2.extend(TorrentDownloadHeader);
  const TorrentDownloadItemVM = Vue2.extend(TorrentDownloadItem);
  const ToolBarVM = Vue2.extend(ToolBar);
  const LinksPopupVM = Vue2.extend(LinksPopup);
  function mountListElement(el) {
    const list = new Vue2({
      data() {
        return {
          header: null,
          all: [],
          selected: [],
          popupIndex: 10,
          toolbars: []
        };
      },
      computed: {
        links() {
          return this.selected.map((item) => item.magnet).filter((m) => !!m);
        }
      },
      watch: {
        links(val) {
          const isEmpty = !val || val.length <= 0;
          this.toolbars.forEach((t) => {
            t.visible = !isEmpty;
          });
        }
      },
      mounted() {
        this.$nextTick(function() {
          const table = this.$el;
          let tableContainer;
          if (!table.parentNode || (tableContainer = table.parentNode.parentNode, !tableContainer || tableContainer.className.indexOf("table") < 0)) {
            return;
          }
          if (table.tHead && table.tBodies) {
            if (table.tHead.rows && table.tHead.rows.length > 0) {
              this.insertHeaderToRow(table.tHead.rows[0]);
            }
            let index = 0;
            for (let i = 0, len = table.tBodies.length; i < len; i++) {
              let body = table.tBodies[i];
              for (let j = 0, rowLen = body.rows.length; j < rowLen; j++) {
                this.insertItemToRow(body.rows[j], index++);
              }
            }
          } else {
            if (table.rows) {
              for (let i = 0, len = table.rows.length; i < len; i++) {
                let row = table.rows[i];
                if (i === 0) {
                  this.insertHeaderToRow(row);
                } else {
                  this.insertItemToRow(row, i - 1);
                }
              }
            }
          }
          this.initToolBars(tableContainer);
        });
      },
      beforeDestroy() {
        if (this.header) {
          this.header.$off("change");
          this.header = null;
        }
        this.all.forEach((item) => {
          item.$off("change");
        });
        this.toolbars.forEach((t) => {
          t.$off("copy");
          t.$off("show");
        });
        this.all.splice(0, this.all.length);
        this.selected.splice(0, this.selected.length);
        this.toolbars.splice(0, this.toolbars.length);
      },
      methods: {
        initToolBars(tableContainer) {
          const headerToolbar = new ToolBarVM({
            propsData: {
              position: "top"
            }
          }).$mount();
          headerToolbar.$on("copy", this.onCopyLinks);
          headerToolbar.$on("show", this.onShowLinks);
          const bottomToobar = new ToolBarVM({
            propsData: {
              position: "bottom"
            }
          }).$mount();
          bottomToobar.$on("copy", this.onCopyLinks);
          bottomToobar.$on("show", this.onShowLinks);
          tableContainer.insertBefore(
            headerToolbar.$el,
            tableContainer.firstChild
          );
          tableContainer.appendChild(bottomToobar.$el);
          this.toolbars.push(headerToolbar, bottomToobar);
        },
        insertHeaderToRow(row) {
          if (row.cells.length <= 0)
            return;
          const firstCell = row.cells[0];
          let sizeCell;
          if (row.cells.length >= 5) {
            sizeCell = row.cells[4];
          }
          const checkboxTH = new CheckboxHeaderVM().$mount();
          checkboxTH.$on("change", this.onSelectAllChange);
          row.insertBefore(checkboxTH.$el, firstCell);
          this.header = checkboxTH;
          if (sizeCell) {
            const bittorrentDownloadTH = new TorrentDownloadHeaderVM().$mount();
            row.insertBefore(bittorrentDownloadTH.$el, sizeCell);
          }
        },
        insertItemToRow(row, index) {
          if (row.cells.length <= 0)
            return;
          const firstCell = row.cells[0];
          let sizeCell;
          if (row.cells.length >= 5) {
            sizeCell = row.cells[4];
          }
          const magnetLinkDOM = row.querySelector("td > .arrow-magnet");
          const checkboxTD = new CheckboxItemVM({
            propsData: {
              index,
              magnet: magnetLinkDOM ? magnetLinkDOM.href : ""
            }
          }).$mount();
          const _self = this;
          checkboxTD.$on("change", function(checked) {
            _self.onItemSelectChange(checkboxTD, checked);
          });
          row.insertBefore(checkboxTD.$el, firstCell);
          this.all.push(checkboxTD);
          if (sizeCell) {
            const detailLinkDom = row.querySelector("td.title > a");
            const bittorrentDownloadTD = new TorrentDownloadItemVM({
              propsData: {
                index,
                detailLink: detailLinkDom ? detailLinkDom.href : "",
                title: detailLinkDom ? detailLinkDom.innerText : ""
              }
            }).$mount();
            row.insertBefore(bittorrentDownloadTD.$el, sizeCell);
          }
        },
        onSelectAllChange(checked) {
          this.all.forEach(function(item) {
            item.checked = checked;
          });
          if (checked) {
            this.selected = [...this.all];
          } else {
            this.selected.splice(0, this.selected.length);
          }
        },
        onItemSelectChange(item, checked) {
          const selectedIndex = this.selected.indexOf(item);
          if (checked && selectedIndex < 0) {
            this.selected.push(item);
          } else if (!checked && selectedIndex > -1) {
            this.selected.splice(selectedIndex, 1);
          }
          if (this.header) {
            this.header.checked = this.all.length === this.selected.length;
          }
        },
        onCopyLinks(opts) {
          const links = magnetLinksWithOptions(this.links, opts);
          if (links.length > 0) {
            try {
              const content = links.join(opts.separator);
              GM_setClipboard(content, "{ type: 'text', mimetype: 'text/plain'}");
              this.$toast.display("复制成功!");
            } catch (e) {
              this.$toast.display("复制失败,请重试。");
            }
          }
        },
        onShowLinks(opts) {
          const links = magnetLinksWithOptions(this.links, opts);
          if (links.length > 0) {
            const popup = new LinksPopupVM({
              propsData: {
                zIndex: this.popupIndex++,
                links,
                options: opts
              }
            }).$mount();
            popup.$on("close", function() {
              popup.$off("close");
              try {
                popup.$el.remove();
              } catch (e) {
                document.body.removeChild(popup.$el);
              }
            });
            document.body.appendChild(popup.$el);
          }
        }
      }
    });
    list.$mount(el);
  }
  var render$1 = function render2() {
    var _vm = this, _c = _vm._self._c;
    return _c("li", { staticClass: "tree-item", class: { "collection": this.isFolder, "last": this.isLast } }, [_vm.isFolder ? _c("div", { staticClass: "hitarea", class: { "collapsable-hitarea": _vm.isOpen, "last-hitarea": _vm.isLast }, on: { "click": _vm.toggle } }) : _vm._e(), _c("div", { class: ["title", _vm.icon], on: { "click": function($event) {
      _vm.isFolder && _vm.toggle();
    } } }, [_c("h5", [_vm._v(_vm._s(_vm.name))]), _c("span", { staticClass: "size" }, [_vm._v(_vm._s(_vm.totalSize))])]), _vm.isFolder ? _c("ul", { directives: [{ name: "show", rawName: "v-show", value: _vm.isOpen, expression: "isOpen" }] }, _vm._l(_vm.children, function(child, index) {
      return _c("tree-item", _vm._b({ key: child.key, staticClass: "item", attrs: { "is-last": index === _vm.children.length - 1 } }, "tree-item", child, false));
    }), 1) : _vm._e()]);
  };
  var staticRenderFns$1 = [];
  const Videos = ["mp4", "rmvb", "avi", "mkv", "wmv", "flv", "ts"];
  const Audios = ["mp3", "ogg", "wma", "wav", "aac", "flac", "mka", "cue"];
  const Subtitles = ["sub", "idx", "sup", "sst", "srt", "ssa", "ass", "tts"];
  const Images = ["jpg", "jpeg", "png", "gif", "bmp", "pdf", "webp"];
  const Archives = ["rar", "rar5", "zip", "7z", "tar", "gz", "xz"];
  const Documents = [
    "txt",
    "log",
    "md",
    "doc",
    "docx",
    "xls",
    "xlsx",
    "ppt",
    "pptx",
    "md5"
  ];
  const _sfc_main$1 = {
    name: "TreeItem",
    props: {
      parentKey: {
        type: Number,
        default: -1
      },
      name: {
        type: String,
        default: ""
      },
      level: {
        type: Number,
        default: 1
      },
      size: {
        type: Number,
        default: 0
      },
      children: {
        type: Array,
        default() {
          return [];
        }
      },
      isLast: {
        type: Boolean,
        default: false
      },
      expand: {
        type: Boolean,
        default: false
      }
    },
    data() {
      return {
        isOpen: this.expand
      };
    },
    computed: {
      isFolder() {
        return this.children && this.children.length > 0;
      },
      totalSize() {
        let sum;
        if (this.size > 0) {
          sum = this.size;
        } else {
          const sizeList = this.sizeListWithItem({
            size: this.size,
            children: this.children
          });
          sum = sizeList.reduce((a, b) => a + b, 0);
        }
        return XBytes(sum, { iec: true, fixed: 1 });
      },
      icon() {
        if (this.isFolder) {
          return this.isOpen ? "folder-open" : "folder-close";
        }
        const ext = this.name.split(".").pop().toLowerCase();
        switch (true) {
          case Videos.indexOf(ext) > -1:
            return "video";
          case Images.indexOf(ext) > -1:
            return "image";
          case Archives.indexOf(ext) > -1:
            return "archive";
          case Audios.indexOf(ext) > -1:
            return "audio";
          case Documents.indexOf(ext) > -1:
            return "document";
          case Subtitles.indexOf(ext) > -1:
            return "subtitle";
          default:
            return "unknown";
        }
      }
    },
    methods: {
      toggle() {
        this.isOpen = !this.isOpen;
      },
      sizeListWithItem(item) {
        const children = item.children;
        if (!children || children.length <= 0) {
          return item.size ? [item.size] : [];
        }
        let list = [];
        for (let i = 0, len = children.length; i < len; i++) {
          let cList = this.sizeListWithItem(children[i]);
          list.push(...cList);
        }
        return list;
      }
    }
  };
  var __component__$1 = /* @__PURE__ */ normalizeComponent(
    _sfc_main$1,
    render$1,
    staticRenderFns$1,
    false,
    null,
    "3ab763b5"
  );
  const TreeItem = __component__$1.exports;
  var render = function render2() {
    var _vm = this, _c = _vm._self._c;
    return _c("ul", { staticStyle: { "padding": "0", "margin": "0", "list-style": "none" } }, _vm._l(_vm.folders, function(folder, index) {
      return _c("tree-item", _vm._b({ key: folder.key, attrs: { "is-last": index === _vm.folders.length - 1, "expand": true } }, "tree-item", folder, false));
    }), 1);
  };
  var staticRenderFns = [];
  const _sfc_main = {
    name: "TreeRoot",
    components: {
      TreeItem
    },
    props: {
      folders: {
        type: Array,
        default() {
          return [];
        }
      }
    }
  };
  var __component__ = /* @__PURE__ */ normalizeComponent(
    _sfc_main,
    render,
    staticRenderFns,
    false,
    null,
    null
  );
  const TreeRoot = __component__.exports;
  const TreeRootVM = Vue2.extend(TreeRoot);
  function folderTreeFromNodeList(fileNodeList) {
    const map = {};
    const list = [];
    for (let i = 0, len = fileNodeList.length; i < len; i++) {
      const fileNode = fileNodeList[i];
      const fileSizeNode = fileNode.querySelector(".bt_file_size");
      const nodeText = fileNode.innerText;
      let fileSizeStr;
      if (fileSizeNode) {
        fileSizeStr = fileSizeNode.innerText.trim();
      }
      let filePath;
      let fileBytes = 0;
      if (fileSizeStr) {
        const bytes = fileSizeStr.replace(/(\d+)bytes?/i, "$1");
        if (!isNaN(+bytes)) {
          fileBytes = +bytes;
        } else {
          fileBytes = XBytes.parseSize(fileSizeStr, { iec: false });
        }
        filePath = nodeText.substring(0, nodeText.indexOf(fileSizeStr)).trim();
      } else {
        filePath = nodeText.trim();
      }
      if (!filePath) {
        filePath = `No. ${i + 1} - Unknown filename`;
      }
      let slice = filePath.split("/");
      let parentKey = 0;
      for (let j = 0, sLen = slice.length; j < sLen; j++) {
        let fileName = slice[j];
        let level = j + 1;
        let baseName = filePath.substring(
          0,
          filePath.indexOf(fileName) + fileName.length
        );
        let key = hashCode(baseName);
        if (!map[key]) {
          let file = {
            key,
            parentKey,
            name: fileName,
            level,
            size: level === sLen ? fileBytes : 0,
            children: level === sLen ? null : []
          };
          map[key] = file;
          list.push(file);
        }
        parentKey = key;
      }
    }
    const root = [];
    for (let i = 0, len = list.length; i < len; i++) {
      let file = list[i];
      if (!file.parentKey) {
        root.push(file);
      } else {
        map[file.parentKey].children.push(file);
      }
    }
    return root;
  }
  function mountFileListElement(el, title) {
    const fileListNode = el.querySelector("ul");
    const fileItemNodeList = el.querySelectorAll("ul > li");
    if (!fileListNode || fileItemNodeList.length <= 0) {
      return;
    }
    const folders = folderTreeFromNodeList(fileItemNodeList);
    if (folders.length <= 0) {
      return;
    }
    const tree = new TreeRootVM({
      propsData: {
        folders: (folders.length > 1 || folders[0].size) && title ? [
          {
            key: 0,
            parentKey: -1,
            name: title,
            children: folders
          }
        ] : folders
      }
    });
    tree.$mount(fileListNode);
  }
  const ToastItemVM = Vue2.extend(ToastItem);
  const toast = new ToastItemVM().$mount();
  document.body.appendChild(toast.$el);
  Object.defineProperty(Vue2.prototype, "$toast", { value: toast });
  let topicListEl, fileListEl;
  if (topicListEl = document.querySelector("#topic_list")) {
    mountListElement(topicListEl);
    if (typeof jQuery !== "undefined" && typeof jQuery.tablesorter !== "undefined") {
      jQuery("#topic_list").tablesorter({ widgets: ["zebra"] });
      jQuery("#topic_list").bind("sortStart", function() {
        jQuery("#overlay").show();
      }).bind("sortEnd", function() {
        jQuery("#overlay").hide();
      });
    }
  }
  if (fileListEl = document.querySelector("#resource-tabs .file_list")) {
    let title = "";
    let titleEl;
    if (titleEl = document.querySelector(".topic-title h3")) {
      title = titleEl.innerText.trim();
    }
    mountFileListElement(fileListEl, title);
  }
})(Vue, xbytes);