Greasy Fork

Greasy Fork is available in English.

mTorrent (M-Team) PT 助手

用于基于 mTorrent PT 站 (M-Team) 的辅助脚本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name        mTorrent (M-Team) PT Helper
// @name:zh-CN  mTorrent (M-Team) PT 助手
// @namespace   http://greasyfork.icu/zh-CN/users/163820-ysc3839
// @description Helper script for mTorrent based PT sites (M-Team)
// @description:zh-CN 用于基于 mTorrent PT 站 (M-Team) 的辅助脚本
// @version     1.3.2
// @author      ysc3839
// @license     MIT
// @match       https://kp.m-team.cc/*
// @match       https://test2.m-team.cc/*
// @run-at      document-idle
// @grant       none
// ==/UserScript==
// This file is built from https://github.com/ysc3839/mTorrent-PT-Helper DO NOT EDIT.
// Icon svgs are from https://github.com/ant-design/ant-design-icons Licensed unser MIT LICENSE.

// jsx.ts
function E(tag, attr, ...nodes) {
  const ns = attr?.xmlns, e = tag ? ns ? document.createElementNS(ns, tag) : document.createElement(tag) : new DocumentFragment();
  if (attr) {
    delete attr.xmlns;
    for (const k in attr) {
      const v = attr[k];
      if (typeof v === "function") {
        e.addEventListener(k, v);
      } else {
        e.setAttribute(k, v);
      }
    }
  }
  e.append(...nodes.flat());
  return e;
}

// icons.tsx
var xmlns = "http://www.w3.org/2000/svg";
var Copy = () => E("span", { role: "img", "aria-label": "copy", class: "anticon anticon-copy" }, E("svg", { xmlns, viewBox: "64 64 896 896", focusable: "false", "data-icon": "copy", width: "1em", height: "1em", fill: "currentColor", "aria-hidden": "true" }, E("path", { xmlns, d: "M832 64H296c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h496v688c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8V96c0-17.7-14.3-32-32-32zM704 192H192c-17.7 0-32 14.3-32 32v530.7c0 8.5 3.4 16.6 9.4 22.6l173.3 173.3c2.2 2.2 4.7 4 7.4 5.5v1.9h4.2c3.5 1.3 7.2 2 11 2H704c17.7 0 32-14.3 32-32V224c0-17.7-14.3-32-32-32zM350 856.2L263.9 770H350v86.2zM664 888H414V746c0-22.1-17.9-40-40-40H232V264h432v624z" })));
var Loading = () => E("span", { role: "img", "aria-label": "loading", class: "anticon anticon-loading anticon-spin" }, E("svg", { xmlns, viewBox: "0 0 1024 1024", focusable: "false", "data-icon": "loading", width: "1em", height: "1em", fill: "currentColor", "aria-hidden": "true" }, E("path", { xmlns, d: "M988 548c-19.9 0-36-16.1-36-36 0-59.4-11.6-117-34.6-171.3a440.45 440.45 0 00-94.3-139.9 437.71 437.71 0 00-139.9-94.3C629 83.6 571.4 72 512 72c-19.9 0-36-16.1-36-36s16.1-36 36-36c69.1 0 136.2 13.5 199.3 40.3C772.3 66 827 103 874 150c47 47 83.9 101.8 109.7 162.7 26.7 63.1 40.2 130.2 40.2 199.3.1 19.9-16 36-35.9 36z" })));
var Check = () => E("span", { role: "img", "aria-label": "check", class: "anticon anticon-check" }, E("svg", { xmlns, viewBox: "64 64 896 896", focusable: "false", "data-icon": "check", width: "1em", height: "1em", fill: "currentColor", "aria-hidden": "true" }, E("path", { xmlns, d: "M912 190h-69.9c-9.8 0-19.1 4.5-25.1 12.2L404.7 724.5 207 474a32 32 0 00-25.1-12.2H112c-6.7 0-10.4 7.7-6.3 12.9l273.9 347c12.8 16.2 37.4 16.2 50.3 0l488.4-618.9c4.1-5.1.4-12.8-6.3-12.8z" })));
var ArrowUp = () => E("span", { role: "img", "aria-label": "arrow-up", class: "anticon anticon-arrow-up" }, E("svg", { xmlns, viewBox: "64 64 896 896", focusable: "false", "data-icon": "arrow-up", width: "1em", height: "1em", fill: "currentColor", "aria-hidden": "true" }, E("path", { xmlns, d: "M868 545.5L536.1 163a31.96 31.96 0 00-48.3 0L156 545.5a7.97 7.97 0 006 13.2h81c4.6 0 9-2 12.1-5.5L474 300.9V864c0 4.4 3.6 8 8 8h60c4.4 0 8-3.6 8-8V300.9l218.9 252.3c3 3.5 7.4 5.5 12.1 5.5h81c6.8 0 10.5-8 6-13.2z" })));
var ArrowDown = () => E("span", { role: "img", "aria-label": "arrow-down", class: "anticon anticon-arrow-down" }, E("svg", { xmlns, viewBox: "64 64 896 896", focusable: "false", "data-icon": "arrow-down", width: "1em", height: "1em", fill: "currentColor", "aria-hidden": "true" }, E("path", { xmlns, d: "M862 465.3h-81c-4.6 0-9 2-12.1 5.5L550 723.1V160c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v563.1L255.1 470.8c-3-3.5-7.4-5.5-12.1-5.5h-81c-6.8 0-10.5 8.1-6 13.2L487.9 861a31.96 31.96 0 0048.3 0L868 478.5c4.5-5.2.8-13.2-6-13.2z" })));

// content.ts
function addStyle(style) {
  const s = document.createElement("style");
  s.textContent = style;
  document.head.appendChild(s);
}
var specialClass;
function waitForContent() {
  const e = document.getElementsByClassName("mx-auto")[0];
  if (e)
    return Promise.resolve(e);
  return new Promise((resolve) => {
    const root = document.getElementById("root");
    new MutationObserver(function(records) {
      for (const r of records) {
        for (const n of r.addedNodes) {
          if (n.nodeType === Node.ELEMENT_NODE && n.id === "app-content") {
            this.disconnect();
            for (const c of n.classList) {
              if (c.startsWith("css-")) {
                specialClass = c;
                break;
              }
            }
            const e2 = n.getElementsByClassName("mx-auto")[0];
            resolve(e2);
            return;
          }
        }
      }
    }).observe(root, { childList: true });
  });
}

// api.ts
var apiUrls = (() => {
  let urls = [];
  try {
    urls = _APIHOSTS.map((u) => new URL(u));
  } catch (e) {
    console.warn("get _APIHOSTS error:", e);
  }
  urls.push(new URL(location.origin + "/api"));
  return urls;
})();
function isApiUrlWithPath(u, path) {
  for (const a of apiUrls) {
    if (u.origin === a.origin && u.pathname === a.pathname + path)
      return true;
  }
  return false;
}
function getApiUrl() {
  return localStorage.getItem("apiHost") || apiUrls[Math.random() * apiUrls.length | 0].href;
}
function getAuth() {
  return localStorage.getItem("auth");
}
function getVisitorId() {
  return localStorage.getItem("visitorId");
}
function getDid() {
  return localStorage.getItem("did");
}
function getApiFetchOptions() {
  return {
    method: "POST",
    headers: {
      authorization: getAuth(),
      visitorId: getVisitorId(),
      did: getDid(),
      ts: Math.floor(Date.now() / 1e3)
    }
  };
}
async function genDlToken(id) {
  const f = new FormData();
  f.set("id", id);
  const opts = getApiFetchOptions();
  opts.body = f;
  const res = await fetch(getApiUrl() + "/torrent/genDlToken", opts);
  const data = await res.json();
  if (data.code !== "0") {
    console.error("genDlToken API error:", data);
    return null;
  }
  return data.data;
}
async function getTorrentPeers(id) {
  const f = new FormData();
  f.set("id", id);
  const opts = getApiFetchOptions();
  opts.body = f;
  const res = await fetch(getApiUrl() + "/torrent/peers", opts);
  const data = await res.json();
  if (data.code !== "0") {
    console.error("getTorrentPeers API error:", data);
    return null;
  }
  return data.data;
}

// keystate.ts
var modifierState = false;
(() => {
  const onkey = (e) => {
    if (!e.repeat) {
      modifierState = e.altKey || e.ctrlKey || e.metaKey || e.shiftKey;
    }
  };
  document.addEventListener("keydown", onkey);
  document.addEventListener("keyup", onkey);
})();

// animation.ts
function waitAnimationFrame() {
  return new Promise((r) => requestAnimationFrame(r));
}
function waitAnimationEnd(e) {
  return new Promise((resolve) => {
    const onEnd = () => {
      e.removeEventListener("transitionend", onEnd, true);
      e.removeEventListener("animationend", onEnd, true);
      resolve();
    };
    e.addEventListener("transitionend", onEnd, true);
    e.addEventListener("animationend", onEnd, true);
  });
}
async function doAnimation(e, name, enter) {
  const origCls = e.className;
  const dir = enter ? "enter" : "leave";
  const animCls = origCls + ` ${name} ${name}-${dir} ${name}-${dir}-`;
  e.className = animCls + "prepare";
  await waitAnimationFrame();
  e.className = animCls + "start";
  await waitAnimationFrame();
  e.className = animCls + "active";
  await waitAnimationEnd(e);
  e.className = origCls;
}

// dropdown.tsx
addStyle(`:where(.my-ant-menu-override).ant-menu {
  --ant-menu-item-height: 1.5714285714285714; /* var(--ant-line-height); */
  box-sizing: border-box;
  position: fixed;
}
:where(.my-ant-menu-override).ant-menu-submenu-popup .ant-menu-vertical >.ant-menu-item {
  height: var(--ant-menu-item-height);
  line-height: var(--ant-menu-item-height);
}
:where(.my-ant-menu-override).ant-menu-submenu-popup .ant-menu-vertical .ant-menu-item {
  padding: 5px 12px; /* var(--ant-dropdown-padding-block) var(--ant-control-padding-horizontal); */
  margin: 0;
  width: auto;
}
:where(.my-ant-menu-override).ant-menu-submenu-popup .ant-menu-vertical.ant-menu-sub {
  min-width: auto;
  padding: 4px !important; /* var(--ant-padding-xxs) */
}
:where(.my-ant-menu-override).ant-menu .ant-menu-item-divider {
  height: 1px;
  margin-block: 4px !important; /* var(--ant-margin-xxs) */
}`);
function addDropdownMenu(target, items) {
  let dropdown;
  let state = 0 /* Hide */;
  const win = target.ownerDocument.defaultView;
  const clickCallback = (callback) => function(ev) {
    const r = callback.call(this, ev);
    if (r === false || !ev.defaultPrevented)
      hide();
    return r;
  };
  const create = () => {
    if (!dropdown) {
      dropdown = E(
        "div",
        {
          class: `ant-menu ant-menu-css-var ant-menu-light ant-menu-submenu ant-menu-submenu-hidden ant-menu-submenu-popup my-ant-menu-override ${specialClass}`
        },
        E("ul", { class: "ant-menu ant-menu-sub ant-menu-vertical", role: "menu" }, items.map(
          (i) => (
            /*i === null ?
            <li role="separator" class="ant-menu-item-divider"></li> :*/
            // separator unused
            E("li", { role: "menuitem", tabindex: "-1", class: "ant-menu-item ant-menu-item-only-child", click: clickCallback(i[1]) }, E("span", { class: "ant-menu-title-content" }, i[0]))
          )
        ))
      );
      document.body.appendChild(E("div", null, dropdown));
    }
  };
  const show = async () => {
    state = 3 /* Showing */;
    const { bottom, left } = target.getBoundingClientRect();
    const s = dropdown.style;
    s.top = bottom + 4 + "px";
    s.left = left + "px";
    dropdown.classList.remove("ant-menu-submenu-hidden");
    await doAnimation(dropdown, "ant-slide-up", true);
    state = 2 /* Show */;
    win?.addEventListener("mousedown", onTriggerClose, true);
    win?.addEventListener("contextmenu", onTriggerClose, true);
  };
  const hide = async () => {
    state = 1 /* Hiding */;
    win?.removeEventListener("mousedown", onTriggerClose, true);
    win?.removeEventListener("contextmenu", onTriggerClose, true);
    await doAnimation(dropdown, "ant-slide-up", false);
    dropdown.classList.add("ant-menu-submenu-hidden");
    state = 0 /* Hide */;
  };
  const onTriggerClose = (ev) => {
    if (state === 2 /* Show */ && !dropdown.contains(ev.target))
      hide();
  };
  target.addEventListener("contextmenu", (ev) => {
    ev.preventDefault();
    create();
    if (state === 0 /* Hide */) {
      show();
    } else if (state === 2 /* Show */) {
      hide();
    }
  });
}

// tooltip.tsx
addStyle(`:where(.my-ant-tooltip-override).ant-tooltip {
  box-sizing: border-box;
  top: -9999px;
  left: -9999px;
}

:where(.my-ant-tooltip-override).ant-tooltip .ant-tooltip-inner {
  color: #232222;
  background-color: #e7e1e0;
}`);
var Tooltip = class {
  container;
  tooltip;
  inner;
  constructor() {
    this.container = E("div", null, this.tooltip = E("div", { class: `ant-tooltip ant-tooltip-hidden ant-tooltip-placement-top my-ant-tooltip-override ${specialClass}`, style: "top: 100px; left: 100px;" }, E("div", { class: "ant-tooltip-content" }, this.inner = E("div", { class: "ant-tooltip-inner", role: "tooltip" }))));
    document.body.appendChild(this.container);
  }
  show(e) {
    this.tooltip.classList.remove("ant-tooltip-hidden");
    const { left, top } = e.getBoundingClientRect();
    const { scrollX, scrollY } = window;
    const s = this.tooltip.style;
    s.left = left + scrollX + "px";
    s.top = top + scrollY - this.tooltip.getBoundingClientRect().height - 4 + "px";
    doAnimation(this.tooltip, "ant-zoom-big-fast", true);
  }
  async destroy() {
    await doAnimation(this.tooltip, "ant-zoom-big-fast", false);
    this.tooltip.classList.add("ant-tooltip-hidden");
    this.container.remove();
    this.container = this.tooltip = this.inner = void 0;
  }
};

// torrents-table.tsx
var TorrentsTableManager = class {
  type;
  table;
  tbodyObserver;
  rowsCount;
  peersColIndex;
  linkColor;
  linkObserver;
  peersObserver;
  constructor() {
    this.tbodyObserver = new MutationObserver(this.onTbodyChange.bind(this));
    this.linkObserver = new MutationObserver(this.onLinkChange.bind(this));
    this.peersObserver = new MutationObserver(this.onPeersChange.bind(this));
  }
  set(table, type) {
    this.tbodyObserver.disconnect();
    this.linkObserver.disconnect();
    this.type = type;
    this.table = table;
    this.peersColIndex = void 0;
    if (table) {
      const theadr = table.querySelector("thead > tr"), tbody = table.getElementsByTagName("tbody")[0];
      if (theadr && tbody) {
        const c = theadr.children;
        this.rowsCount = c.length;
        if (this.type === 2 /* Rankings */) {
          for (let i = c.length - 1; i >= 0; --i) {
            const e = c[i].firstElementChild;
            if (e && e.getAttribute("color") === "red") {
              this.peersColIndex = i + 1;
              break;
            }
          }
          const colgroup = table.getElementsByTagName("colgroup")[0];
          colgroup?.prepend(E("col", { style: "width: 42px;" }));
        }
        this.modifyPlaceholderColspan(tbody);
        this.addListSelectHead(theadr, tbody);
        this.tbodyObserver.observe(tbody, { childList: true });
      }
    }
  }
  modifyPlaceholderColspan(tbody) {
    const td = tbody.querySelector("tr.ant-table-placeholder > td[colspan]");
    if (td)
      ++td.colSpan;
  }
  addListSelectHead(theadr, tbody) {
    let span, loading = false;
    const button = E(
      "button",
      {
        type: "button",
        class: `ant-btn ant-btn-default ant-btn-sm ant-btn-color-default ant-btn-variant-outlined ant-btn-icon-only ${specialClass}`,
        click: async function() {
          if (!loading) {
            const selected = tbody.getElementsByClassName("ant-table-row-selected");
            if (selected.length !== 0) {
              const setLoading = (l) => {
                loading = l;
                this.classList.toggle("ant-btn-loading", l);
                span.classList.toggle("ant-btn-loading-icon", l);
                span.replaceChildren(l ? Loading() : Check());
              };
              const tooltip = new Tooltip();
              let count = 0;
              const total = selected.length;
              const updateCount = () => {
                tooltip.inner.textContent = `${count}/${total}`;
              };
              setLoading(true);
              updateCount();
              tooltip.show(this);
              const urls = [];
              try {
                for (const tr of selected) {
                  const url = await genDlToken(tr.dataset.id);
                  urls.push(url);
                  ++count;
                  updateCount();
                }
              } catch (e) {
                console.error(e);
                tooltip.inner.textContent = "获取种子链接失败";
              }
              if (!urls.some((i) => !i)) {
                for (let i = 0; i < 2; ++i) {
                  try {
                    await navigator.clipboard.writeText(urls.join("\n"));
                    break;
                  } catch (e) {
                    alert("页面不在前台,复制失败,按确定再次尝试复制");
                    await new Promise((r) => setTimeout(r, 0));
                  }
                }
                setLoading(false);
                tooltip.destroy();
              }
            }
          }
        }
      },
      span = E("span", { class: "ant-btn-icon" }, Copy())
    );
    const select = (all) => {
      const inputs = tbody.getElementsByClassName("ant-checkbox-input");
      const event = new Event("change");
      for (const i of inputs) {
        i.checked = all || !i.checked;
        i.dispatchEvent(event);
      }
    };
    addDropdownMenu(button, [
      ["全选", select.bind(null, true)],
      ["反选", select.bind(null, false)]
    ]);
    let attr;
    if (this.type === 1 /* Torrents */) {
      attr = { class: "border border-solid border-black p-2", style: "width: 42px;" };
    } else if (this.type === 2 /* Rankings */) {
      attr = { class: "ant-table-cell", scope: "col", style: "text-align: center;" };
    }
    theadr.prepend(E("th", attr, button));
  }
  onTbodyChange(records) {
    for (const r of records) {
      for (const n of r.addedNodes) {
        if (n.tagName === "TR" && n.childElementCount === this.rowsCount && n.className !== "ant-table-placeholder") {
          const a = this.addListSelect(n);
          this.setPeersLinkAndColor(n, a);
          this.linkObserver.observe(a, { attributes: true, attributeFilter: ["href"] });
          this.replaceDownloadIcon(n);
        }
      }
    }
  }
  onLinkChange(records) {
    for (const r of records) {
      r.target?.__my_update_id?.();
    }
  }
  addListSelect(tr) {
    const origClass = tr.className;
    tr.style.transition = "background-color .2s";
    const a = tr.querySelector('a[href^="/detail/"]');
    if (a) {
      a.__my_dataset_tr = tr;
      a.__my_update_id = function() {
        const id = this.getAttribute("href").slice(8);
        this.__my_dataset_tr.dataset.id = id;
        const links = this.__my_peers_links;
        if (links) {
          const href = `/detail/${id}#peers`;
          for (const l of links) {
            l.href = href;
          }
        }
      };
      a.__my_update_id();
    }
    let label, span, attr;
    if (this.type === 1 /* Torrents */) {
      attr = { class: "border border-solid border-black p-2 ", align: "center" };
    } else if (this.type === 2 /* Rankings */) {
      attr = { class: "ant-table-cell", style: "text-align: center;" };
    }
    tr.prepend(E(
      "td",
      attr,
      label = E("label", { class: `ant-checkbox-wrapper ${specialClass}`, style: "transform: scale(1.5);" }, span = E("span", { class: `ant-checkbox ${specialClass}` }, E("input", { class: "ant-checkbox-input", type: "checkbox", change: function() {
        const c = this.checked;
        span.classList.toggle("ant-checkbox-checked", c);
        label.classList.toggle("ant-checkbox-wrapper-checked", c);
        if (c) {
          const l = tr.classList;
          l.remove("bg-sticky_top", "bg-sticky_normal");
          l.add("bg-black/10", "ant-table-row-selected");
        } else {
          tr.className = origClass;
        }
      } }), E("span", { class: "ant-checkbox-inner" })))
    ));
    return a;
  }
  onPeersChange(records) {
    const seedersSet = /* @__PURE__ */ new Set();
    for (const r of records) {
      let e = r.target;
      if (e.nodeType === Node.TEXT_NODE) {
        e = e.parentElement;
      }
      const s = e.__my_linked_seeders;
      if (s)
        e = s;
      seedersSet.add(e);
    }
    for (const s of seedersSet) {
      s.__my_update_color();
    }
  }
  setPeersLinkAndColor(n, a) {
    const [seedersTd, leechersTd] = this.getPeersTd(n);
    if (seedersTd && leechersTd) {
      const seeders = seedersTd.lastElementChild, leechers = leechersTd.lastElementChild;
      if (seeders && leechers) {
        leechers.__my_linked_seeders = seeders;
        seeders.__my_linked_leechers = leechers;
        seeders.__my_update_color = function() {
          const seedersCount = parseInt(this.textContent, 10), leechersCount = parseInt(this.__my_linked_leechers.textContent, 10);
          if (leechersCount && !Number.isNaN(seedersCount)) {
            const ratio = seedersCount / leechersCount;
            let color = "";
            if (ratio < 0.025)
              color = "#ff0000";
            else if (ratio < 0.05)
              color = "#ee0000";
            else if (ratio < 0.075)
              color = "#dd0000";
            else if (ratio < 0.1)
              color = "#cc0000";
            else if (ratio < 0.125)
              color = "#bb0000";
            else if (ratio < 0.15)
              color = "#aa0000";
            else if (ratio < 0.175)
              color = "#990000";
            else if (ratio < 0.2)
              color = "#880000";
            else if (ratio < 0.225)
              color = "#770000";
            else if (ratio < 0.25)
              color = "#660000";
            else if (ratio < 0.275)
              color = "#550000";
            else if (ratio < 0.3)
              color = "#440000";
            else if (ratio < 0.325)
              color = "#330000";
            else if (ratio < 0.35)
              color = "#220000";
            else if (ratio < 0.375)
              color = "#110000";
            this.style.color = color;
          }
        };
        seeders.__my_update_color();
        this.peersObserver.observe(seeders, { subtree: true, characterData: true });
        this.peersObserver.observe(leechers, { subtree: true, characterData: true });
        const id = n.dataset.id;
        a.__my_peers_links = [
          this.addPeersLink(seedersTd, id, seeders, leechers),
          this.addPeersLink(leechersTd, id, seeders, leechers)
        ];
      }
    }
  }
  getPeersTd(n) {
    if (!this.peersColIndex && this.type === 1 /* Torrents */) {
      const c = n.children;
      for (let i = c.length - 1; i >= 0; --i) {
        if (c[i].getElementsByClassName("anticon-arrow-down").length !== 0) {
          this.peersColIndex = i;
          break;
        }
      }
    }
    if (!this.peersColIndex)
      return [void 0, void 0];
    const seeders = n.children[this.peersColIndex - 1], leechers = n.children[this.peersColIndex];
    return [seeders, leechers];
  }
  addPeersLink(p, id, seeders, leechers) {
    const c = Array.from(p.children);
    for (const i of c) {
      i.remove();
    }
    const a = E("a", { href: `/detail/${id}#peers`, target: "_blank" }, c);
    if (seeders && leechers) {
      let loading = false, loadingIcon;
      a.addEventListener("click", async function(e) {
        if (!modifierState) {
          e.preventDefault();
          if (!loading) {
            loading = true;
            if (!loadingIcon) {
              loadingIcon = Loading();
              this.prepend(loadingIcon);
            }
            const peers = await getTorrentPeers(id);
            if (peers) {
              let s = 0, l = 0;
              for (const p2 of peers) {
                if (p2.left === "0") {
                  ++s;
                } else {
                  ++l;
                }
              }
              seeders.textContent = s;
              leechers.textContent = l;
              loadingIcon.remove();
              loadingIcon = null;
            }
            loading = false;
          }
        }
      });
    }
    if (this.linkColor) {
      a.style.color = this.linkColor;
      p.append(a);
    } else {
      p.append(a);
      a.style.color = this.linkColor = a.computedStyleMap().get("color");
    }
    return a;
  }
  replaceDownloadIcon(n) {
    const i = n.getElementsByClassName("anticon-download")[0];
    i?.replaceWith(Copy());
  }
};
var torrentsTable = new TorrentsTableManager();
function findAndSetTable(n, type) {
  if (n) {
    const e = n.querySelector("table");
    if (e) {
      torrentsTable.set(e, type);
      return true;
    }
  } else {
    torrentsTable.set(void 0, void 0);
  }
}
function handleTorrentsTable(records, type) {
  for (const r of records) {
    for (const n of r.addedNodes) {
      if (n.nodeType === Node.ELEMENT_NODE) {
        if (findAndSetTable(n, type))
          return;
      }
    }
  }
}

// xhr-hook.ts
var xhr = XMLHttpRequest;
var xhrPrototypeProxy = new Proxy(xhr.prototype, {
  get(target, prop, receiver) {
    const v = Reflect.get(...arguments);
    if (prop === "responseText") {
      const u = new URL(receiver.responseURL);
      if (isApiUrlWithPath(u, "/torrent/peers")) {
        XMLHttpRequest = xhr;
        const data = JSON.parse(v);
        if (data.code === "0") {
          data.data.sort((a, b) => {
            return parseInt(a.left, 10) - parseInt(b.left, 10);
          });
          return JSON.stringify(data);
        }
      }
    }
    return v;
  }
});
var xhrProxy = new Proxy(xhr, {
  construct() {
    const o = Reflect.construct(...arguments);
    Object.setPrototypeOf(o, xhrPrototypeProxy);
    return o;
  }
});
function hookXHR(enable) {
  XMLHttpRequest = enable ? xhrProxy : xhr;
}

// detail-page.ts
var peersTr;
function handlePeersTr(trs, storeTr) {
  for (let i = trs.length - 1; i >= 0; --i) {
    const tr = trs[i], labelText = tr.getElementsByClassName("ant-descriptions-item-label")[0]?.textContent;
    if (labelText === "同伴" || labelText === "Peers") {
      if (storeTr)
        peersTr = tr;
      return tr.getElementsByTagName("button")[0];
    }
  }
}
function clickButton(button) {
  hookXHR(true);
  button.click();
}
function hookButtonClick(button) {
  button.onclick = function() {
    this.onclick = null;
    hookXHR(true);
  };
}
function scrollToPeers() {
  if (peersTr) {
    peersTr.scrollIntoView({ behavior: "smooth" });
    peersTr = null;
  }
}
function handleDetailPage(e) {
  e = e.firstElementChild;
  if (e?.classList.contains("detail-view")) {
    const showPeers = location.hash === "#peers";
    const tbody = e.querySelector(".ant-descriptions-view > table > tbody");
    if (tbody) {
      const button = handlePeersTr(tbody.children, showPeers);
      if (button) {
        if (showPeers) {
          clickButton(button);
          setTimeout(scrollToPeers, 1e3);
        } else {
          hookButtonClick(button);
        }
      }
      let first = true;
      const o = new MutationObserver(function(records) {
        if (first) {
          first = false;
          setTimeout(() => {
            this.disconnect();
            scrollToPeers();
          }, 1e3);
        }
        const button2 = handlePeersTr(tbody.children, showPeers);
        if (button2 && !showPeers) {
          hookButtonClick(button2);
        }
      });
      setTimeout(o.disconnect.bind(o), 1e4);
      o.observe(tbody, { childList: true });
    }
  }
}

// intercept-dl.ts
navigation.addEventListener("navigate", (e) => {
  console.log("navigate", e);
  if (!modifierState && e.cancelable && !e.hashChange && e.navigationType === "push") {
    const url = e.destination.url, u = new URL(url);
    if (isApiUrlWithPath(u, "/rss/dl") || isApiUrlWithPath(u, "/rss/dlv2")) {
      e.preventDefault();
      navigator.clipboard.writeText(url).then(() => {
        alert("复制成功");
      });
    }
  }
});

// upload-page.tsx
var dragging = false;
var dragOverlay;
function onDragEnter() {
  if (!dragging) {
    dragging = true;
    if (!dragOverlay) {
      dragOverlay = E("div", { style: `position: fixed; top: 0; left: 0; z-index: 100; width: 100%; height: 100%; color: white; background: #00000080; user-select: none; justify-content: center; align-items: center; display: none;`, dragleave: function() {
        dragging = false;
        this.style.display = "none";
      }, dragover: function(e) {
        e.preventDefault();
      }, drop: function(e) {
        e.preventDefault();
        dragging = false;
        this.style.display = "none";
        const items = e.dataTransfer?.items;
        if (items) {
          let f;
          for (const i of items) {
            const entry = i.webkitGetAsEntry();
            if (entry?.isFile) {
              f = i.getAsFile();
              break;
            }
          }
          if (f) {
            const torrentInput = document.getElementById("torrent-input");
            if (torrentInput) {
              const dt = new DataTransfer();
              dt.items.add(f);
              torrentInput.files = dt.files;
              torrentInput.dispatchEvent(new Event("change", { bubbles: true }));
            }
          }
        }
      } }, E("div", { style: "pointer-events: none; background: #000a; border-radius: 10px; padding: 10px;" }, E("h1", { style: "margin: 0;" }, "放下以选择该文件")));
      document.body.append(dragOverlay);
    }
    dragOverlay.style.display = "flex";
  }
}
function enterUploadPage() {
  addEventListener("dragenter", onDragEnter);
}
function exitUploadPage() {
  removeEventListener("dragenter", onDragEnter);
}

// main.user.tsx
addStyle(`#app-content {
  overflow: unset !important;
}

@media screen and (max-width: 1380px) {
  #app-content > div { width: 100% !important; }
  .mx-auto { margin: 0 !important; }
}

.braft-output-content {
  max-height: none !important;
  overflow-y: auto !important;
}

.ant-image {
  background: unset !important;
}
.ant-image .ant-image-img-placeholder {
  background: unset !important;
}`);
function replaceUpDownIcon() {
  const up = document.querySelector("img.arrowup"), down = document.querySelector("img.arrowdown");
  if (up && down) {
    const upIcon = ArrowUp(), downIcon = ArrowDown();
    upIcon.classList.add("text-mt-dark-green");
    downIcon.classList.add("text-mt-dark-red");
    up.replaceWith(upIcon);
    down.replaceWith(downIcon);
  }
}
function getPageType() {
  return location.pathname.split("/")[1];
}
waitForContent().then((e) => {
  replaceUpDownIcon();
  const page = getPageType();
  if (page === "upload") {
    enterUploadPage();
  } else if (page === "browse") {
    findAndSetTable(e, 1 /* Torrents */);
  } else if (page === "rankings") {
    findAndSetTable(e, 2 /* Rankings */);
  }
  new MutationObserver(function(records) {
    const page2 = getPageType();
    if (page2 === "upload") {
      enterUploadPage();
    } else {
      exitUploadPage();
      if (page2 === "browse") {
        handleTorrentsTable(records, 1 /* Torrents */);
      } else if (page2 === "rankings") {
        handleTorrentsTable(records, 2 /* Rankings */);
      } else {
        findAndSetTable();
        if (page2 === "detail") {
          handleDetailPage(e);
        }
      }
    }
  }).observe(e, { childList: true });
});