(() => {
  if (window.__MMW_DOUYIN_SOURCE_BRIDGE__) return;
  window.__MMW_DOUYIN_SOURCE_BRIDGE__ = true;

  const host = location.hostname || "";
  if (!/(^|\.)douyin\.com$/i.test(host) && !/(^|\.)iesdouyin\.com$/i.test(host)) return;

  const BRIDGE_ITEM_CLASS = "mmw-dy-bridge-panel-item";
  const CURRENT_ITEM_CLASS = "mmw-dy-current-video-aix-item";
  const ITEM_CLASS = "mmw-dy-source-trace-aix-item";
  const TITLE_ITEM_CLASS = "mmw-dy-video-title-aix-item";
  const FALLBACK_WRAPPER_CLASS = "mmw-source-fallback-wrapper";
  const FALLBACK_PANEL_CLASS = "mmw-source-fallback-panel";
  const STYLE_ID = "mmw-dy-source-trace-aix-style";
  const PROFILE_CARD_LINK_SELECTOR = 'a[href*="/video/"], a[href*="/note/"]';
  const CONTEXT_TTL_MS = 12000;
  const TRACE_TIMEOUT_MS = 65000;
  const DOWNLOAD_TIMEOUT_MS = 24000;
  const CURRENT_RESOURCE_WAIT_MS = 6500;
  const CURRENT_RESOURCE_POLL_MS = 450;
  const REMIND_STORAGE_KEY = "mmwSourceVideoCostConfirmHidden";
  const AUTH_STORAGE_KEY = "pluginAuth";
  const TEXT = {
    label: "\u4e0b\u8f7d\u6e90\u89c6\u9891",
    titleLabel: "\u4e0b\u8f7d\u6807\u9898",
    titleDownloaded: "\u6807\u9898\u5df2\u4e0b\u8f7d",
    titleFailed: "\u6807\u9898\u4e0b\u8f7d\u5931\u8d25",
    sourceTip: "\u6ce8\uff1a\u63d0\u4ea4\u89c6\u9891\u4fe1\u606f\u5230\u6e20\u9053\uff0c\u4ece\u6e90\u5934\u83b7\u53d6\u9ad8\u6e05\u65e0\u6c34\u5370\u539f\u89c6\u9891\u8d44\u6e90\u3002\u82e5\u662f\u6e90\u5934\u7684\u7d20\u6750\u672c\u8eab\u5c31\u6709\u6c34\u5370\uff0c\u83b7\u53d6\u5230\u7684\u8d44\u6e90\u4e5f\u540c\u6837\u6709\uff0c\u8fd9\u79cd\u60c5\u51b5\u4e00\u822c\u51fa\u73b0\u5728\u6df7\u526a\u7d20\u6750\u4e0a\u9762\uff0c\u975e\u6df7\u526a\u89c6\u9891\u57fa\u672c\u4e0d\u4f1a\u6709\u95ee\u9898\uff0c\u8bf7\u6089\u77e5\uff08\u6240\u4ee5\u4e0d\u662f\u6e05\u7406\u6c34\u5370\u7684\u903b\u8f91\uff09\u3002",
    normalLabel: "\u4e0b\u8f7d\u666e\u901a\u89c6\u9891",
    currentLabel: "\u4e0b\u8f7d\u5f53\u524d\u89c6\u9891",
    currentDetail: "\u5f53\u524d\u64ad\u653e\u6e90",
    shortLabel: "\u6e90\u89c6\u9891",
    currentShortLabel: "\u5f53\u524d\u89c6\u9891",
    tracing: "\u6b63\u5728\u6eaf\u6e90...",
    submitted: "\u5df2\u63d0\u4ea4\u4e0b\u8f7d",
    failed: "\u4e0b\u8f7d\u5931\u8d25",
    traceFailed: "\u6eaf\u6e90\u5931\u8d25",
    noTraceUrl: "\u6ca1\u6709\u627e\u5230\u53ef\u7528\u7684\u6296\u97f3\u89c6\u9891\u94fe\u63a5",
    noSourceUrl: "\u6eaf\u6e90\u7ed3\u679c\u6ca1\u6709\u8fd4\u56de\u6e90\u89c6\u9891\u4e0b\u8f7d\u5730\u5740",
    noCurrentUrl: "\u6ca1\u6709\u627e\u5230\u5f53\u524d\u64ad\u653e\u89c6\u9891",
    dontRemind: "\u4e0d\u518d\u63d0\u9192",
    cancel: "\u53d6\u6d88",
    continueFetch: "\u5f00\u59cb\u6eaf\u6e90"
  };

  let activeContext = null;
  let activeContextExpiresAt = 0;
  let injectTimer = 0;
  let confirmDialogPromise = null;
  let sourceAccess = false;
  let sourceTraceAccess = false;
  let sourceAuth = null;
  const sourceDownloadRetries = new Map();

  injectStyle();
  observePanels();
  refreshSourceAccess().then(queuePanelInjection).catch(() => {});
  window.addEventListener("resize", queuePanelInjection, true);
  window.addEventListener("scroll", queuePanelInjection, true);

  chrome.storage?.onChanged?.addListener((changes, areaName) => {
    if (areaName !== "local" || !changes[AUTH_STORAGE_KEY]) return;
    sourceAuth = changes[AUTH_STORAGE_KEY].newValue;
    sourceAccess = hasSourceAccess(sourceAuth);
    sourceTraceAccess = hasSourceTraceAccess(sourceAuth);
    if (!sourceAccess) {
      removeSourceItems();
      removeTitleItems();
    } else if (!sourceTraceAccess) {
      removeSourceItems();
    }
    queuePanelInjection();
  });
  chrome.runtime?.onMessage?.addListener((message) => {
    if (message?.type !== "VD_SAFE_VIDEO_RESULT" || message.ok === true) return false;
    showToast(`${TEXT.failed}：${message.error || TEXT.failed}`, "error");
    return false;
  });

  document.addEventListener("click", (event) => {
    const button = event.target?.closest?.(".dy-download-button");
    if (!button) return;
    const context = buildTraceContext(button);
    if (!context?.url) return;
    activeContext = context;
    activeContextExpiresAt = Date.now() + CONTEXT_TTL_MS;
    queuePanelInjection();
    setTimeout(queuePanelInjection, 80);
  }, true);
  document.addEventListener("click", (event) => {
    const target = event.target;
    if (!(target instanceof Element)) return;
    if (target.closest(".dy-download-panel, .dy-download-wrapper, .mmw-source-confirm-mask, .mmw-source-results-popover")) return;
    closeOpenDownloadPanels();
  }, true);
  document.addEventListener("keydown", (event) => {
    if (event.key === "Escape") closeOpenDownloadPanels();
  }, true);

  function observePanels() {
    const start = () => {
      if (!document.body) {
        setTimeout(start, 100);
        return;
      }
      new MutationObserver(queuePanelInjection).observe(document.body, { childList: true, subtree: true });
    };
    start();
  }

  function queuePanelInjection() {
    if (injectTimer) return;
    injectTimer = setTimeout(() => {
      injectTimer = 0;
      injectOpenPanels();
      renderFallbackButton();
    }, 16);
  }

  function injectOpenPanels() {
    if (!sourceAccess) {
      removeSourceItems();
      removeCurrentItems();
      removeTitleItems();
      return;
    }
    const context = getActiveContext();
    if (!context) {
      removeCurrentItems();
      removeTitleItems();
      return;
    }
    for (const panel of document.querySelectorAll(".dy-download-panel")) {
      syncStandaloneCurrentItem(panel, context);
      injectTitleItem(panel, context);
      if (sourceTraceAccess) injectSourceItem(panel, context, { append: true });
      else removeSourceItems(panel);
    }
  }

  function renderFallbackButton() {
    if (!sourceAccess || !isStandaloneDetailPage()) {
      removeFallbackButton();
      return;
    }
    if (hasVisibleAixButton()) {
      removeFallbackButton();
      return;
    }
    const context = buildStandaloneTraceContext();
    if (!context?.url) {
      removeFallbackButton();
      return;
    }
    let wrapper = document.querySelector(`.${FALLBACK_WRAPPER_CLASS}`);
    if (!wrapper) {
      wrapper = document.createElement("div");
      wrapper.className = `dy-download-wrapper ${FALLBACK_WRAPPER_CLASS}`;
      const button = document.createElement("button");
      button.type = "button";
      button.className = "dy-download-button";
      button.title = TEXT.label;
      button.innerHTML = '<div class="dy-download-button-inner">' + downloadIcon() + "</div>";
      button.addEventListener("click", (event) => {
        event.preventDefault();
        event.stopPropagation();
        openFallbackPanel(button, buildStandaloneTraceContext());
      });
      wrapper.appendChild(button);
      document.body?.appendChild(wrapper);
    }
    const button = wrapper.querySelector(".dy-download-button");
    if (button) button.title = TEXT.label;
    wrapper.dataset.videoId = context.awemeId || "";
    wrapper.dataset.traceUrl = context.url;
    positionFallbackButton(wrapper);
  }

  function removeFallbackButton() {
    document.querySelector(`.${FALLBACK_WRAPPER_CLASS}`)?.remove();
    closeFallbackPanels();
  }

  function hasVisibleAixButton() {
    for (const button of document.querySelectorAll(`.dy-download-wrapper:not(.${FALLBACK_WRAPPER_CLASS}) .dy-download-button`)) {
      if (isVisibleElement(button)) return true;
    }
    return false;
  }

  function openFallbackPanel(button, context) {
    if (!context?.url) {
      showToast(TEXT.noTraceUrl, "error");
      return;
    }
    closeFallbackPanels();
    const panel = document.createElement("div");
    panel.className = `dy-download-panel ${FALLBACK_PANEL_CLASS}`;
    panel.style.cssText = [
      "position: fixed",
      "background: #fff",
      "border-radius: 8px",
      "box-shadow: 0 4px 12px rgba(0,0,0,0.15)",
      "padding: 4px 0",
      "min-width: 220px",
      "max-width: 280px",
      "z-index: 10000",
      "font-size: 14px",
      "color: #333",
      'font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif'
    ].join(";");
    syncStandaloneCurrentItem(panel, context, { force: true });
    injectTitleItem(panel, context);
    if (sourceTraceAccess) injectSourceItem(panel, context, { append: true });
    document.body?.appendChild(panel);
    positionFallbackPanel(panel, button);
  }

  function closeFallbackPanels() {
    for (const panel of document.querySelectorAll(`.${FALLBACK_PANEL_CLASS}`)) panel.remove();
  }

  function closeOpenDownloadPanels() {
    for (const panel of document.querySelectorAll(".dy-download-panel")) panel.remove();
  }

  function getActiveContext() {
    if (!activeContext || Date.now() > activeContextExpiresAt) return null;
    return activeContext;
  }

  function injectSourceItem(panel, context, options = {}) {
    if (!(panel instanceof HTMLElement)) return;
    panel.style.overflow = "visible";
    const existing = panel.querySelector(`.${ITEM_CLASS}`);
    if (existing?.dataset.contextKey === context.key) return;
    existing?.remove();

    const item = document.createElement("div");
    item.className = `dy-download-panel-item ${BRIDGE_ITEM_CLASS} ${ITEM_CLASS}`;
    item.dataset.contextKey = context.key;
    item.innerHTML = `
      <div class="mmw-source-row">
        <div class="mmw-source-main">
          <span class="mmw-source-icon">${sourceIcon()}</span>
          <span class="mmw-source-label">${TEXT.label}</span>
        </div>
        <span class="mmw-source-status">${downloadIcon()}</span>
      </div>
      <div class="mmw-source-tip" role="tooltip">${TEXT.sourceTip}</div>
    `;

    const updateTip = () => updateSourceTipPlacement(item);
    item.addEventListener("mouseenter", updateTip);
    item.addEventListener("focusin", updateTip);
    setTimeout(updateTip, 0);

    item.addEventListener("click", async (event) => {
      event.preventDefault();
      event.stopPropagation();
      await handleSourceItemClick(item, context);
    });

    if (options.append) panel.appendChild(item);
    else panel.insertBefore(item, panel.firstChild);
  }

  function injectTitleItem(panel, context) {
    if (!(panel instanceof HTMLElement)) return;
    const title = sanitizeVideoTitle(context?.videoTitle);
    const existing = panel.querySelector(`.${TITLE_ITEM_CLASS}`);
    if (!title) {
      existing?.remove();
      return;
    }
    const contextKey = `${context.key}|${title}`;
    if (existing?.dataset.contextKey === contextKey) return;
    existing?.remove();

    const item = document.createElement("div");
    item.className = `dy-download-panel-item ${BRIDGE_ITEM_CLASS} ${TITLE_ITEM_CLASS}`;
    item.dataset.contextKey = contextKey;
    item.title = title;
    item.innerHTML = `
      <div class="mmw-source-row">
        <div class="mmw-source-main">
          <span class="mmw-source-icon">${titleIcon()}</span>
          <span class="mmw-source-label">${TEXT.titleLabel}</span>
        </div>
        <span class="mmw-source-status">${downloadIcon()}</span>
      </div>
    `;
    item.addEventListener("click", async (event) => {
      event.preventDefault();
      event.stopPropagation();
      if (item.dataset.busy === "true") return;
      item.dataset.busy = "true";
      setStatus(item, loadingIcon(), TEXT.titleLabel);
      try {
        const response = await sendMessageWithTimeout({
          type: "DOWNLOAD_VIDEO_TITLE",
          title,
          awemeId: context.awemeId || "",
          sourceTraceUrl: context.url || ""
        }, DOWNLOAD_TIMEOUT_MS, TEXT.titleFailed);
        if (!response?.ok) throw new Error(readResponseError(response) || TEXT.titleFailed);
        setStatus(item, successIcon(), TEXT.titleDownloaded);
        showToast(TEXT.titleDownloaded, "success");
        setTimeout(() => document.querySelector(".dy-download-panel")?.remove(), 500);
      } catch (error) {
        setStatus(item, errorIcon(), TEXT.titleFailed);
        showToast(error.message || TEXT.titleFailed, "error");
      } finally {
        setTimeout(() => {
          item.dataset.busy = "false";
          setStatus(item, downloadIcon(), title);
        }, 1800);
      }
    });
    const sourceItem = panel.querySelector(`.${ITEM_CLASS}`);
    if (sourceItem) panel.insertBefore(item, sourceItem);
    else panel.appendChild(item);
  }

  function updateSourceTipPlacement(item) {
    if (!(item instanceof HTMLElement)) return;
    const tip = item.querySelector(".mmw-source-tip");
    if (!(tip instanceof HTMLElement)) return;
    const rect = item.getBoundingClientRect();
    const viewportWidth = window.innerWidth || document.documentElement.clientWidth || 0;
    const viewportHeight = window.innerHeight || document.documentElement.clientHeight || 0;
    const gap = 10;
    const margin = 12;
    const tipWidth = Math.min(304, Math.max(180, tip.offsetWidth || 304));
    const tipHeight = Math.max(80, tip.offsetHeight || 140);
    const rightSpace = viewportWidth - rect.right - gap - margin;
    const leftSpace = rect.left - gap - margin;
    const side = rightSpace >= tipWidth || rightSpace >= leftSpace ? "right" : "left";
    const availableWidth = Math.max(160, Math.min(304, side === "right" ? rightSpace : leftSpace));
    const centerY = rect.top + rect.height / 2;
    let align = "middle";
    if (centerY - tipHeight / 2 < margin) align = "top";
    if (centerY + tipHeight / 2 > viewportHeight - margin) align = "bottom";
    item.dataset.tipSide = side;
    item.dataset.tipAlign = align;
    tip.style.width = `${Math.round(availableWidth)}px`;
  }

  function injectCurrentItem(panel, candidate, context = null) {
    if (!(panel instanceof HTMLElement) || !candidate) return;
    const candidateKey = `${candidate.id || context?.key || "current"}|${candidate.url || "pending"}`;
    const existing = panel.querySelector(`.${CURRENT_ITEM_CLASS}`);
    if (existing?.dataset.candidateKey === candidateKey) return;
    existing?.remove();
    const label = candidate.menuLabel || TEXT.currentLabel;
    const item = document.createElement("div");
    item.className = `dy-download-panel-item ${BRIDGE_ITEM_CLASS} ${CURRENT_ITEM_CLASS}`;
    item.dataset.candidateKey = candidateKey;
    item.innerHTML = `
      <div class="mmw-source-row">
        <div class="mmw-source-main">
          <span class="mmw-source-icon">${videoIcon()}</span>
          <span class="mmw-source-label">${label}</span>
        </div>
        <span class="mmw-source-status">${downloadIcon()}</span>
      </div>
    `;

    item.addEventListener("click", async (event) => {
      event.preventDefault();
      event.stopPropagation();
      await handleCurrentItemClick(item, candidate, context);
    });

    const sourceItem = panel.querySelector(`.${ITEM_CLASS}`);
    if (sourceItem) panel.insertBefore(item, sourceItem);
    else panel.appendChild(item);
  }

  function syncStandaloneCurrentItem(panel, context, options = {}) {
    if (!(panel instanceof HTMLElement)) return;
    const shouldOffer = Boolean(options.force) || shouldOfferStandaloneCurrentVideo(panel);
    const candidate = shouldOffer ? createStandaloneCurrentCandidate(context) : null;
    if (candidate) injectCurrentItem(panel, candidate, context);
    else removeCurrentItems(panel);
  }

  function shouldOfferStandaloneCurrentVideo(panel) {
    return isStandaloneDetailPage() && !hasPanelVideoRows(panel);
  }

  function hasPanelVideoRows(panel) {
    if (!(panel instanceof HTMLElement)) return false;
    for (const item of panel.querySelectorAll(".dy-download-panel-item")) {
      if (item.classList.contains(BRIDGE_ITEM_CLASS)) continue;
      const text = String(item.textContent || "").toLowerCase();
      if (/\b(mp4|webm|mov|m4v|m3u8|1080p|720p|576p|480p)\b/.test(text)) return true;
    }
    return false;
  }

  async function refreshSourceAccess() {
    const response = await sendMessageWithTimeout(
      { type: "PLUGIN_AUTH_STATUS", refresh: true },
      8000,
      ""
    ).catch(() => null);
    if (response?.ok && response.auth) {
      sourceAuth = response.auth;
    } else {
      const values = await chrome.storage.local.get([AUTH_STORAGE_KEY]).catch(() => ({}));
      sourceAuth = values?.[AUTH_STORAGE_KEY] || null;
    }
    sourceAccess = hasSourceAccess(sourceAuth);
    sourceTraceAccess = hasSourceTraceAccess(sourceAuth);
  }

  function hasSourceAccess(auth) {
    return Boolean(auth && typeof auth === "object" && auth.loggedIn);
  }

  function hasSourceTraceAccess(auth) {
    return globalThis.MMWPluginFeatureAccess?.isVisible?.(
      auth,
      globalThis.MMWPluginFeatureAccess.SOURCE_TRACE
    ) === true;
  }

  function removeSourceItems(root = document) {
    if (!root?.querySelectorAll) return;
    for (const item of root.querySelectorAll(`.${ITEM_CLASS}`)) item.remove();
  }

  function findSourceDownloadPanel(contextKey) {
    for (const panel of document.querySelectorAll(".dy-download-panel")) {
      const sourceItem = panel.querySelector(`.${ITEM_CLASS}`);
      if (sourceItem?.dataset.contextKey === contextKey) return panel;
    }
    return null;
  }

  function removeCurrentItems(root = document) {
    if (!root?.querySelectorAll) return;
    for (const item of root.querySelectorAll(`.${CURRENT_ITEM_CLASS}`)) item.remove();
  }

  function removeTitleItems(root = document) {
    if (!root?.querySelectorAll) return;
    for (const item of root.querySelectorAll(`.${TITLE_ITEM_CLASS}`)) item.remove();
  }

  async function handleCurrentItemClick(item, candidate, context = null) {
    if (item.dataset.busy === "true") return;
    item.dataset.busy = "true";
    setStatus(item, loadingIcon(), TEXT.currentLabel);
    try {
      const resolvedCandidate = await resolveCurrentVideoCandidate(candidate, context);
      if (!resolvedCandidate?.url) throw new Error(TEXT.noCurrentUrl);
      const downloadResponse = await downloadCurrentVideoCandidate(resolvedCandidate);
      if (!downloadResponse?.ok) throw new Error(readResponseError(downloadResponse) || TEXT.failed);
      setStatus(item, successIcon(), TEXT.submitted);
      showToast(TEXT.submitted, "success");
      setTimeout(() => document.querySelector(".dy-download-panel")?.remove(), 500);
    } catch (error) {
      setStatus(item, errorIcon(), TEXT.failed);
      showToast(error.message || TEXT.failed, "error");
      setTimeout(() => {
        item.dataset.busy = "false";
        setStatus(item, downloadIcon(), "");
      }, 1800);
      return;
    }
    setTimeout(() => {
      item.dataset.busy = "false";
      setStatus(item, downloadIcon(), "");
    }, 1800);
  }

  async function downloadCurrentVideoCandidate(candidate) {
    const referer = candidate.requestHeaders?.Referer || candidate.pageUrl || location.href;
    const title = candidate.pageTitle || candidate.name || document.title || TEXT.currentShortLabel;
    const directResponse = await sendMessageWithTimeout(
      {
        type: isStandaloneDetailPage() ? "VD_FETCH_VIDEO_SAFE" : "VD_FETCH_VIDEO",
        url: candidate.url,
        referer,
        suggestedTitle: title,
        name: candidate.name || "",
        extension: candidate.extension || "mp4",
        kind: candidate.kind || "video",
        pageTitle: candidate.pageTitle || title,
        pageUrl: candidate.pageUrl || location.href,
        requestHeaders: candidate.requestHeaders || null
      },
      DOWNLOAD_TIMEOUT_MS,
      TEXT.failed
    );
    if (directResponse?.ok) return directResponse;
    const fallbackResponse = await sendMessageWithTimeout(
      { type: "DOWNLOAD_SELECTED", mode: "firstPlayable", items: [candidate] },
      DOWNLOAD_TIMEOUT_MS,
      TEXT.failed
    );
    if (fallbackResponse?.ok) return fallbackResponse;
    return directResponse || fallbackResponse || { ok: false, error: TEXT.failed };
  }

  async function handleSourceItemClick(item, context) {
    await refreshSourceAccess();
    if (!sourceTraceAccess) {
      removeSourceItems();
      showToast(
        globalThis.MMWPluginFeatureAccess?.deniedReason?.(
          sourceAuth,
          globalThis.MMWPluginFeatureAccess.SOURCE_TRACE
        ) || "当前账号未开通视频溯源。",
        "error"
      );
      return;
    }
    if (item.dataset.busy === "true" || item.dataset.confirming === "true") return;
    const retryKey = context.url;
    const retry = sourceDownloadRetries.get(retryKey);
    if (retry) {
      await submitTracedSourceDownload(item, retryKey, retry, true);
      return;
    }
    item.dataset.confirming = "true";
    const confirmed = await confirmSourceCost();
    item.dataset.confirming = "false";
    if (!confirmed) return;
    item.dataset.busy = "true";
    setStatus(item, loadingIcon(), TEXT.tracing);
    try {
      const traceResponse = await sendMessageWithTimeout(
        { type: "SOURCE_TRACE", url: context.url },
        TRACE_TIMEOUT_MS,
        TEXT.traceFailed
      );
      if (!traceResponse?.ok) throw new Error(readResponseError(traceResponse) || TEXT.traceFailed);
      const payload = traceResponse.payload || {};
      const candidate = createPreferredSourceCandidate(payload, context);
      if (!candidate) throw new Error(TEXT.noSourceUrl);
      const traced = { candidate, payload };
      sourceDownloadRetries.set(retryKey, traced);
      await submitTracedSourceDownload(item, retryKey, traced, false);
      return;
    } catch (error) {
      const sourceError = normalizeErrorMessage(error, TEXT.traceFailed);
      logSourceTraceDiagnostic("source-trace-failed", context, sourceError);
      setStatus(item, errorIcon(), TEXT.failed);
      showToast(sourceError, "error");
      setTimeout(() => {
        item.dataset.busy = "false";
        setStatus(item, downloadIcon(), "");
      }, 1800);
      return;
    }
  }

  async function submitTracedSourceDownload(item, retryKey, traced, isRetry) {
    item.dataset.busy = "true";
    setStatus(item, loadingIcon(), isRetry ? "正在重试下载..." : "正在下载...");
    try {
      await downloadSourceCandidate(traced.candidate);
      sourceDownloadRetries.delete(retryKey);
      const original = traced.candidate.isOriginal === true;
      setStatus(item, successIcon(), original ? "Original下载中" : "最大资源下载中");
      showToast(buildDirectSourceMessage(traced.payload, original), original ? "success" : "info");
    } catch (error) {
      const downloadError = normalizeErrorMessage(error, TEXT.failed);
      logSourceTraceDiagnostic("source-trace-download-failed", { url: retryKey }, downloadError);
      setStatus(item, errorIcon(), "重试下载");
      showToast(
        `溯源已完成，下载提交失败；点击“下载源视频”可直接重试，不会再次溯源扣费。${downloadError}`,
        "error"
      );
      setTimeout(() => {
        item.dataset.busy = "false";
        setStatus(item, downloadIcon(), "重试下载");
      }, 1800);
      return false;
    }
    setTimeout(() => {
      item.dataset.busy = "false";
      setStatus(item, downloadIcon(), "");
    }, 1800);
    return true;
  }

  function buildDirectSourceMessage(payload, original) {
    if (original) {
      const billing = String(payload?.message || "已找到 Original 原视频，本次按账号规则结算。").trim();
      return `${billing} 已开始下载 Original 原视频。`;
    }
    const refundedPoints = Math.max(0, Number(payload?.refunded_points || 0));
    const refund = payload?.free_quota_refunded
      ? "已恢复本次免费额度"
      : (refundedPoints > 0 ? `已返还 ${refundedPoints} 点预扣积分` : "本次未扣积分");
    return `未获取到源视频，已开始下载返回结果中的最大资源；${refund}。`;
  }

  async function confirmSourceCost() {
    const preview = buildSourceCostPreview(sourceAuth);
    if (await readNoReminder(preview.signature)) return true;
    return showSourceConfirmDialog(preview);
  }

  function buildSourceCostPreview(auth) {
    const account = auth?.account && typeof auth.account === "object" ? auth.account : {};
    const rawCost = account.source_trace_cost_points;
    const parsedCost = Number(rawCost);
    const costPoints = Number.isFinite(parsedCost) && parsedCost >= 0 ? Math.trunc(parsedCost) : null;
    const freeRemaining = Math.max(0, Math.trunc(Number(account.source_trace_free_remaining || 0)));
    const pointsBalance = Math.max(0, Math.trunc(Number(account.points_balance || 0)));
    if (costPoints === 0) {
      return {
        signature: "0|free",
        message: "当前账号溯源定价为 0 积分/次，本次无需预扣积分。如果未返回“原视频 + Original”素材，仍会下载最大资源。是否继续？"
      };
    }
    if (costPoints !== null && freeRemaining > 0) {
      return {
        signature: `${costPoints}|quota`,
        message: `当前账号溯源定价为 ${costPoints} 积分/次；本次将使用 1 次免费额度（当前剩余 ${freeRemaining} 次），不会预扣积分。如果未返回“原视频 + Original”素材，系统会自动恢复本次免费额度。是否继续？`
      };
    }
    if (costPoints !== null) {
      return {
        signature: `${costPoints}|points`,
        message: `当前账号溯源定价为 ${costPoints} 积分/次；本次将预扣 ${costPoints} 积分，当前余额 ${pointsBalance} 积分。如果未返回“原视频 + Original”素材，系统会自动返还本次预扣。是否继续？`
      };
    }
    return {
      signature: "unknown|points",
      message: "当前账号溯源定价暂未同步，本次将按服务器实时定价预扣积分。如果未返回“原视频 + Original”素材，系统会自动返还本次预扣。是否继续？"
    };
  }

  function readNoReminder(signature) {
    return new Promise((resolve) => {
      try {
        if (chrome?.storage?.local) {
          chrome.storage.local.get([REMIND_STORAGE_KEY], (result) => {
            if (chrome.runtime?.lastError) {
              resolve(readNoReminderFallback(signature));
              return;
            }
            resolve(result?.[REMIND_STORAGE_KEY] === signature);
          });
          return;
        }
      } catch {}
      resolve(readNoReminderFallback(signature));
    });
  }

  function writeNoReminder(signature) {
    return new Promise((resolve) => {
      try {
        if (chrome?.storage?.local) {
          chrome.storage.local.set({ [REMIND_STORAGE_KEY]: signature }, () => {
            if (chrome.runtime?.lastError) writeNoReminderFallback(signature);
            resolve();
          });
          return;
        }
      } catch {}
      writeNoReminderFallback(signature);
      resolve();
    });
  }

  function readNoReminderFallback(signature) {
    try {
      return localStorage.getItem(REMIND_STORAGE_KEY) === signature;
    } catch {
      return false;
    }
  }

  function writeNoReminderFallback(signature) {
    try {
      localStorage.setItem(REMIND_STORAGE_KEY, signature);
    } catch {}
  }

  function showSourceConfirmDialog(preview) {
    if (confirmDialogPromise) return confirmDialogPromise;
    const message = preview.message;
    confirmDialogPromise = new Promise((resolve) => {
      const mount = document.body || document.documentElement;
      const mask = document.createElement("div");
      mask.className = "mmw-source-confirm-mask";
      mask.innerHTML = `
        <div class="mmw-source-confirm-dialog" role="dialog" aria-modal="true">
          <div class="mmw-source-confirm-message">${message}</div>
          <label class="mmw-source-confirm-check">
            <input type="checkbox" />
            <span>${TEXT.dontRemind}</span>
          </label>
          <div class="mmw-source-confirm-actions">
            <button type="button" class="mmw-source-confirm-cancel">${TEXT.cancel}</button>
            <button type="button" class="mmw-source-confirm-ok">${TEXT.continueFetch}</button>
          </div>
        </div>
      `;

      let settled = false;
      const checkbox = mask.querySelector('input[type="checkbox"]');
      const cancelButton = mask.querySelector(".mmw-source-confirm-cancel");
      const okButton = mask.querySelector(".mmw-source-confirm-ok");
      const cleanup = (value) => {
        if (settled) return;
        settled = true;
        document.removeEventListener("keydown", onKeydown, true);
        mask.classList.remove("is-show");
        setTimeout(() => mask.remove(), 160);
        confirmDialogPromise = null;
        resolve(value);
      };
      const onKeydown = (event) => {
        if (event.key === "Escape") cleanup(false);
      };

      cancelButton?.addEventListener("click", () => cleanup(false));
      okButton?.addEventListener("click", async () => {
        okButton.disabled = true;
        if (checkbox?.checked) await writeNoReminder(preview.signature);
        cleanup(true);
      });
      mask.addEventListener("click", (event) => {
        if (event.target === mask) cleanup(false);
      });
      document.addEventListener("keydown", onKeydown, true);
      mount.appendChild(mask);
      setTimeout(() => mask.classList.add("is-show"), 0);
      setTimeout(() => okButton?.focus?.(), 30);
    });
    return confirmDialogPromise;
  }

  function buildTraceContext(button) {
    const wrapper = button.closest(".dy-download-wrapper");
    const media = readMediaList(button);
    const rawId = wrapper?.dataset.videoId || button.__videoData?.id || findIdInMedia(media);
    const awemeId = normalizeAwemeId(rawId);
    const url = wrapper?.dataset.traceUrl || resolveTraceUrl(wrapper || button, awemeId);
    const videoTitle = readContextVideoTitle(button, media);
    const title = pickTitle(media, awemeId, videoTitle);
    return {
      key: `${awemeId || "no-id"}|${url || location.href}`,
      awemeId,
      url,
      title,
      videoTitle,
      media
    };
  }

  function buildStandaloneTraceContext() {
    const awemeId = normalizeAwemeId(location.pathname) || normalizeAwemeId(new URLSearchParams(location.search).get("modal_id"));
    const url = isUsableDouyinPageUrl(location.href) ? location.href : (awemeId ? buildAwemePageUrl(awemeId) : "");
    const videoTitle = readCurrentPageVideoTitle();
    const title = sanitizeName(videoTitle || document.title || (awemeId ? `douyin-${awemeId}` : TEXT.shortLabel));
    return {
      key: `${awemeId || "detail"}|${url || location.href}`,
      awemeId,
      url,
      title,
      videoTitle,
      media: []
    };
  }

  function createCurrentVideoCandidate(context) {
    const video = findPrimaryVideoElement();
    const url = getVideoUrl(video);
    if (!url) return null;
    return createCurrentCandidateFromUrl(url, context, "douyin-detail-current");
  }

  function createPendingCurrentVideoCandidate(context) {
    if (!findPrimaryVideoElement()) return null;
    return createCurrentCandidateFromUrl("", context, "douyin-detail-current-pending");
  }

  function createStandaloneCurrentCandidate(context) {
    const candidate = createCurrentVideoCandidate(context) || createPendingCurrentVideoCandidate(context);
    if (candidate) candidate.menuLabel = TEXT.normalLabel;
    return candidate;
  }

  function createCurrentCandidateFromUrl(url, context, source) {
    const extension = getExtension(url) || "mp4";
    const title = sanitizeName(context?.title || document.title || (context?.awemeId ? `douyin-${context.awemeId}` : TEXT.currentShortLabel));
    return {
      id: `douyin-current-${context?.awemeId || Date.now()}`,
      url,
      kind: extension === "m3u8" ? "playlist" : "video",
      extension,
      mimeType: extension === "m3u8" ? "application/vnd.apple.mpegurl" : "video/mp4",
      name: sanitizeName(`${title} ${TEXT.currentShortLabel}`).slice(0, 100) || "douyin-current-video",
      pageTitle: title,
      pageUrl: context?.url || location.href,
      source,
      awemeId: context?.awemeId || "",
      requestHeaders: {
        Referer: context?.url || location.href,
        Origin: "https://www.douyin.com"
      },
      displayLabel: TEXT.currentDetail,
      variantPriority: 500
    };
  }

  async function resolveCurrentVideoCandidate(candidate, context) {
    const deadline = Date.now() + CURRENT_RESOURCE_WAIT_MS;
    do {
      const freshCandidate = createCurrentVideoCandidate(context);
      if (freshCandidate?.url) return freshCandidate;
      if (candidate?.url) return candidate;
      requestPageResourceScan();
      const response = await sendMessageWithTimeout({ type: "GET_RESOURCES" }, 1800, TEXT.noCurrentUrl);
      const resource = pickCapturedCurrentVideo(response?.resources || [], context);
      if (resource?.url) return createCurrentCandidateFromResource(resource, context);
      await delay(CURRENT_RESOURCE_POLL_MS);
    } while (Date.now() < deadline);
    return null;
  }

  function createCurrentCandidateFromResource(resource, context) {
    const resolved = createCurrentCandidateFromUrl(resource.url, context, "douyin-detail-current-captured");
    resolved.extension = resource.extension || getExtension(resource.url) || resolved.extension;
    resolved.mimeType = resource.mimeType || resolved.mimeType;
    resolved.size = Number(resource.size) || 0;
    resolved.width = Number(resource.width) || 0;
    resolved.height = Number(resource.height) || 0;
    resolved.name = sanitizeName(resource.name || resource.fileName || resolved.name).slice(0, 100) || resolved.name;
    resolved.pageTitle = sanitizeName(resource.pageTitle || resolved.pageTitle);
    resolved.requestHeaders = {
      ...resolved.requestHeaders,
      ...(resource.requestHeaders && typeof resource.requestHeaders === "object" ? resource.requestHeaders : {})
    };
    return resolved;
  }

  function requestPageResourceScan() {
    try {
      window.__MRP_SCANNER__?.scan?.();
    } catch {}
  }

  function pickCapturedCurrentVideo(resources, context) {
    const candidates = (Array.isArray(resources) ? resources : []).filter(isCapturedVideoCandidate);
    const contextMatched = candidates.filter((resource) => isContextMatchedCapturedVideo(resource, context));
    const base = contextMatched.length ? contextMatched : candidates;
    const directDouyin = base.filter((resource) => isDouyinDirectMediaUrl(resource.url));
    const pool = directDouyin.length ? directDouyin : base;
    return pool
      .sort((left, right) => scoreCapturedVideo(right, context) - scoreCapturedVideo(left, context))[0] || null;
  }

  function isContextMatchedCapturedVideo(resource, context) {
    if (!context?.awemeId && !context?.url) return false;
    const values = [
      resource?.awemeId,
      resource?.sourceTraceUrl,
      resource?.pageUrl,
      resource?.requestHeaders?.Referer,
      resource?.requestHeaders?.referer
    ].map((value) => String(value || ""));
    if (context?.awemeId && values.some((value) => value.includes(context.awemeId))) return true;
    if (context?.url) {
      const contextKey = stripUrlVolatileParts(context.url);
      return Boolean(contextKey && values.some((value) => stripUrlVolatileParts(value) === contextKey));
    }
    return false;
  }

  function isCapturedVideoCandidate(resource) {
    const url = normalizeHttpUrl(resource?.url || "");
    if (!url || isKnownPageLikeUrl(url)) return false;
    const extension = String(resource?.extension || getExtension(url) || "").toLowerCase();
    const mimeType = String(resource?.mimeType || "").toLowerCase();
    const kind = String(resource?.kind || "").toLowerCase();
    if (["m3u8", "mpd"].includes(extension)) return false;
    return kind === "video"
      || mimeType.startsWith("video/")
      || ["mp4", "m4v", "mov", "webm"].includes(extension);
  }

  function scoreCapturedVideo(resource, context) {
    let score = 0;
    const url = String(resource?.url || "");
    const sizeMb = (Number(resource?.size) || 0) / 1024 / 1024;
    if (isDouyinDirectMediaUrl(url)) score += 3000;
    if (/dom-video/i.test(String(resource?.source || ""))) score += 1200;
    if (context?.awemeId && String(resource?.awemeId || "").includes(context.awemeId)) score += 900;
    if (context?.awemeId && String(resource?.sourceTraceUrl || resource?.pageUrl || "").includes(context.awemeId)) score += 600;
    const area = (Number(resource?.width) || 0) * (Number(resource?.height) || 0);
    score += Math.min(sizeMb * 1000, 12000);
    score += Math.min(area / 10000, 500);
    score += Math.min(Math.max(0, Number(resource?.time) || 0) / 1000000000000, 20);
    return score;
  }

  function isDouyinDirectMediaUrl(url) {
    try {
      const parsed = new URL(url);
      return /(^|\.)(douyinvod|bytev|365yg)\.com$/i.test(parsed.hostname);
    } catch {
      return false;
    }
  }

  function readMediaList(button) {
    try {
      const list = button.__videoData?.getMediaList?.();
      return Array.isArray(list) ? list : [];
    } catch {
      return [];
    }
  }

  function findIdInMedia(media) {
    for (const item of media || []) {
      const id = normalizeAwemeId(item?.id || item?.url || "");
      if (id) return id;
    }
    return "";
  }

  function normalizeAwemeId(value) {
    const match = String(value || "").match(/\d{8,}/);
    return match ? match[0] : "";
  }

  function isStandaloneDetailPage() {
    return /\/(video|note)\//i.test(location.pathname) || new URLSearchParams(location.search).has("modal_id");
  }

  function positionFallbackButton(wrapper) {
    if (!(wrapper instanceof HTMLElement)) return;
    const rect = findPrimaryVideoRect();
    const size = 40;
    const inset = window.innerWidth <= 768 ? 14 : 20;
    const top = rect ? rect.top + inset : 92;
    const right = rect ? window.innerWidth - rect.right + inset : 20;
    wrapper.style.position = "fixed";
    wrapper.style.width = `${size}px`;
    wrapper.style.height = `${size}px`;
    wrapper.style.left = "auto";
    wrapper.style.top = `${Math.max(12, Math.min(window.innerHeight - size - 12, Math.round(top)))}px`;
    wrapper.style.right = `${Math.max(12, Math.round(right))}px`;
    wrapper.style.zIndex = "100";
  }

  function positionFallbackPanel(panel, button) {
    const rect = button.getBoundingClientRect();
    const panelWidth = 240;
    let left = rect.left - panelWidth - 10;
    if (left < 12) left = Math.min(window.innerWidth - panelWidth - 12, rect.right + 10);
    let top = rect.top;
    if (top > window.innerHeight - 120) top = window.innerHeight - 120;
    panel.style.left = `${Math.max(12, Math.round(left))}px`;
    panel.style.top = `${Math.max(12, Math.round(top))}px`;
  }

  function findPrimaryVideoRect() {
    const video = findPrimaryVideoElement();
    return video?.getBoundingClientRect?.() || null;
  }

  function findPrimaryVideoElement() {
    let best = null;
    for (const video of document.querySelectorAll("video")) {
      const rect = video.getBoundingClientRect();
      if (!isRenderableRect(rect)) continue;
      const area = rect.width * rect.height;
      const bestRect = best?.getBoundingClientRect?.();
      const bestArea = bestRect ? bestRect.width * bestRect.height : 0;
      if (!best || area > bestArea) best = video;
    }
    return best;
  }

  function getVideoUrl(video) {
    if (!(video instanceof HTMLVideoElement)) return "";
    const candidates = [
      video.currentSrc,
      video.src,
      ...Array.from(video.querySelectorAll("source")).map((source) => source.src || source.getAttribute("src") || "")
    ];
    for (const value of candidates) {
      const url = normalizeHttpUrl(value);
      if (/^https?:\/\//i.test(url) && !isKnownPageLikeUrl(url)) return url;
    }
    return "";
  }

  function isKnownPageLikeUrl(url) {
    const extension = getExtension(url);
    return ["htm", "html", "shtml", "xhtml", "php", "asp", "aspx"].includes(extension);
  }

  function isRenderableRect(rect) {
    return rect
      && rect.width >= 160
      && rect.height >= 160
      && rect.bottom > 0
      && rect.right > 0
      && rect.top < window.innerHeight
      && rect.left < window.innerWidth;
  }

  function isVisibleElement(element) {
    if (!(element instanceof Element)) return false;
    const rect = element.getBoundingClientRect();
    const style = window.getComputedStyle(element);
    return rect.width > 8
      && rect.height > 8
      && rect.bottom > 0
      && rect.right > 0
      && rect.top < window.innerHeight
      && rect.left < window.innerWidth
      && style.display !== "none"
      && style.visibility !== "hidden"
      && Number(style.opacity || 1) > 0.05;
  }

  function pickTitle(media, awemeId, videoTitle = "") {
    const filename = media.find((item) => item?.filename)?.filename || "";
    return sanitizeName(videoTitle || filename || document.title || (awemeId ? `douyin-${awemeId}` : TEXT.shortLabel));
  }

  function readContextVideoTitle(button, media) {
    for (const item of media || []) {
      const title = sanitizeVideoTitle(item?.title || item?.desc || item?.description || item?.caption);
      if (title) return title;
    }
    let shell = resolveTitleCardShell(button);
    if (!shell && button instanceof Element) {
      const buttonRect = button.getBoundingClientRect();
      const centerX = buttonRect.left + buttonRect.width / 2;
      const centerY = buttonRect.top + buttonRect.height / 2;
      shell = Array.from(document.querySelectorAll('[id^="waterfall_item_"], .search-result-card'))
        .map((node) => node.closest?.('[id^="waterfall_item_"]') || node)
        .find((node) => {
          const rect = node.getBoundingClientRect();
          return centerX >= rect.left && centerX <= rect.right && centerY >= rect.top && centerY <= rect.bottom;
        }) || null;
    }
    const explicit = shell?.querySelector?.(
      '[data-e2e="video-desc"], [data-e2e="feed-video-desc"], [data-e2e="video-title"], [data-e2e="feed-video-title"]'
    );
    const explicitTitle = sanitizeVideoTitle(explicit?.innerText || explicit?.textContent || "");
    if (explicitTitle) return explicitTitle;
    const cardTitle = findTitleInCardText(shell?.innerText || "");
    if (cardTitle) return cardTitle;
    return isStandaloneDetailPage() ? readCurrentPageVideoTitle() : "";
  }

  function resolveTitleCardShell(button) {
    const direct = button?.closest?.(
      '[id^="waterfall_item_"], .search-result-card, [data-e2e="feed-active-video"], [data-e2e="feed-item"], [data-e2e="feed-video"]'
    );
    if (direct) return direct.closest?.('[id^="waterfall_item_"]') || direct;
    const listItem = button?.closest?.("li");
    const profileLink = button?.closest?.(PROFILE_CARD_LINK_SELECTOR)
      || listItem?.querySelector?.(PROFILE_CARD_LINK_SELECTOR);
    return profileLink?.closest?.("li") || profileLink || null;
  }

  function readCurrentPageVideoTitle() {
    const node = document.querySelector(
      '[data-e2e="video-desc"], [data-e2e="feed-video-desc"], [data-e2e="video-title"], [data-e2e="feed-video-title"], [data-e2e="detail-video-info"] h1'
    );
    const nodeTitle = sanitizeVideoTitle(node?.innerText || node?.textContent || "");
    if (nodeTitle) return nodeTitle;
    if (!/\/(video|note)\//i.test(location.pathname)) return "";
    return sanitizeVideoTitle(
      String(document.title || "").replace(/\s*-\s*抖音(?:搜索)?\s*$/u, "")
    );
  }

  function findTitleInCardText(value) {
    const lines = String(value || "")
      .split(/\n+/)
      .map((line) => line.trim())
      .filter(Boolean)
      .slice(0, 20);
    const title = lines.find((line) => {
      if (line.length < 2) return false;
      if (/^\d{1,2}:\d{2}$/.test(line)) return false;
      if (/^(?:\d+(?:\.\d+)?(?:万|亿)?|\d{1,3}(?:,\d{3})+)(?:赞)?$/.test(line)) return false;
      if (/^@/.test(line)) return false;
      if (/^[\u00b7\u2022]?\s*(?:\d{4}[-/.年])?\d{1,2}(?:月|[-/.])\d{1,2}日?$/.test(line)) return false;
      return !/^(?:广告|置顶|搜索|关注|喜欢|分享)$/.test(line);
    });
    return sanitizeVideoTitle(title || "");
  }

  function sanitizeVideoTitle(value) {
    return String(value || "")
      .replace(/\s+/g, " ")
      .replace(/\s*(?:展开|收起)\s*$/u, "")
      .trim()
      .slice(0, 500);
  }

  function resolveTraceUrl(root, awemeId) {
    const link = findSurfacePageLink(root, awemeId);
    if (link) return link;
    if (awemeId) return buildAwemePageUrl(awemeId);
    if (isUsableDouyinPageUrl(location.href)) return location.href;
    return "";
  }

  function findSurfacePageLink(root, awemeId) {
    const roots = [];
    let node = root instanceof Element ? root : null;
    for (let depth = 0; node && depth < 9; depth += 1) {
      roots.push(node);
      node = node.parentElement;
    }
    const selector = [
      'a[href*="/video/"]',
      'a[href*="/note/"]',
      'a[href*="modal_id="]',
      'a[href*="/jingxuan/"]'
    ].join(",");
    for (const current of roots) {
      const links = Array.from(current.matches?.("a[href]") ? [current] : current.querySelectorAll(selector));
      const urls = links
        .map((link) => normalizeHttpUrl(link.getAttribute("href") || link.href || ""))
        .filter(isUsableDouyinPageUrl);
      if (awemeId) {
        const matched = urls.find((url) => url.includes(awemeId));
        if (matched) return matched;
      }
      if (urls[0]) return urls[0];
    }
    return "";
  }

  function buildAwemePageUrl(awemeId) {
    return `https://www.douyin.com/video/${awemeId}`;
  }

  function isUsableDouyinPageUrl(url) {
    try {
      const parsed = new URL(url, location.href);
      if (!/(^|\.)douyin\.com$/i.test(parsed.hostname)) return false;
      return /\/(video|note|jingxuan)\b/i.test(parsed.pathname) || parsed.searchParams.has("modal_id");
    } catch {
      return false;
    }
  }

  function normalizeHttpUrl(value) {
    try {
      return new URL(String(value || ""), location.href).href;
    } catch {
      return "";
    }
  }

  function stripUrlVolatileParts(url) {
    try {
      const parsed = new URL(url, location.href);
      parsed.hash = "";
      return parsed.href;
    } catch {
      return "";
    }
  }

  function createPreferredSourceCandidate(payload, context) {
    const source = globalThis.MMWSourceTraceSelector?.selectPreferredMedia?.(payload)
      || globalThis.MMWSourceTraceSelector?.selectLargestMedia?.(payload);
    return source ? createSourceCandidate(source, payload, context, 0) : null;
  }

  function createSourceCandidate(source, payload, context, index) {
    const url = String(source.download_url || source.url || payload?.download_url || "").trim();
    if (!/^https?:\/\//i.test(url)) return null;
    const resolution = String(source.resolution || "").trim();
    const sizeText = String(source.size_text || source.size || "").trim();
    const title = sanitizeName(payload?.title || context.title || document.title || TEXT.shortLabel);
    const original = Boolean(source.is_original);
    const sourceLabel = original ? "Original 原视频" : "最大资源 未获取源视频";
    const suffix = [sourceLabel, resolution, sizeText].filter(Boolean).join(" ");
    return {
      id: `source-trace-${payload?.request_id || context.awemeId || Date.now()}-${index + 1}`,
      url,
      kind: "video",
      extension: getExtension(url) || "mp4",
      mimeType: "video/mp4",
      name: sanitizeName(`${title} ${suffix}`).slice(0, 100) || "douyin-source-video",
      pageTitle: title,
      pageUrl: context.url || location.href,
      requestHeaders: {
        Referer: context.url || "https://www.douyin.com/",
        Origin: "https://www.douyin.com"
      },
      displayLabel: suffix || TEXT.shortLabel,
      semanticKey: "source-trace",
      variantPriority: 1000 - index,
      resolution,
      sizeText,
      isOriginal: original
    };
  }

  async function downloadSourceCandidate(candidate) {
    const response = await sendMessageWithTimeout(
      { type: "DOWNLOAD_SELECTED", mode: "firstPlayable", items: [candidate] },
      DOWNLOAD_TIMEOUT_MS,
      TEXT.failed
    );
    if (!response?.ok) throw new Error(readResponseError(response) || TEXT.failed);
    return response;
  }

  function sendMessageWithTimeout(message, timeoutMs, timeoutMessage) {
    return new Promise((resolve) => {
      let settled = false;
      const timer = setTimeout(() => {
        if (settled) return;
        settled = true;
        resolve({ ok: false, error: timeoutMessage });
      }, Math.max(1000, timeoutMs || 10000));
      try {
        chrome.runtime.sendMessage(message, (response) => {
          if (settled) return;
          settled = true;
          clearTimeout(timer);
          if (chrome.runtime.lastError) {
            resolve({ ok: false, error: chrome.runtime.lastError.message });
            return;
          }
          resolve(response);
        });
      } catch (error) {
        if (settled) return;
        settled = true;
        clearTimeout(timer);
        resolve({ ok: false, error: error.message || String(error) });
      }
    });
  }

  function delay(ms) {
    return new Promise((resolve) => setTimeout(resolve, Math.max(0, Number(ms) || 0)));
  }

  function readResponseError(response) {
    if (response?.error) return response.error;
    if (response?.message) return response.message;
    const failure = Array.isArray(response?.results)
      ? response.results.find((item) => !item.ok && item.error)
      : null;
    return failure?.error || "";
  }

  function normalizeErrorMessage(error, fallback = TEXT.failed) {
    const message = String(error?.message || error || fallback || TEXT.failed).trim();
    return message || fallback || TEXT.failed;
  }

  function logSourceTraceDiagnostic(stage, context, errorMessage) {
    try {
      console.warn("[MMWGZT][source-trace]", {
        stage,
        traceUrl: context?.url || "",
        awemeId: context?.awemeId || "",
        pageUrl: location.href,
        error: errorMessage
      });
    } catch {}
  }

  function getExtension(url) {
    try {
      const pathname = new URL(url).pathname;
      return (pathname.match(/\.([a-z0-9]{2,8})$/i)?.[1] || "").toLowerCase();
    } catch {
      return "";
    }
  }

  function sanitizeName(value) {
    return String(value || "")
      .replace(/[\u{1F600}-\u{1F64F}\u{1F300}-\u{1F5FF}\u{1F680}-\u{1F6FF}\u{2600}-\u{26FF}\u{2700}-\u{27BF}]/gu, "")
      .replace(/[<>:"/\\|?*]/g, "")
      .replace(/[\x00-\x1f\x7f]/g, "")
      .trim()
      .replace(/\s+/g, " ")
      .slice(0, 120);
  }

  function setStatus(item, icon, title) {
    const status = item.querySelector(".mmw-source-status");
    if (status) status.innerHTML = icon;
    if (title) item.title = title;
  }

  function showToast(message, type = "info") {
    const toast = document.createElement("div");
    toast.className = "mmw-source-toast";
    toast.textContent = message;
    toast.style.background = type === "success" ? "#67C23A" : type === "error" ? "#E53935" : "#409EFF";
    document.body.appendChild(toast);
    setTimeout(() => toast.classList.add("is-show"), 0);
    setTimeout(() => {
      toast.classList.remove("is-show");
      setTimeout(() => toast.remove(), 220);
    }, 2600);
  }

  function injectStyle() {
    const root = document.head || document.documentElement;
    if (!root || document.getElementById(STYLE_ID)) return;
    const style = document.createElement("style");
    style.id = STYLE_ID;
    style.textContent = `
      .dy-download-panel .${BRIDGE_ITEM_CLASS} {
        padding: 10px 14px;
        cursor: pointer;
        transition: background 0.2s, border-left-color 0.2s;
        border-left: 3px solid transparent;
        box-sizing: border-box;
        color: #333;
        background: #fff;
      }
      .dy-download-panel .${ITEM_CLASS} {
        position: relative;
        overflow: visible;
      }
      .dy-download-panel .${BRIDGE_ITEM_CLASS}:hover {
        background: #f5f5f5;
        border-left-color: #409EFF;
      }
      .dy-download-panel .${BRIDGE_ITEM_CLASS}[data-busy="true"] {
        cursor: wait;
        pointer-events: auto;
      }
      .dy-download-panel .${BRIDGE_ITEM_CLASS} .mmw-source-row,
      .dy-download-panel .${BRIDGE_ITEM_CLASS} .mmw-source-main {
        display: flex !important;
        align-items: center !important;
        min-width: 0 !important;
        line-height: 20px !important;
        visibility: visible !important;
        opacity: 1 !important;
      }
      .dy-download-panel .${BRIDGE_ITEM_CLASS} .mmw-source-row {
        justify-content: space-between;
        font-weight: 500;
        font-size: 14px;
        min-height: 20px !important;
      }
      .dy-download-panel .${BRIDGE_ITEM_CLASS} .mmw-source-main {
        flex: 1 1 auto !important;
        width: auto !important;
        gap: 10px;
      }
      .dy-download-panel .${BRIDGE_ITEM_CLASS} .mmw-source-icon,
      .dy-download-panel .${BRIDGE_ITEM_CLASS} .mmw-source-status {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        flex-shrink: 0;
        line-height: 1;
      }
      .dy-download-panel .${BRIDGE_ITEM_CLASS} .mmw-source-status {
        margin-left: 12px;
      }
      .dy-download-panel .${BRIDGE_ITEM_CLASS} .mmw-source-label {
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
      }
      .dy-download-panel .${ITEM_CLASS} .mmw-source-label,
      .dy-download-panel .${TITLE_ITEM_CLASS} .mmw-source-label {
        display: block !important;
        flex: 1 1 auto !important;
        width: auto !important;
        min-width: 0 !important;
        height: auto !important;
        color: #000000 !important;
        -webkit-text-fill-color: #000000 !important;
        font-size: 14px !important;
        font-weight: 500 !important;
        line-height: 20px !important;
        text-indent: 0 !important;
        text-shadow: none !important;
        visibility: visible !important;
        opacity: 1 !important;
      }
      .dy-download-panel .${ITEM_CLASS} .mmw-source-tip {
        position: absolute;
        left: calc(100% + 10px);
        right: auto;
        top: 50%;
        bottom: auto;
        z-index: 10060;
        width: 304px;
        max-width: min(304px, calc(100vw - 32px));
        box-sizing: border-box;
        padding: 10px 12px;
        border-radius: 8px;
        background: rgba(17, 24, 39, 0.96);
        color: #f8fafc;
        box-shadow: 0 10px 24px rgba(15, 23, 42, 0.28);
        font-size: 12px;
        font-weight: 500;
        line-height: 1.62;
        white-space: normal;
        word-break: break-word;
        pointer-events: none;
        opacity: 0;
        visibility: hidden;
        --mmw-source-tip-x: -4px;
        --mmw-source-tip-y: -50%;
        transform: translate(var(--mmw-source-tip-x), var(--mmw-source-tip-y));
        transition: opacity 0.16s ease, transform 0.16s ease, visibility 0.16s ease;
      }
      .dy-download-panel .${ITEM_CLASS} .mmw-source-tip::before {
        content: "";
        position: absolute;
        left: -6px;
        top: 50%;
        width: 0;
        height: 0;
        border-width: 6px 6px 6px 0;
        border-style: solid;
        border-color: transparent rgba(17, 24, 39, 0.96) transparent transparent;
        transform: translateY(-50%);
      }
      .dy-download-panel .${ITEM_CLASS}[data-tip-side="left"] .mmw-source-tip {
        left: auto;
        right: calc(100% + 10px);
        --mmw-source-tip-x: 4px;
      }
      .dy-download-panel .${ITEM_CLASS}[data-tip-side="left"] .mmw-source-tip::before {
        left: auto;
        right: -6px;
        border-width: 6px 0 6px 6px;
        border-color: transparent transparent transparent rgba(17, 24, 39, 0.96);
      }
      .dy-download-panel .${ITEM_CLASS}[data-tip-align="top"] .mmw-source-tip {
        top: 0;
        bottom: auto;
        --mmw-source-tip-y: 0px;
      }
      .dy-download-panel .${ITEM_CLASS}[data-tip-align="top"] .mmw-source-tip::before {
        top: 18px;
        transform: none;
      }
      .dy-download-panel .${ITEM_CLASS}[data-tip-align="bottom"] .mmw-source-tip {
        top: auto;
        bottom: 0;
        --mmw-source-tip-y: 0px;
      }
      .dy-download-panel .${ITEM_CLASS}[data-tip-align="bottom"] .mmw-source-tip::before {
        top: auto;
        bottom: 18px;
        transform: none;
      }
      .dy-download-panel .${ITEM_CLASS}:hover .mmw-source-tip {
        opacity: 1;
        visibility: visible;
        --mmw-source-tip-x: 0px;
      }
      .mmw-source-loading {
        animation: mmw-source-rotate 1s linear infinite;
        transform-origin: center;
      }
      .mmw-source-toast {
        position: fixed;
        top: 20px;
        left: 50%;
        z-index: 10050;
        transform: translate(-50%, -8px);
        opacity: 0;
        color: #fff;
        padding: 10px 16px;
        border-radius: 6px;
        box-shadow: 0 2px 10px rgba(0,0,0,0.18);
        max-width: min(680px, calc(100vw - 32px));
        white-space: normal;
        word-break: break-word;
        line-height: 1.45;
        font-size: 14px;
        line-height: 1.35;
        transition: opacity 0.2s, transform 0.2s;
        max-width: min(420px, calc(100vw - 32px));
        pointer-events: none;
      }
      .mmw-source-toast.is-show {
        opacity: 1;
        transform: translate(-50%, 0);
      }
      .mmw-source-confirm-mask {
        position: fixed;
        inset: 0;
        z-index: 10060;
        display: flex;
        align-items: center;
        justify-content: center;
        padding: 24px;
        background: rgba(15, 23, 42, 0.42);
        opacity: 0;
        box-sizing: border-box;
        transition: opacity 0.16s ease;
      }
      .mmw-source-confirm-mask.is-show {
        opacity: 1;
      }
      .mmw-source-confirm-dialog {
        width: min(340px, calc(100vw - 48px));
        padding: 18px 18px 16px;
        border-radius: 8px;
        background: #fff;
        color: #1f2937;
        box-shadow: 0 14px 36px rgba(15, 23, 42, 0.24);
        box-sizing: border-box;
        font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
      }
      .mmw-source-confirm-message {
        font-size: 15px;
        line-height: 1.55;
        font-weight: 600;
        color: #202124;
      }
      .mmw-source-confirm-check {
        display: flex;
        align-items: center;
        gap: 8px;
        width: max-content;
        max-width: 100%;
        margin-top: 14px;
        color: #606266;
        font-size: 13px;
        line-height: 1.4;
        cursor: pointer;
        user-select: none;
      }
      .mmw-source-confirm-check input {
        width: 15px;
        height: 15px;
        margin: 0;
        accent-color: #409EFF;
        cursor: pointer;
      }
      .mmw-source-confirm-actions {
        display: flex;
        justify-content: flex-end;
        gap: 10px;
        margin-top: 18px;
      }
      .mmw-source-confirm-actions button {
        min-width: 76px;
        height: 34px;
        border-radius: 6px;
        border: 1px solid #dcdfe6;
        background: #fff;
        color: #606266;
        font-size: 14px;
        cursor: pointer;
        transition: background 0.16s, border-color 0.16s, color 0.16s;
      }
      .mmw-source-confirm-actions button:hover {
        border-color: #409EFF;
        color: #409EFF;
      }
      .mmw-source-confirm-actions button:disabled {
        cursor: wait;
        opacity: 0.7;
      }
      .mmw-source-confirm-actions .mmw-source-confirm-ok {
        border-color: #409EFF;
        background: #409EFF;
        color: #fff;
      }
      .mmw-source-confirm-actions .mmw-source-confirm-ok:hover {
        border-color: #66b1ff;
        background: #66b1ff;
        color: #fff;
      }
      @keyframes mmw-source-rotate {
        from { transform: rotate(0deg); }
        to { transform: rotate(360deg); }
      }
    `;
    root.appendChild(style);
  }

  function sourceIcon() {
    return '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M13 2L4 14H11L10 22L20 9H13L13 2Z" fill="#409EFF"/></svg>';
  }

  function videoIcon() {
    return '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><rect x="3" y="5" width="18" height="14" rx="2" stroke="#409EFF" stroke-width="2"/><path d="M10 9L15 12L10 15V9Z" fill="#409EFF"/></svg>';
  }

  function titleIcon() {
    return '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><rect x="4" y="3" width="16" height="18" rx="2" stroke="#16A34A" stroke-width="2"/><path d="M8 8H16M8 12H16M8 16H13" stroke="#16A34A" stroke-width="2" stroke-linecap="round"/></svg>';
  }

  function downloadIcon() {
    return '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M21 15V19C21 19.5304 20.7893 20.0391 20.4142 20.4142C20.0391 20.7893 19.5304 21 19 21H5C4.46957 21 3.96086 20.7893 3.58579 20.4142C3.21071 20.0391 3 19.5304 3 19V15" stroke="#409EFF" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M7 10L12 15L17 10" stroke="#409EFF" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M12 15V3" stroke="#409EFF" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>';
  }

  function loadingIcon() {
    return '<svg class="mmw-source-loading" width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><circle cx="12" cy="12" r="10" stroke="#409EFF" stroke-width="3" stroke-linecap="round" stroke-dasharray="60" stroke-dashoffset="15"/></svg>';
  }

  function successIcon() {
    return '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><circle cx="12" cy="12" r="10" fill="#67C23A"/><path d="M7 13L10 16L17 9" stroke="white" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/></svg>';
  }

  function errorIcon() {
    return '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><circle cx="12" cy="12" r="10" fill="#F56C6C"/><path d="M15 9L9 15M9 9L15 15" stroke="white" stroke-width="2.5" stroke-linecap="round"/></svg>';
  }
})();
