Twitter/X Media Batch Downloader

Batch download all images and videos from a Twitter/X account, including withheld accounts, in original quality.

// ==UserScript==
// @name         Twitter/X Media Batch Downloader
// @description  Batch download all images and videos from a Twitter/X account, including withheld accounts, in original quality.
// @icon         https://raw.githubusercontent.com/afkarxyz/Twitter-X-Media-Batch-Downloader/refs/heads/main/Archived/icon.svg
// @version      4.4
// @author       afkarxyz
// @namespace    https://github.com/afkarxyz/userscripts/
// @supportURL   https://github.com/afkarxyz/userscripts/issues
// @homepageURL  https://www.patreon.com/exyezed
// @antifeature  Payment
// @license      MIT
// @match        https://twitter.com/*
// @match        https://x.com/*
// @grant        GM_xmlhttpRequest
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_download
// @connect      api.xbatch.online
// @connect      backup.xbatch.online
// @connect      pbs.twimg.com
// @connect      video.twimg.com
// @require      https://cdn.jsdelivr.net/npm/[email protected]/dist/jszip.min.js
// ==/UserScript==

(() => {
  const JSZip = window.JSZip;

  const API_URLS = {
    DEFAULT: "https://api.xbatch.online",
    BACKUP: "https://backup.xbatch.online",
  };

  const ICONS = {
    PREV: `<svg style="width: 12px; height: 12px; margin-right: 6px; display: inline-block; vertical-align: middle;" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512">
      <path fill="currentColor" d="M9.4 233.4c-12.5 12.5-12.5 32.8 0 45.3l192 192c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L77.3 256 246.6 86.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0l-192 192z"/>
    </svg>`,
    NEXT: `<svg style="width: 12px; height: 12px; margin-left: 6px; display: inline-block; vertical-align: middle;" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512">
      <path fill="currentColor" d="M310.6 233.4c12.5 12.5 12.5 32.8 0 45.3l-192 192c-12.5 12.5-32.8 12.5-45.3 0s-12.5-32.8 0-45.3L242.7 256 73.4 86.6c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0l192 192z"/>
    </svg>`,
    AUTO: `<svg style="width: 12px; height: 12px; margin-right: 6px; display: inline-block; vertical-align: middle;" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
      <path fill="currentColor" d="M327.5 85.2c-4.5 1.7-7.5 6-7.5 10.8s3 9.1 7.5 10.8L384 128l21.2 56.5c1.7 4.5 6 7.5 10.8 7.5s9.1-3 10.8-7.5L448 128l56.5-21.2c4.5-1.7 7.5-6 7.5-10.8s-3-9.1-7.5-10.8L448 64 426.8 7.5C425.1 3 420.8 0 416 0s-9.1 3-10.8 7.5L384 64 327.5 85.2zM205.1 73.3c-2.6-5.7-8.3-9.3-14.5-9.3s-11.9 3.6-14.5 9.3L123.3 187.3 9.3 240C3.6 242.6 0 248.3 0 254.6s3.6 11.9 9.3 14.5l114.1 52.7L176 435.8c2.6 5.7 8.3 9.3 14.5 9.3s11.9-3.6 14.5-9.3l52.7-114.1 114.1-52.7c5.7-2.6 9.3-8.3 9.3-14.5s-3.6-11.9-9.3-14.5L257.8 187.4 205.1 73.3zM384 384l-56.5 21.2c-4.5 1.7-7.5 6-7.5 10.8s3 9.1 7.5 10.8L384 448l21.2 56.5c1.7 4.5 6 7.5 10.8 7.5s9.1-3 10.8-7.5L448 448l56.5-21.2c4.5-1.7 7.5-6 7.5-10.8s-3-9.1-7.5-10.8L448 384l-21.2-56.5c-1.7-4.5-6-7.5-10.8-7.5s-9.1 3-10.8 7.5L384 384z"/>
    </svg>`,
    STOP: `<svg style="width: 12px; height: 12px; margin-right: 6px; display: inline-block; vertical-align: middle;" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512">
      <path fill="currentColor" d="M0 128C0 92.7 28.7 64 64 64H320c35.3 0 64 28.7 64 64V384c0 35.3-28.7 64-64 64H64c-35.3 0-64-28.7-64-64V128z"/>
    </svg>`,
    CLEAR: {
      PATH: "M135.2 17.7C140.6 6.8 151.7 0 163.8 0L284.2 0c12.1 0 23.2 6.8 28.6 17.7L320 32l96 0c17.7 0 32 14.3 32 32s-14.3 32-32 32L32 96C14.3 96 0 81.7 0 64S14.3 32 32 32l96 0 7.2-14.3zM32 128l384 0 0 320c0 35.3-28.7 64-64 64L96 512c-35.3 0-64-28.7-64-64l0-320zm96 64c-8.8 0-16 7.2-16 16l0 224c0 8.8 7.2 16 16 16s16-7.2 16-16l0-224c0-8.8-7.2-16-16-16zm96 0c-8.8 0-16 7.2-16 16l0 224c0 8.8 7.2 16 16 16s16-7.2 16-16l0-224c0-8.8-7.2-16-16-16zm96 0c-8.8 0-16 7.2-16 16l0 224c0 8.8 7.2 16 16 16s16-7.2 16-16l0-224c0-8.8-7.2-16-16-16z",
      VIEWBOX: "0 0 448 512",
    },
    RESET: {
      PATH: "M463.5 224l8.5 0c13.3 0 24-10.7 24-24l0-128c0-9.7-5.8-18.5-14.8-22.2s-19.3-1.7-26.2 5.2L413.4 96.6c-87.6-86.5-228.7-86.2-315.8 1c-87.5 87.5-87.5 229.3 0 316.8s229.3 87.5 316.8 0c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0c-62.5 62.5-163.8 62.5-226.3 0s-62.5-163.8 0-226.3c62.2-62.2 162.7-62.5 225.3-1L327 183c-6.9 6.9-8.9 17.2-5.2 26.2s12.5 14.8 22.2 14.8l119.5 0z",
      VIEWBOX: "0 0 512 512",
    },
    PATREON: {
      PATH: "M489.7 153.8c-.1-65.4-51-119-110.7-138.3C304.8-8.5 207-5 136.1 28.4C50.3 68.9 23.3 157.7 22.3 246.2C21.5 319 28.7 510.6 136.9 512c80.3 1 92.3-102.5 129.5-152.3c26.4-35.5 60.5-45.5 102.4-55.9c72-17.8 121.1-74.7 121-150z",
      VIEWBOX: "0 0 512 512",
    },
    INFO: {
      PATH: "M256 48a208 208 0 1 1 0 416 208 208 0 1 1 0-416zm0 464A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM216 336c-13.3 0-24 10.7-24 24s10.7 24 24 24l80 0c13.3 0 24-10.7 24-24s-10.7-24-24-24l-8 0 0-88c0-13.3-10.7-24-24-24l-48 0c-13.3 0-24 10.7-24 24s10.7 24 24 24l24 0 0 64-24 0zm40-144a32 32 0 1 0 0-64 32 32 0 1 0 0 64z",
      VIEWBOX: "0 0 512 512",
    },
    DOWNLOAD: {
      SECONDARY_PATH:
        "M0 256C0 397.4 114.6 512 256 512s256-114.6 256-256c0-17.7-14.3-32-32-32s-32 14.3-32 32c0 106-86 192-192 192S64 362 64 256c0-17.7-14.3-32-32-32s-32 14.3-32 32z",
      PRIMARY_PATH:
        "M390.6 185.4c12.5 12.5 12.5 32.8 0 45.3l-112 112c-12.5 12.5-32.8 12.5-45.3 0l-112-112c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0L224 242.7 224 32c0-17.7 14.3-32 32-32s32 14.3 32 32l0 210.7 57.4-57.4c12.5-12.5 32.8-12.5 45.3 0z",
      VIEWBOX: "0 0 512 512",
    },
    FETCH: {
      PATH: "M374.6 214.6l-128 128c-12.5 12.5-32.8 12.5-45.3 0l-128-128c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0L192 242.7 192 32c0-17.7 14.3-32 32-32s32 14.3 32 32l0 210.7 73.4-73.4c12.5-12.5 32.8-12.5 45.3 0s12.5 32.8 0 45.3zM64 352l0 64c0 17.7 14.3 32 32 32l256 0c17.7 0 32-14.3 32-32l0-64c0-17.7 14.3-32 32-32s32 14.3 32 32l0 64c0 53-43 96-96 96L96 512c-53 0-96-43-96-96l0-64c0-17.7 14.3-32 32-32s32 14.3 32 32z",
      VIEWBOX: "0 0 448 512",
    },
    ACCOUNT: {
      PATH: "M412.1 416.6C398.1 361.1 347.9 320 288 320l-64 0c-59.9 0-110.1 41.1-124.1 96.6C58 375.9 32 319 32 256C32 132.3 132.3 32 256 32s224 100.3 224 224c0 63-26 119.9-67.9 160.6zm-28.5 23.4C347.5 465.2 303.5 480 256 480s-91.5-14.8-127.7-39.9c4-49.3 45.3-88.1 95.7-88.1l64 0c50.4 0 91.6 38.8 95.7 88.1zM256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zm0-256a48 48 0 1 1 0-96 48 48 0 1 1 0 96zm-80-48a80 80 0 1 0 160 0 80 80 0 1 0 -160 0z",
      VIEWBOX: "0 0 512 512",
    },
    TOTAL: {
      PATH: "M320 464c8.8 0 16-7.2 16-16l0-288-80 0c-17.7 0-32-14.3-32-32l0-80L64 48c-8.8 0-16 7.2-16 16l0 384c0 8.8 7.2 16 16 16l256 0zM0 64C0 28.7 28.7 0 64 0L229.5 0c17 0 33.3 6.7 45.3 18.7l90.5 90.5c12 12 18.7 28.3 18.7 45.3L384 448c0 35.3-28.7 64-64 64L64 512c-35.3 0-64-28.7-64-64L0 64z",
      VIEWBOX: "0 0 384 512",
    },
    BATCH: {
      PATH: "M48 272L48 64c0-8.8 7.2-16 16-16l160 0 0 80c0 17.7 14.3 32 32 32l80 0 0 112L48 272zm288 48l16 0 32 0 0-165.5c0-17-6.7-33.3-18.7-45.3L274.7 18.7C262.7 6.7 246.5 0 229.5 0L64 0C28.7 0 0 28.7 0 64L0 320l32 0 16 0 288 0zM0 352l0 64 48 0 0-64L0 352zM64 512l0-48c-8.8 0-16-7.2-16-16L0 448c0 35.3 28.7 64 64 64zm256-48l0 48c35.3 0 64-28.7 64-64l-48 0c0 8.8-7.2 16-16 16zm64-112l-48 0 0 64 48 0 0-64zM96 464l0 48 80 0 0-48-80 0zm112 0l0 48 80 0 0-48-80 0z",
      VIEWBOX: "0 0 384 512",
    },
    ZIP: {
      PATH: "M64 464c-8.8 0-16-7.2-16-16L48 64c0-8.8 7.2-16 16-16l48 0c0 8.8 7.2 16 16 16l32 0c8.8 0 16-7.2 16-16l48 0 0 80c0 17.7 14.3 32 32 32l80 0 0 288c0 8.8-7.2 16-16 16L64 464zM64 0C28.7 0 0 28.7 0 64L0 448c0 35.3 28.7 64 64 64l256 0c35.3 0 64-28.7 64-64l0-293.5c0-17-6.7-33.3-18.7-45.3L274.7 18.7C262.7 6.7 246.5 0 229.5 0L64 0zm48 112c0 8.8 7.2 16 16 16l32 0c8.8 0 16-7.2 16-16s-7.2-16-16-16l-32 0c-8.8 0-16 7.2-16 16zm0 64c0 8.8 7.2 16 16 16l32 0c8.8 0 16-7.2 16-16s-7.2-16-16-16l-32 0c-8.8 0-16 7.2-16 16zm-6.3 71.8L82.1 335.9c-1.4 5.4-2.1 10.9-2.1 16.4c0 35.2 28.8 63.7 64 63.7s64-28.5 64-63.7c0-5.5-.7-11.1-2.1-16.4l-23.5-88.2c-3.7-14-16.4-23.8-30.9-23.8l-14.8 0c-14.5 0-27.2 9.7-30.9 23.8zM128 336l32 0c8.8 0 16 7.2 16 16s-7.2 16-16 16l-32 0c-8.8 0-16-7.2-16-16s7.2-16 16-16z",
      VIEWBOX: "0 0 384 512",
    },
    MEDIA_TYPE: {
      ALL: {
        PATH: "M256 48c-8.8 0-16 7.2-16 16l0 224c0 8.7 6.9 15.8 15.6 16l69.1-94.2c4.5-6.2 11.7-9.8 19.4-9.8s14.8 3.6 19.4 9.8L380 232.4l56-85.6c4.4-6.8 12-10.9 20.1-10.9s15.7 4.1 20.1 10.9L578.7 303.8c7.6-1.3 13.3-7.9 13.3-15.8l0-224c0-8.8-7.2-16-16-16L256 48zM192 64c0-35.3 28.7-64 64-64L576 0c35.3 0 64 28.7 64 64l0 224c0 35.3-28.7 64-64 64l-320 0c-35.3 0-64-28.7-64-64l0-224zm-56 64l24 0 0 48 0 88 0 112 0 8 0 80 192 0 0-80 48 0 0 80 48 0c8.8 0 16-7.2 16-16l0-64 48 0 0 64c0 35.3-28.7 64-64 64l-48 0-24 0-24 0-192 0-24 0-24 0-48 0c-35.3 0-64-28.7-64-64L0 192c0-35.3 28.7-64 64-64l48 0 24 0zm-24 48l-48 0c-8.8 0-16 7.2-16 16l0 48 64 0 0-64zm0 288l0-64-64 0 0 48c0 8.8 7.2 16 16 16l48 0zM48 352l64 0 0-64-64 0 0 64zM304 80a32 32 0 1 1 0 64 32 32 0 1 1 0-64z",
        VIEWBOX: "0 0 640 512",
      },
      IMAGE: {
        PATH: "M448 80c8.8 0 16 7.2 16 16l0 319.8-5-6.5-136-176c-4.5-5.9-11.6-9.3-19-9.3s-14.4 3.4-19 9.3L202 340.7l-30.5-42.7C167 291.7 159.8 288 152 288s-15 3.7-19.5 10.1l-80 112L48 416.3l0-.3L48 96c0-8.8 7.2-16 16-16l384 0zM64 32C28.7 32 0 60.7 0 96L0 416c0 35.3 28.7 64 64 64l384 0c35.3 0 64-28.7 64-64l0-320c0-35.3-28.7-64-64-64L64 32zm80 192a48 48 0 1 0 0-96 48 48 0 1 0 0 96z",
        VIEWBOX: "0 0 512 512",
      },
      VIDEO: {
        PATH: "M352 432l-192 0 0-112 0-40 192 0 0 40 0 112zm0-200l-192 0 0-40 0-112 192 0 0 112 0 40zM64 80l48 0 0 88-64 0 0-72c0-8.8 7.2-16 16-16zM48 216l64 0 0 80-64 0 0-80zm64 216l-48 0c-8.8 0-16-7.2-16-16l0-72 64 0 0 88zM400 168l0-88 48 0c8.8 0 16 7.2 16 16l0 72-64 0zm0 48l64 0 0 80-64 0 0-80zm0 128l64 0 0 72c0 8.8-7.2 16-16 16l-48 0 0-88zM448 32L64 32C28.7 32 0 60.7 0 96L0 416c0 35.3 28.7 64 64 64l384 0c35.3 0 64-28.7 64-64l0-320c0-35.3-28.7-64-64-64z",
        VIEWBOX: "0 0 512 512",
      },
      GIF: {
        PATH: "M512 80c8.8 0 16 7.2 16 16l0 320c0 8.8-7.2 16-16 16L64 432c-8.8 0-16-7.2-16-16L48 96c0-8.8 7.2-16 16-16l448 0zM64 32C28.7 32 0 60.7 0 96L0 416c0 35.3 28.7 64 64 64l448 0c35.3 0 64-28.7 64-64l0-320c0-35.3-28.7-64-64-64L64 32zM296 160c-13.3 0-24 10.7-24 24l0 144c0 13.3 10.7 24 24 24s24-10.7 24-24l0-144c0-13.3-10.7-24-24-24zm56 24l0 80 0 64c0 13.3 10.7 24 24 24s24-10.7 24-24l0-40 40 0c13.3 0 24-10.7 24-24s-10.7-24-24-24l-40 0 0-32 64 0c13.3 0 24-10.7 24-24s-10.7-24-24-24l-88 0c-13.3 0-24 10.7-24 24zM128 256c0-26.5 21.5-48 48-48c8 0 15.4 1.9 22 5.3c11.8 6.1 26.3 1.5 32.3-10.3s1.5-26.3-10.3-32.3c-13.2-6.8-28.2-10.7-44-10.7c-53 0-96 43-96 96s43 96 96 96c19.6 0 37.5-6.1 52.8-15.8c7-4.4 11.2-12.1 11.2-20.3l0-51.9c0-13.3-10.7-24-24-24l-32 0c-13.3 0-24 10.7-24 24s10.7 24 24 24l8 0 0 13.1c-5.3 1.9-10.6 2.9-16 2.9c-26.5 0-48-21.5-48-48z",
        VIEWBOX: "0 0 576 512",
      },
      MEDIA: {
        PATH: "M160 80l352 0c8.8 0 16 7.2 16 16l0 224c0 8.8-7.2 16-16 16l-21.2 0L388.1 178.9c-4.4-6.8-12-10.9-20.1-10.9s-15.7 4.1-20.1 10.9l-52.2 79.8-12.4-16.9c-4.5-6.2-11.7-9.8-19.4-9.8s-14.8 3.6-19.4 9.8L175.6 336 160 336c-8.8 0-16-7.2-16-16l0-224c0-8.8 7.2-16 16-16zM96 96l0 224c0 35.3 28.7 64 64 64l352 0c35.3 0 64-28.7 64-64l0-224c0-35.3-28.7-64-64-64L160 32c-35.3 0-64 28.7-64 64zM48 120c0-13.3-10.7-24-24-24S0 106.7 0 120L0 344c0 75.1 60.9 136 136 136l320 0c13.3 0 24-10.7 24-24s-10.7-24-24-24l-320 0c-48.6 0-88-39.4-88-88l0-224zm208 24a32 32 0 1 0 -64 0 32 32 0 1 0 64 0z",
        VIEWBOX: "0 0 576 512",
      },
      POST: {
        PATH: "M168 80c-13.3 0-24 10.7-24 24l0 304c0 8.4-1.4 16.5-4.1 24L440 432c13.3 0 24-10.7 24-24l0-304c0-13.3-10.7-24-24-24L168 80zM72 480c-39.8 0-72-32.2-72-72L0 112C0 98.7 10.7 88 24 88s24 10.7 24 24l0 296c0 13.3 10.7 24 24 24s24-10.7 24-24l0-304c0-39.8 32.2-72 72-72l272 0c39.8 0 72 32.2 72 72l0 304c0 39.8-32.2 72-72 72L72 480zM176 136c0-13.3 10.7-24 24-24l96 0c13.3 0 24 10.7 24 24l0 80c0 13.3-10.7 24-24 24l-96 0c-13.3 0-24-10.7-24-24l0-80zm200-24l32 0c13.3 0 24 10.7 24 24s-10.7 24-24 24l-32 0c-13.3 0-24-10.7-24-24s10.7-24 24-24zm0 80l32 0c13.3 0 24 10.7 24 24s-10.7 24-24 24l-32 0c-13.3 0-24-10.7-24-24s10.7-24 24-24zM200 272l208 0c13.3 0 24 10.7 24 24s-10.7 24-24 24l-208 0c-13.3 0-24-10.7-24-24s10.7-24 24-24zm0 80l208 0c13.3 0 24 10.7 24 24s-10.7 24-24 24l-208 0c-13.3 0-24-10.7-24-24s10.7-24 24-24z",
        VIEWBOX: "0 0 512 512",
      },
      TWEETS: {
        PATH: "M88.2 309.1c9.8-18.3 6.8-40.8-7.5-55.8C59.4 230.9 48 204 48 176c0-63.5 63.8-128 160-128s160 64.5 160 128s-63.8 128-160 128c-13.1 0-25.8-1.3-37.8-3.6c-10.4-2-21.2-.6-30.7 4.2c-4.1 2.1-8.3 4.1-12.6 6c-16 7.2-32.9 13.5-49.9 18c2.8-4.6 5.4-9.1 7.9-13.6c1.1-1.9 2.2-3.9 3.2-5.9zM208 352c114.9 0 208-78.8 208-176S322.9 0 208 0S0 78.8 0 176c0 41.8 17.2 80.1 45.9 110.3c-.9 1.7-1.9 3.5-2.8 5.1c-10.3 18.4-22.3 36.5-36.6 52.1c-6.6 7-8.3 17.2-4.6 25.9C5.8 378.3 14.4 384 24 384c43 0 86.5-13.3 122.7-29.7c4.8-2.2 9.6-4.5 14.2-6.8c15.1 3 30.9 4.5 47.1 4.5zM432 480c16.2 0 31.9-1.6 47.1-4.5c4.6 2.3 9.4 4.6 14.2 6.8C529.5 498.7 573 512 616 512c9.6 0 18.2-5.7 22-14.5c3.8-8.8 2-19-4.6-25.9c-14.2-15.6-26.2-33.7-36.6-52.1c-.9-1.7-1.9-3.4-2.8-5.1C622.8 384.1 640 345.8 640 304c0-94.4-87.9-171.5-198.2-175.8c4.1 15.2 6.2 31.2 6.2 47.8l0 .6c87.2 6.7 144 67.5 144 127.4c0 28-11.4 54.9-32.7 77.2c-14.3 15-17.3 37.6-7.5 55.8c1.1 2 2.2 4 3.2 5.9c2.5 4.5 5.2 9 7.9 13.6c-17-4.5-33.9-10.7-49.9-18c-4.3-1.9-8.5-3.9-12.6-6c-9.5-4.8-20.3-6.2-30.7-4.2c-12.1 2.4-24.8 3.6-37.8 3.6c-61.7 0-110-26.5-136.8-62.3c-16 5.4-32.8 9.4-50 11.8C279 439.8 350 480 432 480z",
        VIEWBOX: "0 0 640 512",
      },
      REPLIES: {
        PATH: "M224 240l96 0c66.2 0 122 44.7 138.8 105.5c3.3-12.4 5.2-26.2 5.2-41.5c0-70.7-57.3-128-128-128l-112 0-24 0c-13.3 0-24-10.7-24-24l0-24 0-28.1L55.9 208 176 316.1l0-28.1 0-24c0-13.3 10.7-24 24-24l24 0zm0 48l0 48 0 16c0 12.6-7.4 24.1-19 29.2s-25 3-34.4-5.4l-160-144C3.9 225.7 0 217.1 0 208s3.9-17.7 10.6-23.8l160-144c9.4-8.5 22.9-10.6 34.4-5.4s19 16.6 19 29.2l0 16 0 48 48 0 64 0c97.2 0 176 78.8 176 176c0 78-38.6 126.2-68.7 152.1c-4.1 3.5-8.1 6.6-11.7 9.3c-3.2 2.4-6.2 4.4-8.9 6.2c-4.5 3-8.3 5.1-10.8 6.5c-2.5 1.4-5.3 1.9-8.1 1.9c-10.9 0-19.7-8.9-19.7-19.7c0-6.8 3.6-13.2 8.3-18.1c.5-.5 .9-.9 1.4-1.4c2.4-2.3 5.1-5.1 7.7-8.6c1.7-2.3 3.4-5 5-7.9c5.3-9.7 9.5-22.9 9.5-40.2c0-53-43-96-96-96l-48 0-48 0z",
        VIEWBOX: "0 0 512 512",
      },
    },
  };

  const defaultSettings = {
    patreonAuth: "",
    authToken: "",
    batchEnabled: false,
    autoBatchEnabled: false,
    batchSize: 100,
    startingBatch: 0,
    timelineType: "media",
    mediaType: "all",
    concurrentDownloads: 50,
    cacheDuration: 360,
    apiServer: "default",
    darkTheme: false,
  };
  const batchSizes = [25, 50, 100, 200];
  const cacheDurations = [60, 120, 180, 240, 300, 360, 720, 1440];

  function getSettings() {
    return {
      patreonAuth: GM_getValue("patreonAuth", defaultSettings.patreonAuth),
      authToken: GM_getValue("authToken", defaultSettings.authToken),
      batchEnabled: GM_getValue("batchEnabled", defaultSettings.batchEnabled),
      autoBatchEnabled: GM_getValue(
        "autoBatchEnabled",
        defaultSettings.autoBatchEnabled
      ),
      batchSize: GM_getValue("batchSize", defaultSettings.batchSize),
      startingBatch: GM_getValue(
        "startingBatch",
        defaultSettings.startingBatch
      ),
      timelineType: GM_getValue("timelineType", defaultSettings.timelineType),
      mediaType: GM_getValue("mediaType", defaultSettings.mediaType),
      concurrentDownloads: GM_getValue(
        "concurrentDownloads",
        defaultSettings.concurrentDownloads
      ),
      cacheDuration: GM_getValue(
        "cacheDuration",
        defaultSettings.cacheDuration
      ),
      apiServer: GM_getValue("apiServer", defaultSettings.apiServer),
      darkTheme: GM_getValue("darkTheme", defaultSettings.darkTheme),
    };
  }

  function saveSettings(settings) {
    GM_setValue("patreonAuth", settings.patreonAuth);
    GM_setValue("authToken", settings.authToken);
    GM_setValue("batchEnabled", settings.batchEnabled);
    GM_setValue("autoBatchEnabled", settings.autoBatchEnabled);
    GM_setValue("batchSize", settings.batchSize);
    GM_setValue("startingBatch", settings.startingBatch);
    GM_setValue("timelineType", settings.timelineType);
    GM_setValue("mediaType", settings.mediaType);
    GM_setValue("concurrentDownloads", settings.concurrentDownloads);
    GM_setValue("cacheDuration", settings.cacheDuration);
    GM_setValue("apiServer", settings.apiServer);
    GM_setValue("darkTheme", settings.darkTheme);
  }

  function getServiceBaseUrl() {
    const settings = getSettings();
    return settings.apiServer === "default"
      ? API_URLS.DEFAULT
      : API_URLS.BACKUP;
  }

  function formatNumber(num) {
    return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
  }

  const cacheManager = {
    set: (key, data, success = true) => {
      if (!success) return;

      const settings = getSettings();
      const cacheItem = {
        data: data,
        timestamp: Date.now(),
        expiry: Date.now() + settings.cacheDuration * 60 * 1000,
      };
      localStorage.setItem(`twitter_dl_${key}`, JSON.stringify(cacheItem));
    },

    get: (key) => {
      const cacheItem = localStorage.getItem(`twitter_dl_${key}`);
      if (!cacheItem) return null;

      try {
        const parsed = JSON.parse(cacheItem);
        if (Date.now() > parsed.expiry) {
          localStorage.removeItem(`twitter_dl_${key}`);
          return null;
        }
        return parsed.data;
      } catch (e) {
        localStorage.removeItem(`twitter_dl_${key}`);
        return null;
      }
    },

    clear: () => {
      const keysToRemove = [];
      for (let i = 0; i < localStorage.length; i++) {
        const key = localStorage.key(i);
        if (key.startsWith("twitter_dl_")) {
          keysToRemove.push(key);
        }
      }

      keysToRemove.forEach((key) => localStorage.removeItem(key));
    },
  };

  function createDownloadIcon() {
    const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
    svg.setAttribute("xmlns", "http://www.w3.org/2000/svg");
    svg.setAttribute("viewBox", ICONS.DOWNLOAD.VIEWBOX);
    svg.setAttribute("width", "18");
    svg.setAttribute("height", "18");
    svg.style.verticalAlign = "middle";
    svg.style.cursor = "pointer";

    const defs = document.createElementNS("http://www.w3.org/2000/svg", "defs");
    const style = document.createElementNS(
      "http://www.w3.org/2000/svg",
      "style"
    );
    style.textContent = ".fa-secondary{opacity:.4}";
    defs.appendChild(style);
    svg.appendChild(defs);

    const secondaryPath = document.createElementNS(
      "http://www.w3.org/2000/svg",
      "path"
    );
    secondaryPath.setAttribute("class", "fa-secondary");
    secondaryPath.setAttribute("fill", "currentColor");
    secondaryPath.setAttribute("d", ICONS.DOWNLOAD.SECONDARY_PATH);
    svg.appendChild(secondaryPath);

    const primaryPath = document.createElementNS(
      "http://www.w3.org/2000/svg",
      "path"
    );
    primaryPath.setAttribute("class", "fa-primary");
    primaryPath.setAttribute("fill", "currentColor");
    primaryPath.setAttribute("d", ICONS.DOWNLOAD.PRIMARY_PATH);
    svg.appendChild(primaryPath);

    return svg;
  }

  function createPatreonIcon() {
    const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
    svg.setAttribute("xmlns", "http://www.w3.org/2000/svg");
    svg.setAttribute("viewBox", ICONS.PATREON.VIEWBOX);
    svg.setAttribute("width", "18");
    svg.setAttribute("height", "18");
    svg.style.verticalAlign = "middle";
    svg.style.marginRight = "8px";

    const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
    path.setAttribute("fill", "currentColor");
    path.setAttribute("d", ICONS.PATREON.PATH);
    svg.appendChild(path);

    return svg;
  }

  function createInfoIcon() {
    const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
    svg.setAttribute("xmlns", "http://www.w3.org/2000/svg");
    svg.setAttribute("viewBox", ICONS.INFO.VIEWBOX);
    svg.setAttribute("width", "16");
    svg.setAttribute("height", "16");
    svg.style.marginLeft = "8px";
    svg.style.cursor = "pointer";
    svg.style.color = "#64748b";
    svg.style.transition = "color 0.2s ease";

    const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
    path.setAttribute("fill", "currentColor");
    path.setAttribute("d", ICONS.INFO.PATH);
    svg.appendChild(path);

    svg.addEventListener("mouseenter", () => {
      svg.style.color = "#0ea5e9";
    });

    svg.addEventListener("mouseleave", () => {
      svg.style.color = "#64748b";
    });

    return svg;
  }

  function createInfoTooltip(message) {
    let activeTooltip = null;

    function showTooltip(e) {
      if (activeTooltip) {
        document.body.removeChild(activeTooltip);
      }

      const tooltip = document.createElement("div");
      tooltip.textContent = message;
      tooltip.style.cssText = `
        position: fixed;
        background-color: #1f2937;
        color: white;
        padding: 8px 12px;
        border-radius: 6px;
        font-size: 12px;
        line-height: 1.4;
        max-width: 300px;
        z-index: 10003;
        box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
        pointer-events: none;
        white-space: normal;
        word-wrap: break-word;
      `;

      document.body.appendChild(tooltip);
      activeTooltip = tooltip;

      const rect = e.target.getBoundingClientRect();
      const tooltipRect = tooltip.getBoundingClientRect();

      let top = rect.bottom + 8;
      let left = rect.left + rect.width / 2 - tooltipRect.width / 2;

      if (left < 8) left = 8;
      if (left + tooltipRect.width > window.innerWidth - 8) {
        left = window.innerWidth - tooltipRect.width - 8;
      }
      if (top + tooltipRect.height > window.innerHeight - 8) {
        top = rect.top - tooltipRect.height - 8;
      }

      tooltip.style.top = top + "px";
      tooltip.style.left = left + "px";
    }

    function hideTooltip() {
      if (activeTooltip) {
        document.body.removeChild(activeTooltip);
        activeTooltip = null;
      }
    }

    return { showTooltip, hideTooltip };
  }

  function createAccountIcon(isDarkTheme = false) {
    const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
    svg.setAttribute("xmlns", "http://www.w3.org/2000/svg");
    svg.setAttribute("viewBox", ICONS.ACCOUNT.VIEWBOX);
    svg.setAttribute("width", "12");
    svg.setAttribute("height", "12");
    svg.style.marginRight = "6px";
    svg.style.color = isDarkTheme ? "#ffffff" : "#64748b";
    svg.style.verticalAlign = "middle";
    svg.style.display = "inline-block";

    const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
    path.setAttribute("fill", "currentColor");
    path.setAttribute("d", ICONS.ACCOUNT.PATH);
    svg.appendChild(path);

    return svg;
  }

  function createTotalItemsIcon(isDarkTheme = false) {
    const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
    svg.setAttribute("xmlns", "http://www.w3.org/2000/svg");
    svg.setAttribute("viewBox", ICONS.TOTAL.VIEWBOX);
    svg.setAttribute("width", "12");
    svg.setAttribute("height", "12");
    svg.style.marginRight = "6px";
    svg.style.color = isDarkTheme ? "#ffffff" : "#64748b";
    svg.style.verticalAlign = "middle";
    svg.style.display = "inline-block";

    const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
    path.setAttribute("fill", "currentColor");
    path.setAttribute("d", ICONS.TOTAL.PATH);
    svg.appendChild(path);

    return svg;
  }

  function createCurrentBatchIcon(isDarkTheme = false) {
    const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
    svg.setAttribute("xmlns", "http://www.w3.org/2000/svg");
    svg.setAttribute("viewBox", ICONS.BATCH.VIEWBOX);
    svg.setAttribute("width", "12");
    svg.setAttribute("height", "12");
    svg.style.marginRight = "6px";
    svg.style.color = isDarkTheme ? "#ffffff" : "#64748b";
    svg.style.verticalAlign = "middle";
    svg.style.display = "inline-block";

    const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
    path.setAttribute("fill", "currentColor");
    path.setAttribute("d", ICONS.BATCH.PATH);
    svg.appendChild(path);

    return svg;
  }

  function createTotalZipIcon(isDarkTheme = false) {
    const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
    svg.setAttribute("xmlns", "http://www.w3.org/2000/svg");
    svg.setAttribute("viewBox", ICONS.ZIP.VIEWBOX);
    svg.setAttribute("width", "12");
    svg.setAttribute("height", "12");
    svg.style.marginRight = "6px";
    svg.style.color = isDarkTheme ? "#ffffff" : "#64748b";
    svg.style.verticalAlign = "middle";
    svg.style.display = "inline-block";

    const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
    path.setAttribute("fill", "currentColor");
    path.setAttribute("d", ICONS.ZIP.PATH);
    svg.appendChild(path);

    return svg;
  }

  function createMediaTypeIcon(mediaType, isDarkTheme = false) {
    let iconData;
    switch (mediaType) {
      case "image":
        iconData = ICONS.MEDIA_TYPE.IMAGE;
        break;
      case "video":
        iconData = ICONS.MEDIA_TYPE.VIDEO;
        break;
      case "gif":
        iconData = ICONS.MEDIA_TYPE.GIF;
        break;
      default:
        iconData = ICONS.MEDIA_TYPE.ALL;
    }

    const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
    svg.setAttribute("xmlns", "http://www.w3.org/2000/svg");
    svg.setAttribute("viewBox", iconData.VIEWBOX);
    svg.setAttribute("width", "12");
    svg.setAttribute("height", "12");
    svg.style.marginRight = "6px";
    svg.style.color = isDarkTheme ? "#ffffff" : "#64748b";
    svg.style.verticalAlign = "middle";
    svg.style.display = "inline-block";

    const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
    path.setAttribute("fill", "currentColor");
    path.setAttribute("d", iconData.PATH);
    svg.appendChild(path);

    return svg;
  }

  function createTimelineTypeIcon(timelineType, isDarkTheme = false) {
    let iconData;
    switch (timelineType) {
      case "media":
        iconData = ICONS.MEDIA_TYPE.MEDIA;
        break;
      case "timeline":
        iconData = ICONS.MEDIA_TYPE.POST;
        break;
      case "tweets":
        iconData = ICONS.MEDIA_TYPE.TWEETS;
        break;
      case "with_replies":
        iconData = ICONS.MEDIA_TYPE.REPLIES;
        break;
      default:
        iconData = ICONS.MEDIA_TYPE.MEDIA;
    }

    const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
    svg.setAttribute("xmlns", "http://www.w3.org/2000/svg");
    svg.setAttribute("viewBox", iconData.VIEWBOX);
    svg.setAttribute("width", "16");
    svg.setAttribute("height", "16");
    svg.style.marginRight = "6px";
    svg.style.color = isDarkTheme ? "#ffffff" : "#64748b";
    svg.style.verticalAlign = "middle";
    svg.style.display = "inline-block";

    const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
    path.setAttribute("fill", "currentColor");
    path.setAttribute("d", iconData.PATH);
    svg.appendChild(path);

    return svg;
  }

  function createAuthTokenPopup() {
    const settings = getSettings();
    const overlay = document.createElement("div");
    overlay.style.cssText = `
        position: fixed;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        background-color: rgba(0, 0, 0, 0.35);
        backdrop-filter: blur(2.5px);
        display: flex;
        justify-content: center;
        align-items: center;
        z-index: 10001;
        font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
    `;

    const popup = document.createElement("div");
    popup.style.cssText = `
        background-color: ${settings.darkTheme ? "#1f2937" : "#ffffff"};
        color: ${settings.darkTheme ? "#f1f5f9" : "#0f172a"};
        border-radius: 16px;
        width: 300px;
        max-width: 90%;
        box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
        overflow: hidden;
    `;

    const header = document.createElement("div");
    header.style.cssText = `
        padding: 16px;
        border-bottom: 1px solid ${settings.darkTheme ? "#374151" : "#e2e8f0"};
        font-weight: bold;
        font-size: 16px;
        text-align: center;
        background-color: ${settings.darkTheme ? "#374151" : "#f8fafc"};
    `;
    header.textContent = "Authentication Required";

    const content = document.createElement("div");
    content.style.cssText = `
        padding: 16px;
        text-align: center;
    `;

    const authLink = document.createElement("a");
    authLink.href = "https://www.patreon.com/posts/127206894";
    authLink.target = "_blank";
    authLink.textContent = "How to Obtain Auth Token";
    authLink.style.cssText = `
        color: #0ea5e9;
        text-decoration: none;
        cursor: pointer;
    `;
    content.appendChild(authLink);

    const buttonContainer = document.createElement("div");
    buttonContainer.style.cssText = `
        padding: 16px;
        display: flex;
        justify-content: center;
        border-top: 1px solid ${settings.darkTheme ? "#374151" : "#e2e8f0"};
    `;

    const okButton = document.createElement("button");
    okButton.style.cssText = `
        background-color: #0ea5e9;
        color: white;
        border: none;
        border-radius: 6px;
        padding: 8px 24px;
        font-weight: bold;
        cursor: pointer;
        transition: background-color 0.2s;
    `;
    okButton.textContent = "OK";
    okButton.addEventListener("mouseenter", () => {
      okButton.style.backgroundColor = "#0284c7";
    });
    okButton.addEventListener("mouseleave", () => {
      okButton.style.backgroundColor = "#0ea5e9";
    });
    okButton.onclick = () => {
      document.body.removeChild(overlay);
    };

    buttonContainer.appendChild(okButton);
    popup.appendChild(header);
    popup.appendChild(content);
    popup.appendChild(buttonContainer);
    overlay.appendChild(popup);
    document.body.appendChild(overlay);
    return overlay;
  }

  function createPatreonAuthPopup() {
    const settings = getSettings();
    const overlay = document.createElement("div");
    overlay.style.cssText = `
        position: fixed;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        background-color: rgba(0, 0, 0, 0.35);
        backdrop-filter: blur(2.5px);
        display: flex;
        justify-content: center;
        align-items: center;
        z-index: 10001;
        font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
    `;

    const popup = document.createElement("div");
    popup.style.cssText = `
        background-color: ${settings.darkTheme ? "#1f2937" : "#ffffff"};
        color: ${settings.darkTheme ? "#f1f5f9" : "#0f172a"};
        border-radius: 16px;
        width: 320px;
        max-width: 90%;
        box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
        overflow: hidden;
    `;

    const header = document.createElement("div");
    header.style.cssText = `
        padding: 16px;
        border-bottom: 1px solid ${settings.darkTheme ? "#374151" : "#e2e8f0"};
        font-weight: bold;
        font-size: 16px;
        text-align: center;
        background-color: ${settings.darkTheme ? "#374151" : "#f8fafc"};
    `;
    header.textContent = "Patreon Authentication Required";

    const content = document.createElement("div");
    content.style.cssText = `
        padding: 16px;
        text-align: center;
    `;

    const message = document.createElement("p");
    message.style.cssText = `
        margin-bottom: 16px;
        line-height: 1.5;
    `;
    message.textContent =
      "Please enter your Patreon authentication code. This feature requires a paid membership to access.";
    content.appendChild(message);

    const patreonButton = document.createElement("a");
    patreonButton.href = "https://www.patreon.com/exyezed";
    patreonButton.target = "_blank";
    patreonButton.style.cssText = `
        display: flex;
        align-items: center;
        justify-content: center;
        background-color: ${settings.darkTheme ? "#374151" : "#f1f5f9"};
        color: ${settings.darkTheme ? "#f1f5f9" : "#0f172a"};
        text-decoration: none;
        padding: 10px 16px;
        border-radius: 8px;
        margin-top: 8px;
        transition: background-color 0.2s;
    `;
    patreonButton.innerHTML =
      createPatreonIcon().outerHTML + "Join Patreon Membership";
    patreonButton.addEventListener("mouseenter", () => {
      patreonButton.style.backgroundColor = settings.darkTheme
        ? "#4b5563"
        : "#e2e8f0";
    });
    patreonButton.addEventListener("mouseleave", () => {
      patreonButton.style.backgroundColor = settings.darkTheme
        ? "#374151"
        : "#f1f5f9";
    });
    content.appendChild(patreonButton);

    const buttonContainer = document.createElement("div");
    buttonContainer.style.cssText = `
        padding: 16px;
        display: flex;
        justify-content: center;
        border-top: 1px solid ${settings.darkTheme ? "#374151" : "#e2e8f0"};
    `;

    const okButton = document.createElement("button");
    okButton.style.cssText = `
        background-color: #0ea5e9;
        color: white;
        border: none;
        border-radius: 6px;
        padding: 8px 24px;
        font-weight: bold;
        cursor: pointer;
        transition: background-color 0.2s;
    `;
    okButton.textContent = "OK";
    okButton.addEventListener("mouseenter", () => {
      okButton.style.backgroundColor = "#0284c7";
    });
    okButton.addEventListener("mouseleave", () => {
      okButton.style.backgroundColor = "#0ea5e9";
    });
    okButton.onclick = () => {
      document.body.removeChild(overlay);
    };

    buttonContainer.appendChild(okButton);
    popup.appendChild(header);
    popup.appendChild(content);
    popup.appendChild(buttonContainer);
    overlay.appendChild(popup);

    document.body.appendChild(overlay);
    return overlay;
  }

  function createInfoDialog(message, title = "Information") {
    const settings = getSettings();
    const overlay = document.createElement("div");
    overlay.style.cssText = `
        position: fixed;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        background-color: rgba(0, 0, 0, 0.35);
        backdrop-filter: blur(2.5px);
        display: flex;
        justify-content: center;
        align-items: center;
        z-index: 10001;
        font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
    `;

    const dialog = document.createElement("div");
    dialog.style.cssText = `
        background-color: ${settings.darkTheme ? "#1f2937" : "#ffffff"};
        color: ${settings.darkTheme ? "#f1f5f9" : "#334155"};
        border-radius: 16px;
        width: 300px;
        max-width: 90%;
        box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
        overflow: hidden;
    `;

    const header = document.createElement("div");
    header.style.cssText = `
        padding: 16px;
        border-bottom: 1px solid ${settings.darkTheme ? "#374151" : "#e2e8f0"};
        font-weight: bold;
        font-size: 16px;
        text-align: center;
        background-color: ${settings.darkTheme ? "#374151" : "#f8fafc"};
    `;
    header.textContent = title;

    const content = document.createElement("div");
    content.style.cssText = `
        padding: 20px 16px;
        text-align: center;
        line-height: 1.5;
    `;
    content.textContent = message;

    const buttonContainer = document.createElement("div");
    buttonContainer.style.cssText = `
        padding: 16px;
        display: flex;
        justify-content: center;
        border-top: 1px solid ${settings.darkTheme ? "#374151" : "#e2e8f0"};
    `;

    const okButton = document.createElement("button");
    okButton.style.cssText = `
        background-color: #22c55e;
        color: white;
        border: none;
        border-radius: 6px;
        padding: 8px 24px;
        font-weight: bold;
        cursor: pointer;
        transition: background-color 0.2s;
    `;
    okButton.textContent = "OK";
    okButton.addEventListener("mouseenter", () => {
      okButton.style.backgroundColor = "#16a34a";
    });
    okButton.addEventListener("mouseleave", () => {
      okButton.style.backgroundColor = "#22c55e";
    });
    okButton.onclick = () => {
      document.body.removeChild(overlay);
    };

    buttonContainer.appendChild(okButton);
    dialog.appendChild(header);
    dialog.appendChild(content);
    dialog.appendChild(buttonContainer);
    overlay.appendChild(dialog);

    document.body.appendChild(overlay);
    return overlay;
  }

  function createConfirmDialog(message, onConfirm, onCancel) {
    const settings = getSettings();
    const overlay = document.createElement("div");
    overlay.style.cssText = `
        position: fixed;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        background-color: rgba(0, 0, 0, 0.35);
        backdrop-filter: blur(2.5px);
        display: flex;
        justify-content: center;
        align-items: center;
        z-index: 10001;
        font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
    `;

    const dialog = document.createElement("div");
    dialog.style.cssText = `
        background-color: ${settings.darkTheme ? "#1f2937" : "#ffffff"};
        color: ${settings.darkTheme ? "#f1f5f9" : "#334155"};
        border-radius: 16px;
        width: 300px;
        max-width: 90%;
        box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
        overflow: hidden;
    `;

    const header = document.createElement("div");
    header.style.cssText = `
        padding: 16px;
        border-bottom: 1px solid ${settings.darkTheme ? "#374151" : "#e2e8f0"};
        font-weight: bold;
        font-size: 16px;
        text-align: center;
        background-color: ${settings.darkTheme ? "#374151" : "#f8fafc"};
    `;
    header.textContent = "Confirmation";

    const content = document.createElement("div");
    content.style.cssText = `
        padding: 16px;
        text-align: center;
    `;
    content.textContent = message;

    const buttons = document.createElement("div");
    buttons.style.cssText = `
        display: flex;
        padding: 16px;
        border-top: 1px solid ${settings.darkTheme ? "#374151" : "#e2e8f0"};
    `;

    const cancelButton = document.createElement("button");
    cancelButton.style.cssText = `
        flex: 1;
        background-color: ${settings.darkTheme ? "#374151" : "#94a3b8"};
        color: white;
        border: none;
        border-radius: 6px;
        padding: 8px 16px;
        margin-right: 8px;
        font-weight: bold;
        cursor: pointer;
        text-align: center;
        transition: background-color 0.2s;
    `;
    cancelButton.textContent = "No";
    cancelButton.addEventListener("mouseenter", () => {
      cancelButton.style.backgroundColor = settings.darkTheme
        ? "#4b5563"
        : "#64748b";
    });
    cancelButton.addEventListener("mouseleave", () => {
      cancelButton.style.backgroundColor = settings.darkTheme
        ? "#374151"
        : "#94a3b8";
    });
    cancelButton.onclick = () => {
      document.body.removeChild(overlay);
      if (onCancel) onCancel();
    };

    const confirmButton = document.createElement("button");
    confirmButton.style.cssText = `
        flex: 1;
        background-color: #ef4444;
        color: white;
        border: none;
        border-radius: 6px;
        padding: 8px 16px;
        font-weight: bold;
        cursor: pointer;
        text-align: center;
        transition: background-color 0.2s;
    `;
    confirmButton.textContent = "Yes";
    confirmButton.addEventListener("mouseenter", () => {
      confirmButton.style.backgroundColor = "#dc2626";
    });
    confirmButton.addEventListener("mouseleave", () => {
      confirmButton.style.backgroundColor = "#ef4444";
    });
    confirmButton.onclick = () => {
      document.body.removeChild(overlay);
      if (onConfirm) onConfirm();
    };

    buttons.appendChild(cancelButton);
    buttons.appendChild(confirmButton);

    dialog.appendChild(header);
    dialog.appendChild(content);
    dialog.appendChild(buttons);
    overlay.appendChild(dialog);

    document.body.appendChild(overlay);
  }

  function formatDate(dateString) {
    const date = new Date(dateString);
    const year = date.getFullYear();
    const month = String(date.getMonth() + 1).padStart(2, "0");
    const day = String(date.getDate()).padStart(2, "0");
    const hours = String(date.getHours()).padStart(2, "0");
    const minutes = String(date.getMinutes()).padStart(2, "0");
    const seconds = String(date.getSeconds()).padStart(2, "0");

    return `${year}${month}${day}_${hours}${minutes}${seconds}`;
  }

  function getCurrentTimestamp() {
    const now = new Date();
    const year = now.getFullYear();
    const month = String(now.getMonth() + 1).padStart(2, "0");
    const day = String(now.getDate()).padStart(2, "0");
    const hours = String(now.getHours()).padStart(2, "0");
    const minutes = String(now.getMinutes()).padStart(2, "0");
    const seconds = String(now.getSeconds()).padStart(2, "0");

    return `${year}${month}${day}_${hours}${minutes}${seconds}`;
  }

  function fetchData(url) {
    return new Promise((resolve, reject) => {
      GM_xmlhttpRequest({
        method: "GET",
        url: url,
        responseType: "json",
        onload: (response) => {
          if (response.status >= 200 && response.status < 300) {
            resolve(response.response);
          } else {
            reject(new Error(`Request failed with status ${response.status}`));
          }
        },
        onerror: (error) => {
          reject(
            new Error(`Network error: ${error?.message || "Unknown error"}`)
          );
        },
        ontimeout: () => {
          reject(new Error("Request timed out"));
        },
      });
    });
  }

  function fetchBinary(url) {
    return new Promise((resolve, reject) => {
      GM_xmlhttpRequest({
        method: "GET",
        url: url,
        responseType: "blob",
        onload: (response) => {
          if (response.status >= 200 && response.status < 300) {
            resolve(response.response);
          } else {
            reject(new Error(`Request failed with status ${response.status}`));
          }
        },
        onerror: () => {
          reject(new Error("Network error"));
        },
      });
    });
  }

  function getMediaTypeLabel(mediaType) {
    switch (mediaType) {
      case "image":
        return "Image";
      case "video":
        return "Video";
      case "gif":
        return "GIF";
      default:
        return "Media";
    }
  }

  function createToggleSwitch(options, selectedValue, onChange) {
    const settings = getSettings();
    const toggleWrapper = document.createElement("div");
    toggleWrapper.style.cssText = `
      position: relative;
      height: 40px;
      background-color: ${settings.darkTheme ? "#374151" : "#f1f5f9"};
      border-radius: 8px;
      padding: 0;
      cursor: pointer;
      width: 100%;
      margin-bottom: 16px;
      overflow: hidden;
    `;

    const toggleSlider = document.createElement("div");
    toggleSlider.style.cssText = `
      position: absolute;
      height: 100%;
      background-color: #0ea5e9;
      border-radius: 8px;
      transition: transform 0.3s ease, width 0.3s ease;
      z-index: 1;
    `;

    const optionsContainer = document.createElement("div");
    optionsContainer.style.cssText = `
      position: relative;
      display: flex;
      height: 100%;
      z-index: 2;
      width: 100%;
    `;

    const selectedIndex = options.findIndex(
      (option) => option.value === selectedValue
    );
    const optionWidth = 100 / options.length;
    toggleSlider.style.width = `${optionWidth}%`;
    toggleSlider.style.transform = `translateX(${selectedIndex * 100}%)`;
    options.forEach((option, index) => {
      const optionElement = document.createElement("div");
      optionElement.style.cssText = `
        flex: 1;
        display: flex;
        align-items: center;
        justify-content: center;
        font-size: 14px;
        transition: color 0.3s ease;
        color: ${option.value === selectedValue ? "white" : "#64748b"};
        cursor: pointer;
        user-select: none;
        text-align: center;
        height: 100%;
        padding: 0 4px;
      `;

      if (option.icon) {
        const iconContainer = document.createElement("span");
        iconContainer.style.cssText = `
          display: flex;
          align-items: center;
          justify-content: center;
          margin-right: 6px;
        `;

        const iconClone = option.icon.cloneNode(true);

        const paths = iconClone.querySelectorAll("path");
        paths.forEach((path) => {
          path.setAttribute(
            "fill",
            option.value === selectedValue ? "white" : "#64748b"
          );
        });

        iconContainer.appendChild(iconClone);
        optionElement.appendChild(iconContainer);
      }

      const text = document.createElement("span");
      text.textContent = option.label;
      text.style.cssText = `
        display: inline-block;
        text-align: center;
      `;
      optionElement.appendChild(text);

      optionElement.addEventListener("click", (e) => {
        e.stopPropagation();
        onChange(option.value);

        toggleSlider.style.transform = `translateX(${index * 100}%)`;

        optionsContainer.querySelectorAll("div").forEach((opt, i) => {
          opt.style.color = i === index ? "white" : "#64748b";

          const optIcon = opt.querySelector("svg");
          if (optIcon) {
            const optPaths = optIcon.querySelectorAll("path");
            optPaths.forEach((path) => {
              path.setAttribute("fill", i === index ? "white" : "#64748b");
            });
          }
        });
      });

      optionsContainer.appendChild(optionElement);
    });

    toggleWrapper.appendChild(toggleSlider);
    toggleWrapper.appendChild(optionsContainer);

    return toggleWrapper;
  }

  function createMediaTypeIcons() {
    const allIcon = document.createElementNS(
      "http://www.w3.org/2000/svg",
      "svg"
    );
    allIcon.setAttribute("xmlns", "http://www.w3.org/2000/svg");
    allIcon.setAttribute("viewBox", ICONS.MEDIA_TYPE.ALL.VIEWBOX);
    allIcon.setAttribute("width", "16");
    allIcon.setAttribute("height", "16");
    allIcon.style.verticalAlign = "middle";

    const allPath = document.createElementNS(
      "http://www.w3.org/2000/svg",
      "path"
    );
    allPath.setAttribute("fill", "#64748b");
    allPath.setAttribute("d", ICONS.MEDIA_TYPE.ALL.PATH);
    allIcon.appendChild(allPath);

    const imageIcon = document.createElementNS(
      "http://www.w3.org/2000/svg",
      "svg"
    );
    imageIcon.setAttribute("xmlns", "http://www.w3.org/2000/svg");
    imageIcon.setAttribute("viewBox", ICONS.MEDIA_TYPE.IMAGE.VIEWBOX);
    imageIcon.setAttribute("width", "16");
    imageIcon.setAttribute("height", "16");
    imageIcon.style.verticalAlign = "middle";

    const imagePath = document.createElementNS(
      "http://www.w3.org/2000/svg",
      "path"
    );
    imagePath.setAttribute("fill", "#64748b");
    imagePath.setAttribute("d", ICONS.MEDIA_TYPE.IMAGE.PATH);
    imageIcon.appendChild(imagePath);

    const videoIcon = document.createElementNS(
      "http://www.w3.org/2000/svg",
      "svg"
    );
    videoIcon.setAttribute("xmlns", "http://www.w3.org/2000/svg");
    videoIcon.setAttribute("viewBox", ICONS.MEDIA_TYPE.VIDEO.VIEWBOX);
    videoIcon.setAttribute("width", "16");
    videoIcon.setAttribute("height", "16");
    videoIcon.style.verticalAlign = "middle";

    const videoPath = document.createElementNS(
      "http://www.w3.org/2000/svg",
      "path"
    );
    videoPath.setAttribute("fill", "#64748b");
    videoPath.setAttribute("d", ICONS.MEDIA_TYPE.VIDEO.PATH);
    videoIcon.appendChild(videoPath);

    const gifIcon = document.createElementNS(
      "http://www.w3.org/2000/svg",
      "svg"
    );
    gifIcon.setAttribute("xmlns", "http://www.w3.org/2000/svg");
    gifIcon.setAttribute("viewBox", ICONS.MEDIA_TYPE.GIF.VIEWBOX);
    gifIcon.setAttribute("width", "16");
    gifIcon.setAttribute("height", "16");
    gifIcon.style.verticalAlign = "middle";

    const gifPath = document.createElementNS(
      "http://www.w3.org/2000/svg",
      "path"
    );
    gifPath.setAttribute("fill", "#64748b");
    gifPath.setAttribute("d", ICONS.MEDIA_TYPE.GIF.PATH);
    gifIcon.appendChild(gifPath);

    return {
      all: allIcon,
      image: imageIcon,
      video: videoIcon,
      gif: gifIcon,
    };
  }

  function createSlider(options, selectedValue, onChange) {
    const toggleOptions = options.map((option) => {
      let label = option.toString();
      if (typeof option === "number" && option >= 60 && option % 60 === 0) {
        label = `${option / 60}h`;
      }
      return { value: option, label: label };
    });

    return createToggleSwitch(toggleOptions, selectedValue, onChange);
  }

  function createModal(username) {
    let autoBatchStarted = false;
    let autoBatchCancelled = false;
    const existingModal = document.getElementById("media-downloader-modal");
    if (existingModal) {
      existingModal.remove();
    }

    const settings = getSettings();

    const modal = document.createElement("div");
    modal.id = "media-downloader-modal";
    modal.style.cssText = `
        position: fixed;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        background-color: rgba(0, 0, 0, 0.35);
        backdrop-filter: blur(2.5px);
        display: flex;
        justify-content: center;
        align-items: center;
        z-index: 10000;
        font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
    `;

    const modalContent = document.createElement("div");
    modalContent.style.cssText = `
    background-color: ${settings.darkTheme ? "#1f2937" : "#ffffff"};
    color: ${settings.darkTheme ? "#f1f5f9" : "#334155"};
    border-radius: 16px;
    width: 500px;
    max-width: 90%;
    max-height: 90vh;
    overflow-y: auto;
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
    `;

    const header = document.createElement("div");
    header.style.cssText = `
        display: flex;
        justify-content: space-between;
        align-items: center;
        padding: 16px;
        border-bottom: 1px solid ${settings.darkTheme ? "#374151" : "#e2e8f0"};
    `;

    const title = document.createElement("h2");
    title.innerHTML = `Download ${getMediaTypeLabel(
      settings.mediaType
    )}: <span style="color: #0ea5e9">${username}</span>`;
    title.style.cssText = `
    margin: 0;
    font-size: 18px;
    font-weight: bold;
    color: ${settings.darkTheme ? "#f1f5f9" : "#334155"};
    `;

    const closeButton = document.createElement("button");
    closeButton.innerHTML = "&times;";
    closeButton.style.cssText = `
        background: none;
        border: none;
        color: ${settings.darkTheme ? "#f1f5f9" : "#0f172a"};
        font-size: 24px;
        cursor: pointer;
        padding: 0;
        line-height: 1;
        transition: color 0.2s;
    `;
    closeButton.addEventListener("mouseenter", () => {
      closeButton.style.color = "#0ea5e9";
    });
    closeButton.addEventListener("mouseleave", () => {
      closeButton.style.color = settings.darkTheme ? "#f1f5f9" : "#0f172a";
    });
    closeButton.onclick = () => modal.remove();

    header.appendChild(title);
    header.appendChild(closeButton);

    const tabs = document.createElement("div");
    tabs.style.cssText = `
        display: flex;
        border-bottom: 1px solid ${settings.darkTheme ? "#374151" : "#e2e8f0"};
    `;

    const mainTab = document.createElement("div");
    mainTab.textContent = "Main";
    mainTab.className = "active-tab";
    mainTab.style.cssText = `
        padding: 12px 16px;
        cursor: pointer;
        flex: 1;
        text-align: center;
        border-bottom: 2px solid #0ea5e9;
        color: ${settings.darkTheme ? "#f1f5f9" : "#0f172a"};
    `;

    const settingsTab = document.createElement("div");
    settingsTab.textContent = "Settings";
    settingsTab.style.cssText = `
        padding: 12px 16px;
        cursor: pointer;
        flex: 1;
        text-align: center;
        color: #64748b;
    `;

    tabs.appendChild(mainTab);
    tabs.appendChild(settingsTab);

    const mainContent = document.createElement("div");
    mainContent.style.cssText = `
    padding: 16px;
    `;

    const settingsContent = document.createElement("div");
    settingsContent.style.cssText = `
    padding: 16px;
    display: none;
    `;

    const fetchButton = document.createElement("button");
    const mediaTypeLabelText = getMediaTypeLabel(
      settings.mediaType
    ).toLowerCase();
    const fetchIcon = document.createElementNS(
      "http://www.w3.org/2000/svg",
      "svg"
    );
    fetchIcon.setAttribute("xmlns", "http://www.w3.org/2000/svg");
    fetchIcon.setAttribute("viewBox", ICONS.FETCH.VIEWBOX);
    fetchIcon.setAttribute("width", "16");
    fetchIcon.setAttribute("height", "16");
    fetchIcon.style.marginRight = "8px";

    const fetchPath = document.createElementNS(
      "http://www.w3.org/2000/svg",
      "path"
    );
    fetchPath.setAttribute("fill", "currentColor");
    fetchPath.setAttribute("d", ICONS.FETCH.PATH);
    fetchIcon.appendChild(fetchPath);

    const fetchButtonText = document.createElement("span");
    fetchButtonText.textContent = settings.autoBatchEnabled
      ? "Auto Fetch"
      : settings.mediaType === "all"
      ? "Fetch Media"
      : `Fetch ${
          mediaTypeLabelText === "gif"
            ? "GIF"
            : mediaTypeLabelText.charAt(0).toUpperCase() +
              mediaTypeLabelText.slice(1)
        }`;

    fetchButton.innerHTML = "";
    fetchButton.appendChild(fetchIcon);
    fetchButton.appendChild(fetchButtonText);
    fetchButton.style.cssText = `
        background-color: #22c55e;
        color: white;
        border: none;
        border-radius: 6px;
        padding: 8px 16px;
        font-weight: bold;
        cursor: pointer;
        margin: 0;
        width: 48%;
        display: flex;
        justify-content: center;
        align-items: center;
        text-align: center;
        transition: background-color 0.2s;
    `;
    fetchButton.addEventListener("mouseenter", () => {
      fetchButton.style.backgroundColor = "#16a34a";
    });

    fetchButton.addEventListener("mouseleave", () => {
      fetchButton.style.backgroundColor = "#22c55e";
    });
    const infoContainer = document.createElement("div");
    infoContainer.style.cssText = `
        background-color: ${settings.darkTheme ? "#374151" : "#f1f5f9"};
        border-radius: 8px;
        padding: 12px;
        margin-bottom: 16px;
        display: none;
    `;

    const buttonContainer = document.createElement("div");
    buttonContainer.style.cssText = `
        display: none;
        gap: 8px;
        margin-bottom: 16px;
    `;
    const batchNavContainer = document.createElement("div");
    batchNavContainer.style.cssText = `
        display: none;
        flex-direction: column;
        gap: 8px;
        margin-bottom: 16px;
    `;

    const prevBatchButton = document.createElement("button");
    prevBatchButton.innerHTML = `<span style="display: flex; align-items: center; justify-content: center; gap: 6px; white-space: nowrap;">${ICONS.PREV}<span>Prev Batch</span></span>`;
    prevBatchButton.style.cssText = `
        background-color: #6366f1;
        color: white;
        border: none;
        border-radius: 6px;
        padding: 8px 16px;
        font-weight: bold;
        cursor: pointer;
        width: 40%;
        display: flex;
        align-items: center;
        justify-content: center;
        text-align: center;
        transition: background-color 0.2s;
    `;

    prevBatchButton.addEventListener("mouseenter", () => {
      prevBatchButton.style.backgroundColor = "#4f46e5";
    });
    prevBatchButton.addEventListener("mouseleave", () => {
      prevBatchButton.style.backgroundColor = "#6366f1";
    });

    const nextBatchButton = document.createElement("button");
    nextBatchButton.innerHTML = `<span style="display: flex; align-items: center; justify-content: center; gap: 6px; white-space: nowrap;"><span>Next Batch</span>${ICONS.NEXT}</span>`;
    nextBatchButton.style.cssText = `
        background-color: #6366f1;
        color: white;
        border: none;
        border-radius: 6px;
        padding: 8px 16px;
        font-weight: bold;
        cursor: pointer;
        width: 40%;
        display: flex;
        align-items: center;
        justify-content: center;
        text-align: center;
        transition: background-color 0.2s;
    `;

    nextBatchButton.addEventListener("mouseenter", () => {
      nextBatchButton.style.backgroundColor = "#4f46e5";
    });
    nextBatchButton.addEventListener("mouseleave", () => {
      nextBatchButton.style.backgroundColor = "#6366f1";
    });

    const buttonWrapper = document.createElement("div");
    buttonWrapper.style.cssText = `
        display: flex;
        gap: 8px;
        justify-content: center;
        width: 100%;
    `;

    buttonWrapper.appendChild(prevBatchButton);
    buttonWrapper.appendChild(nextBatchButton);
    batchNavContainer.appendChild(buttonWrapper);

    const stopFetchButton = document.createElement("button");
    stopFetchButton.innerHTML = `<span style="display: flex; align-items: center; justify-content: center; gap: 6px; white-space: nowrap;">${ICONS.STOP}<span>Stop Fetch</span></span>`;

    stopFetchButton.style.cssText = `
        background-color: #ef4444;
        color: white;
        border: none;
        border-radius: 6px;
        padding: 8px 16px;
        font-weight: bold;
        cursor: pointer;
        width: 140px; /* Fixed width instead of percentage */
        display: none;
        align-items: center;
        justify-content: center;
        text-align: center;
        transition: background-color 0.2s;
    `;

    stopFetchButton.addEventListener("mouseenter", () => {
      stopFetchButton.style.backgroundColor = "#dc2626";
    });
    stopFetchButton.addEventListener("mouseleave", () => {
      stopFetchButton.style.backgroundColor = "#ef4444";
    });
    stopFetchButton.addEventListener("click", () => {
      autoBatchCancelled = true;
      autoBatchStarted = false;
      batchNavContainer.style.display = "flex";
      prevBatchButton.style.display =
        mediaData.currentPage > getSettings().startingBatch ? "block" : "none";
      nextBatchButton.style.display = mediaData.hasMore ? "block" : "none";
      stopFetchButton.style.display = "none";
      batchInfoContainer.style.display = "flex";
      infoContainer.style.display = "block";
      fetchButtonText.textContent = "Auto Fetch";
    });

    const stopFetchWrapper = document.createElement("div");
    stopFetchWrapper.style.cssText = `
        display: flex;
        justify-content: center;
        width: 100%;
        margin-top: 8px; /* Add spacing between button rows */
    `;
    stopFetchWrapper.appendChild(stopFetchButton);
    batchNavContainer.appendChild(stopFetchWrapper);

    const batchInfoContainer = document.createElement("div");
    batchInfoContainer.style.cssText = `
        display: none;
        flex-direction: column;
        gap: 8px;
        margin-bottom: 16px;
        padding: 12px;
        background-color: ${settings.darkTheme ? "#374151" : "#f8fafc"};
        border-radius: 8px;
        border: 1px solid ${settings.darkTheme ? "#4b5563" : "#e2e8f0"};
    `;

    const batchStatsRow = document.createElement("div");
    batchStatsRow.style.cssText = `
        display: flex;
        justify-content: space-between;
        align-items: center;
    `;

    const currentBatchLabel = document.createElement("div");
    currentBatchLabel.style.cssText = `
        font-size: 14px;
        color: ${settings.darkTheme ? "#ffffff" : "#475569"};
    `;

    const totalBatchLabel = document.createElement("div");
    totalBatchLabel.style.cssText = `
        font-size: 14px;
        color: ${settings.darkTheme ? "#ffffff" : "#475569"};
    `;

    batchStatsRow.appendChild(currentBatchLabel);
    batchStatsRow.appendChild(totalBatchLabel);

    const downloadButtonsRow = document.createElement("div");
    downloadButtonsRow.style.cssText = `
        display: flex;
        gap: 8px;
        justify-content: center;
    `;

    const downloadCurrentButton = document.createElement("button");
    downloadCurrentButton.innerHTML = "";
    const currentIcon = createDownloadIcon();
    currentIcon.style.width = "12px";
    currentIcon.style.height = "12px";
    currentIcon.style.marginRight = "4px";
    const currentText = document.createElement("span");
    currentText.textContent = "Download Current";
    downloadCurrentButton.appendChild(currentIcon);
    downloadCurrentButton.appendChild(currentText);
    downloadCurrentButton.style.cssText = `
        background-color: #0ea5e9;
        color: white;
        border: none;
        border-radius: 6px;
        padding: 6px 12px;
        font-weight: bold;
        cursor: pointer;
        font-size: 12px;
        transition: background-color 0.2s;
        flex: 1;
        display: flex;
        align-items: center;
        justify-content: center;
    `;
    downloadCurrentButton.addEventListener("mouseenter", () => {
      downloadCurrentButton.style.backgroundColor = "#0284c7";
    });
    downloadCurrentButton.addEventListener("mouseleave", () => {
      downloadCurrentButton.style.backgroundColor = "#0ea5e9";
    });

    const downloadAllButton = document.createElement("button");
    downloadAllButton.innerHTML = "";
    const allIcon = createDownloadIcon();
    allIcon.style.width = "12px";
    allIcon.style.height = "12px";
    allIcon.style.marginRight = "4px";
    const allText = document.createElement("span");
    allText.textContent = "Download All";
    downloadAllButton.appendChild(allIcon);
    downloadAllButton.appendChild(allText);
    downloadAllButton.style.cssText = `
        background-color: #0ea5e9;
        color: white;
        border: none;
        border-radius: 6px;
        padding: 6px 12px;
        font-weight: bold;
        cursor: pointer;
        font-size: 12px;
        transition: background-color 0.2s;
        flex: 1;
        display: flex;
        align-items: center;
        justify-content: center;
    `;
    downloadAllButton.addEventListener("mouseenter", () => {
      downloadAllButton.style.backgroundColor = "#0284c7";
    });
    downloadAllButton.addEventListener("mouseleave", () => {
      downloadAllButton.style.backgroundColor = "#0ea5e9";
    });

    downloadButtonsRow.appendChild(downloadCurrentButton);
    downloadButtonsRow.appendChild(downloadAllButton);

    batchInfoContainer.appendChild(batchStatsRow);
    batchInfoContainer.appendChild(downloadButtonsRow);

    const downloadButton = document.createElement("button");
    const downloadIcon = createDownloadIcon();
    downloadIcon.style.width = "16px";
    downloadIcon.style.height = "16px";
    downloadIcon.style.marginRight = "8px";

    downloadButton.innerHTML = "";
    downloadButton.appendChild(downloadIcon);
    downloadButton.appendChild(document.createTextNode("Download"));
    downloadButton.style.cssText = `
        background-color: #0ea5e9;
        color: white;
        border: none;
        border-radius: 6px;
        padding: 8px 16px;
        font-weight: bold;
        cursor: pointer;
        width: 50%;
        margin-left: auto;
        margin-right: auto;
        display: flex;
        align-items: center;
        justify-content: center;
        text-align: center;
        transition: background-color 0.2s;
    `;
    downloadButton.addEventListener("mouseenter", () => {
      downloadButton.style.backgroundColor = "#0284c7";
    });
    downloadButton.addEventListener("mouseleave", () => {
      downloadButton.style.backgroundColor = "#0ea5e9";
    });
    downloadButton.onclick = () => downloadMedia(false);

    if (settings.batchEnabled) {
      buttonContainer.style.display = "none";
    } else {
      buttonContainer.appendChild(downloadButton);
    }
    const batchButtonsContainer = document.createElement("div");
    batchButtonsContainer.style.cssText = `
        display: none;
        gap: 8px;
        margin-bottom: 16px;
    `;

    const progressContainer = document.createElement("div");
    progressContainer.style.cssText = `
        margin-top: 16px;
        display: none;
    `;

    const progressText = document.createElement("div");
    progressText.style.cssText = `
        margin-bottom: 8px;
        font-size: 14px;
        text-align: center;
    `;
    progressText.textContent = "Downloading...";

    const progressBar = document.createElement("div");
    progressBar.style.cssText = `
        width: 100%;
        height: 8px;
        background-color: ${settings.darkTheme ? "#374151" : "#f1f5f9"};
        border-radius: 4px;
        overflow: hidden;
    `;

    const progressFill = document.createElement("div");
    progressFill.style.cssText = `
        height: 100%;
        width: 0%;
        background-color: #0ea5e9;
        transition: width 0.3s ease-in-out;
        will-change: width;
    `;

    progressBar.appendChild(progressFill);
    progressContainer.appendChild(progressText);
    progressContainer.appendChild(progressBar);
    const convertGifButton = document.createElement("button");
    const convertGifIcon = document.createElementNS(
      "http://www.w3.org/2000/svg",
      "svg"
    );
    convertGifIcon.setAttribute("xmlns", "http://www.w3.org/2000/svg");
    convertGifIcon.setAttribute("viewBox", ICONS.MEDIA_TYPE.GIF.VIEWBOX);
    convertGifIcon.setAttribute("width", "16");
    convertGifIcon.setAttribute("height", "16");
    convertGifIcon.style.marginRight = "8px";

    const convertGifPath = document.createElementNS(
      "http://www.w3.org/2000/svg",
      "path"
    );
    convertGifPath.setAttribute("fill", "currentColor");
    convertGifPath.setAttribute("d", ICONS.MEDIA_TYPE.GIF.PATH);
    convertGifIcon.appendChild(convertGifPath);

    const convertGifButtonText = document.createElement("span");
    convertGifButtonText.textContent = "Convert GIF";

    convertGifButton.innerHTML = "";
    convertGifButton.appendChild(convertGifIcon);
    convertGifButton.appendChild(convertGifButtonText);
    convertGifButton.style.cssText = `
        background-color: #3b82f6;
        color: white;
        border: none;
        border-radius: 6px;
        padding: 8px 16px;
        font-weight: bold;
        cursor: pointer;
        margin: 0;
        width: 48%;
        display: flex;
        justify-content: center;
        align-items: center;
        text-align: center;
        transition: background-color 0.2s;
    `;

    convertGifButton.addEventListener("mouseenter", () => {
      convertGifButton.style.backgroundColor = "#2563eb";
    });

    convertGifButton.addEventListener("mouseleave", () => {
      convertGifButton.style.backgroundColor = "#3b82f6";
    });
    convertGifButton.addEventListener("click", () => {
      const convertUrl = `https://convert.xbatch.online/${username}`;
      window.open(convertUrl, "_blank");
    });

    const mainButtonsContainer = document.createElement("div");
    mainButtonsContainer.style.cssText = `
        display: flex;
        gap: 4%;
        margin: 16px 0;
        width: 100%;
    `;

    mainButtonsContainer.appendChild(fetchButton);
    mainButtonsContainer.appendChild(convertGifButton);

    mainContent.appendChild(mainButtonsContainer);
    mainContent.appendChild(infoContainer);
    mainContent.appendChild(batchInfoContainer);
    mainContent.appendChild(batchNavContainer);
    mainContent.appendChild(buttonContainer);
    mainContent.appendChild(batchButtonsContainer);
    mainContent.appendChild(progressContainer);

    const settingsTabs = document.createElement("div");
    settingsTabs.style.cssText = `
        display: flex;
        border-bottom: 1px solid ${settings.darkTheme ? "#374151" : "#e2e8f0"};
        margin-bottom: 16px;
        width: 100%;
    `;

    const fetchTab = document.createElement("button");
    fetchTab.textContent = "Fetch";
    fetchTab.style.cssText = `
        background: none;
        border: none;
        padding: 12px 16px;
        cursor: pointer;
        color: ${settings.darkTheme ? "#f1f5f9" : "#0f172a"};
        border-bottom: 2px solid #0ea5e9;
        font-weight: bold;
        font-size: 14px;
        flex: 1;
        text-align: center;
    `;

    const authTab = document.createElement("button");
    authTab.textContent = "Auth";
    authTab.style.cssText = `
        background: none;
        border: none;
        padding: 12px 16px;
        cursor: pointer;
        color: #64748b;
        border-bottom: none;
        font-weight: bold;
        font-size: 14px;
        flex: 1;
        text-align: center;
    `;

    const advancedTab = document.createElement("button");
    advancedTab.textContent = "Advanced";
    advancedTab.style.cssText = `
        background: none;
        border: none;
        padding: 12px 16px;
        cursor: pointer;
        color: #64748b;
        border-bottom: none;
        font-weight: bold;
        font-size: 14px;
        flex: 1;
        text-align: center;
    `;

    settingsTabs.appendChild(fetchTab);
    settingsTabs.appendChild(authTab);
    settingsTabs.appendChild(advancedTab);
    const fetchTabContent = document.createElement("div");
    fetchTabContent.style.cssText = `
        display: flex;
        flex-direction: column;
        gap: ${settings.batchEnabled ? "16px" : "8px"};
    `;

    const downloadTabContent = document.createElement("div");
    downloadTabContent.style.cssText = `
        display: none;
        flex-direction: column;
        gap: 16px;
    `;

    const authTabContent = document.createElement("div");
    authTabContent.style.cssText = `
        display: none;
        flex-direction: column;
        gap: 16px;
    `;

    const advancedTabContent = document.createElement("div");
    advancedTabContent.style.cssText = `
        display: none;
        flex-direction: column;
        gap: 0px;
    `;

    const patreonAuthGroup = document.createElement("div");
    patreonAuthGroup.style.cssText = `
        display: flex;
        flex-direction: column;
        gap: 8px;
    `;

    const patreonAuthLabel = document.createElement("label");
    patreonAuthLabel.textContent = "Patreon Auth:";
    patreonAuthLabel.style.cssText = `
        font-size: 14px;
        font-weight: bold;
        color: ${settings.darkTheme ? "#f1f5f9" : "#334155"};
    `;

    const patreonAuthInputContainer = document.createElement("div");
    patreonAuthInputContainer.style.cssText = `
        position: relative;
        display: flex;
        align-items: center;
    `;

    const patreonAuthInput = document.createElement("input");
    patreonAuthInput.type = "text";
    patreonAuthInput.value = settings.patreonAuth;
    patreonAuthInput.style.cssText = `
    background-color: ${settings.darkTheme ? "#374151" : "#f1f5f9"};
    border: 1px solid transparent;
    border-radius: 4px;
    padding: 8px 12px;
    color: ${settings.darkTheme ? "#cbd5e1" : "#64748b"};
    width: 100%;
    box-sizing: border-box;
    transition: all 0.2s ease;
    `;

    patreonAuthInput.addEventListener("focus", () => {
      patreonAuthInput.style.border = "1px solid #0ea5e9";
      patreonAuthInput.style.outline = "none";
    });
    patreonAuthInput.addEventListener("blur", () => {
      patreonAuthInput.style.border = "1px solid transparent";
    });

    patreonAuthInput.addEventListener("input", () => {
      const newSettings = getSettings();
      newSettings.patreonAuth = patreonAuthInput.value;
      saveSettings(newSettings);
      patreonAuthClearButton.style.display = patreonAuthInput.value
        ? "block"
        : "none";
    });

    const patreonAuthClearButton = document.createElement("button");
    patreonAuthClearButton.innerHTML = "&times;";
    patreonAuthClearButton.style.cssText = `
        position: absolute;
        right: 8px;
        background: none;
        border: none;
        color: #64748b;
        font-size: 18px;
        cursor: pointer;
        padding: 0;
        display: ${settings.patreonAuth ? "block" : "none"};
    `;
    patreonAuthClearButton.addEventListener("click", () => {
      patreonAuthInput.value = "";
      const newSettings = getSettings();
      newSettings.patreonAuth = "";
      saveSettings(newSettings);
      patreonAuthClearButton.style.display = "none";
    });

    patreonAuthInputContainer.appendChild(patreonAuthInput);
    patreonAuthInputContainer.appendChild(patreonAuthClearButton);
    patreonAuthGroup.appendChild(patreonAuthLabel);
    patreonAuthGroup.appendChild(patreonAuthInputContainer);

    const tokenGroup = document.createElement("div");
    tokenGroup.style.cssText = `
        display: flex;
        flex-direction: column;
        gap: 8px;
    `;

    const tokenLabel = document.createElement("label");
    tokenLabel.textContent = "Auth Token:";
    tokenLabel.style.cssText = `
        font-size: 14px;
        font-weight: bold;
        color: ${settings.darkTheme ? "#f1f5f9" : "#334155"};
    `;

    const tokenInputContainer = document.createElement("div");
    tokenInputContainer.style.cssText = `
        position: relative;
        display: flex;
        align-items: center;
    `;

    const tokenInput = document.createElement("input");
    tokenInput.type = "text";
    tokenInput.value = settings.authToken;
    tokenInput.style.cssText = `
    background-color: ${settings.darkTheme ? "#374151" : "#f1f5f9"};
    border: 1px solid transparent;
    border-radius: 4px;
    padding: 8px 12px;
    color: ${settings.darkTheme ? "#cbd5e1" : "#64748b"};
    width: 100%;
    box-sizing: border-box;
    transition: all 0.2s ease;
    `;
    tokenInput.addEventListener("focus", () => {
      tokenInput.style.border = "1px solid #0ea5e9";
      tokenInput.style.outline = "none";
    });
    tokenInput.addEventListener("blur", () => {
      tokenInput.style.border = "1px solid transparent";
    });

    tokenInput.addEventListener("input", () => {
      const newSettings = getSettings();
      newSettings.authToken = tokenInput.value;
      saveSettings(newSettings);
      tokenClearButton.style.display = tokenInput.value ? "block" : "none";
    });

    const tokenClearButton = document.createElement("button");
    tokenClearButton.innerHTML = "&times;";
    tokenClearButton.style.cssText = `
        position: absolute;
        right: 8px;
        background: none;
        border: none;
        color: #64748b;
        font-size: 18px;
        cursor: pointer;
        padding: 0;
        display: ${settings.authToken ? "block" : "none"};
    `;
    tokenClearButton.addEventListener("click", () => {
      tokenInput.value = "";
      const newSettings = getSettings();
      newSettings.authToken = "";
      saveSettings(newSettings);
      tokenClearButton.style.display = "none";
    });

    tokenInputContainer.appendChild(tokenInput);
    tokenInputContainer.appendChild(tokenClearButton);
    tokenGroup.appendChild(tokenLabel);
    tokenGroup.appendChild(tokenInputContainer);

    const apiServerGroup = document.createElement("div");
    apiServerGroup.style.cssText = `
        display: flex;
        flex-direction: column;
        gap: 8px;
    `;

    const apiServerLabel = document.createElement("label");
    apiServerLabel.textContent = "Service:";
    apiServerLabel.style.cssText = `
        font-size: 14px;
        font-weight: bold;
        color: ${settings.darkTheme ? "#f1f5f9" : "#334155"};
    `;

    const apiServerOptions = [
      { value: "default", label: "Default" },
      { value: "backup", label: "Backup" },
    ];

    const apiServerToggle = createToggleSwitch(
      apiServerOptions,
      settings.apiServer,
      (value) => {
        const newSettings = getSettings();
        newSettings.apiServer = value;
        saveSettings(newSettings);
        settings.apiServer = value;
      }
    );

    apiServerGroup.appendChild(apiServerLabel);
    apiServerGroup.appendChild(apiServerToggle);

    const batchGroup = document.createElement("div");
    batchGroup.style.cssText = `
        display: flex;
        align-items: center;
        gap: 8px;
    `;
    const batchLabel = document.createElement("label");
    batchLabel.style.cssText = `
        font-size: 14px;
        font-weight: bold;
        color: ${settings.darkTheme ? "#f1f5f9" : "#334155"};
        flex: 1;
        display: flex;
        align-items: center;
    `;
    const batchLabelText = document.createElement("span");
    batchLabelText.textContent = "Batch:";
    batchLabel.appendChild(batchLabelText);

    const batchInfoIcon = createInfoIcon();
    const batchTooltip = createInfoTooltip(
      "When enabled, the media fetching process is performed in batches"
    );
    batchInfoIcon.addEventListener("mouseenter", batchTooltip.showTooltip);
    batchInfoIcon.addEventListener("mouseleave", batchTooltip.hideTooltip);
    batchLabel.appendChild(batchInfoIcon);

    const batchToggle = document.createElement("div");
    batchToggle.style.cssText = `
        position: relative;
        width: 50px;
        height: 24px;
        background-color: ${settings.batchEnabled ? "#22c55e" : "#cbd5e1"};
        border-radius: 12px;
        cursor: pointer;
        transition: background-color 0.3s;
    `;

    const batchToggleHandle = document.createElement("div");
    batchToggleHandle.style.cssText = `
        position: absolute;
        top: 2px;
        left: ${settings.batchEnabled ? "28px" : "2px"};
        width: 20px;
        height: 20px;
        background-color: white;
        border-radius: 50%;
        transition: left 0.3s;
    `;

    batchToggle.appendChild(batchToggleHandle);
    batchToggle.addEventListener("click", () => {
      const newSettings = getSettings();
      newSettings.batchEnabled = !newSettings.batchEnabled;
      saveSettings(newSettings);
      batchToggle.style.backgroundColor = newSettings.batchEnabled
        ? "#22c55e"
        : "#cbd5e1";
      batchToggleHandle.style.left = newSettings.batchEnabled ? "28px" : "2px";
      autoBatchGroup.style.display = newSettings.batchEnabled ? "flex" : "none";
      batchSizeGroup.style.display = newSettings.batchEnabled ? "flex" : "none";
      startingBatchGroup.style.display = newSettings.batchEnabled
        ? "flex"
        : "none";

      if (!newSettings.batchEnabled) {
        fetchTabContent.style.gap = "8px";
        newSettings.autoBatchEnabled = false;
        saveSettings(newSettings);
        autoBatchToggle.style.backgroundColor = "#cbd5e1";
        autoBatchHandle.style.left = "2px";
        const mt = getSettings().mediaType;
        const mtLabel = getMediaTypeLabel(mt).toLowerCase();
        const labelText =
          mt === "all"
            ? "Fetch Media"
            : `Fetch ${
                mtLabel === "gif"
                  ? "GIF"
                  : mtLabel.charAt(0).toUpperCase() + mtLabel.slice(1)
              }`;

        fetchButtonText.textContent = labelText;
        fetchButton.innerHTML = "";
        let updatedFetchIcon = fetchIcon.cloneNode(true);
        fetchButton.appendChild(updatedFetchIcon);
        fetchButton.appendChild(fetchButtonText);
        if (typeof stopFetchButton !== "undefined")
          stopFetchButton.style.display = "none";
        if (typeof prevBatchButton !== "undefined")
          prevBatchButton.style.display = "block";
        if (typeof nextBatchButton !== "undefined")
          nextBatchButton.style.display = "block";
      } else {
        fetchTabContent.style.gap = "16px";
      }
    });

    batchGroup.appendChild(batchLabel);
    batchGroup.appendChild(batchToggle);

    const autoBatchGroup = document.createElement("div");
    autoBatchGroup.style.cssText = `
        display: ${settings.batchEnabled ? "flex" : "none"};
        align-items: center;
        gap: 8px;
    `;
    const autoBatchLabel = document.createElement("label");
    autoBatchLabel.style.cssText = `
        font-size: 14px;
        font-weight: bold;
        color: ${settings.darkTheme ? "#f1f5f9" : "#334155"};
        flex: 1;
        display: flex;
        align-items: center;
    `;
    autoBatchLabel.textContent = "Auto Fetch";
    const autoBatchToggle = document.createElement("div");
    autoBatchToggle.style.cssText = `
        position: relative;
        width: 50px;
        height: 24px;
        background-color: ${settings.autoBatchEnabled ? "#22c55e" : "#cbd5e1"};
        border-radius: 12px;
        cursor: pointer;
        transition: background-color 0.3s;
    `;
    const autoBatchHandle = document.createElement("div");
    autoBatchHandle.style.cssText = `
        position: absolute;
        top: 2px;
        left: ${settings.autoBatchEnabled ? "28px" : "2px"};
        width: 20px;
        height: 20px;
        background-color: white;
        border-radius: 50%;
        transition: left 0.3s;
    `;
    autoBatchToggle.appendChild(autoBatchHandle);
    autoBatchToggle.addEventListener("click", () => {
      const newSettings = getSettings();
      const willEnable = !newSettings.autoBatchEnabled;
      newSettings.autoBatchEnabled = willEnable;
      saveSettings(newSettings);
      autoBatchCancelled = false;
      autoBatchStarted = false;
      autoBatchToggle.style.backgroundColor = willEnable
        ? "#22c55e"
        : "#cbd5e1";
      autoBatchHandle.style.left = willEnable ? "28px" : "2px";
      const label = willEnable
        ? "Auto Fetch"
        : newSettings.mediaType === "all"
        ? "Fetch Media"
        : newSettings.mediaType === "image"
        ? "Fetch Photos"
        : "Fetch Videos";
      fetchButtonText.textContent = label;

      if (willEnable) {
        const keyPrefix = `twitter_dl_${newSettings.timelineType}_${newSettings.mediaType}_${username}_`;
        const keySuffix = `_${newSettings.batchSize}_batch_true`;
        for (let i = 0; i < localStorage.length; i++) {
          const key = localStorage.key(i);
          if (key.startsWith(keyPrefix) && key.endsWith(keySuffix)) {
            const pageNum = parseInt(
              key.slice(keyPrefix.length, key.length - keySuffix.length),
              10
            );
            if (!isNaN(pageNum)) {
              const cachedData = cacheManager.get(key);
              if (cachedData && cachedData.timeline) {
                mediaData.batchData[pageNum] = cachedData.timeline;
              }
            }
          }
        }
        const batchNums = Object.keys(mediaData.batchData).map(Number);
        mediaData.currentPage =
          batchNums.length > 0 ? Math.max(...batchNums) + 1 : 0;
        fetchButtonText.textContent = "Auto Fetch";
      } else {
        const mt = newSettings.mediaType;
        const label =
          mt === "all"
            ? "Fetch Media"
            : `Fetch ${getMediaTypeLabel(mt).toUpperCase()}`;
        fetchButtonText.textContent = label;
      }
    });
    autoBatchGroup.appendChild(autoBatchLabel);
    autoBatchGroup.appendChild(autoBatchToggle);

    const batchSizeGroup = document.createElement("div");
    batchSizeGroup.style.cssText = `
    display: ${settings.batchEnabled ? "flex" : "none"};
    flex-direction: column;
    gap: 8px;
    `;

    const batchSizeLabel = document.createElement("label");
    batchSizeLabel.style.cssText = `
        font-size: 14px;
        font-weight: bold;
        color: ${settings.darkTheme ? "#f1f5f9" : "#334155"};
        display: flex;
        align-items: center;
    `;
    const batchSizeLabelText = document.createElement("span");
    batchSizeLabelText.textContent = "Batch Size:";
    batchSizeLabel.appendChild(batchSizeLabelText);

    const batchSizeInfoIcon = createInfoIcon();
    const batchSizeTooltip = createInfoTooltip(
      "Number of media items fetched in a single request"
    );
    batchSizeInfoIcon.addEventListener(
      "mouseenter",
      batchSizeTooltip.showTooltip
    );
    batchSizeInfoIcon.addEventListener(
      "mouseleave",
      batchSizeTooltip.hideTooltip
    );
    batchSizeLabel.appendChild(batchSizeInfoIcon);

    const batchSizeToggle = createSlider(
      batchSizes,
      settings.batchSize,
      (value) => {
        const newSettings = getSettings();
        newSettings.batchSize = value;
        saveSettings(newSettings);
        settings.batchSize = value;
      }
    );

    batchSizeGroup.appendChild(batchSizeLabel);
    batchSizeGroup.appendChild(batchSizeToggle);

    const startingBatchGroup = document.createElement("div");
    startingBatchGroup.style.cssText = `
    display: ${settings.batchEnabled ? "flex" : "none"};
    flex-direction: column;
    gap: 8px;
    `;

    const startingBatchLabel = document.createElement("label");
    startingBatchLabel.style.cssText = `
        font-size: 14px;
        font-weight: bold;
        color: ${settings.darkTheme ? "#f1f5f9" : "#334155"};
        display: flex;
        align-items: center;
    `;
    const startingBatchLabelText = document.createElement("span");
    startingBatchLabelText.textContent = "Starting Batch:";
    startingBatchLabel.appendChild(startingBatchLabelText);

    const startingBatchInfoIcon = createInfoIcon();
    const startingBatchTooltip = createInfoTooltip(
      "Determines which batch number to start fetching from"
    );
    startingBatchInfoIcon.addEventListener(
      "mouseenter",
      startingBatchTooltip.showTooltip
    );
    startingBatchInfoIcon.addEventListener(
      "mouseleave",
      startingBatchTooltip.hideTooltip
    );
    startingBatchLabel.appendChild(startingBatchInfoIcon);

    const startingBatchInput = document.createElement("input");
    startingBatchInput.type = "number";
    startingBatchInput.value = settings.startingBatch;
    startingBatchInput.min = "0";
    startingBatchInput.style.cssText = `
        background-color: ${settings.darkTheme ? "#374151" : "#f1f5f9"};
        border: 1px solid transparent;
        border-radius: 4px;
        padding: 8px 12px;
        color: ${settings.darkTheme ? "#cbd5e1" : "#64748b"};
        width: 100%;
        box-sizing: border-box;
        transition: all 0.2s ease;
    `;
    startingBatchInput.addEventListener("focus", () => {
      startingBatchInput.style.border = "1px solid #0ea5e9";
      startingBatchInput.style.outline = "none";
    });
    startingBatchInput.addEventListener("blur", () => {
      startingBatchInput.style.border = "1px solid transparent";
    });

    startingBatchInput.addEventListener("input", () => {
      const newSettings = getSettings();
      newSettings.startingBatch = parseInt(startingBatchInput.value) || 0;
      saveSettings(newSettings);
      settings.startingBatch = newSettings.startingBatch;
    });

    startingBatchGroup.appendChild(startingBatchLabel);
    startingBatchGroup.appendChild(startingBatchInput);

    const timelineTypeGroup = document.createElement("div");
    timelineTypeGroup.style.cssText = `
    display: flex;
    flex-direction: column;
    gap: 8px;
    `;

    const timelineTypeLabel = document.createElement("label");
    timelineTypeLabel.textContent = "Timeline Type:";
    timelineTypeLabel.style.cssText = `
        font-size: 14px;
        font-weight: bold;
        color: ${settings.darkTheme ? "#f1f5f9" : "#334155"};
    `;

    const timelineTypeOptions = [
      { value: "media", label: "Media", icon: createTimelineTypeIcon("media") },
      {
        value: "timeline",
        label: "Post",
        icon: createTimelineTypeIcon("timeline"),
      },
      {
        value: "tweets",
        label: "Tweets",
        icon: createTimelineTypeIcon("tweets"),
      },
      {
        value: "with_replies",
        label: "Replies",
        icon: createTimelineTypeIcon("with_replies"),
      },
    ];

    const timelineTypeToggle = createToggleSwitch(
      timelineTypeOptions,
      settings.timelineType,
      (value) => {
        const newSettings = getSettings();
        newSettings.timelineType = value;
        saveSettings(newSettings);
        settings.timelineType = value;
      }
    );

    timelineTypeGroup.appendChild(timelineTypeLabel);
    timelineTypeGroup.appendChild(timelineTypeToggle);

    const mediaTypeGroup = document.createElement("div");
    mediaTypeGroup.style.cssText = `
    display: flex;
    flex-direction: column;
    gap: 8px;
    `;

    const mediaTypeLabel = document.createElement("label");
    mediaTypeLabel.textContent = "Media Type:";
    mediaTypeLabel.style.cssText = `
        font-size: 14px;
        font-weight: bold;
        color: ${settings.darkTheme ? "#f1f5f9" : "#334155"};
    `;

    const mediaTypeIcons = createMediaTypeIcons();
    const mediaTypeOptions = [
      { value: "all", label: "All", icon: mediaTypeIcons.all },
      { value: "image", label: "Image", icon: mediaTypeIcons.image },
      { value: "video", label: "Video", icon: mediaTypeIcons.video },
      { value: "gif", label: "GIF", icon: mediaTypeIcons.gif },
    ];

    const mediaTypeToggle = createToggleSwitch(
      mediaTypeOptions,
      settings.mediaType,
      (value) => {
        const newSettings = getSettings();
        newSettings.mediaType = value;
        saveSettings(newSettings);
        settings.mediaType = value;

        const newMediaTypeLabel = getMediaTypeLabel(value).toLowerCase();
        const newFetchButtonText =
          value === "all"
            ? "Fetch Media"
            : `Fetch ${
                newMediaTypeLabel === "gif"
                  ? "GIF"
                  : newMediaTypeLabel.charAt(0).toUpperCase() +
                    newMediaTypeLabel.slice(1)
              }`;

        fetchButton.innerHTML = "";
        const newFetchIcon = fetchIcon.cloneNode(true);
        fetchButton.appendChild(newFetchIcon);
        fetchButton.appendChild(document.createTextNode(newFetchButtonText));

        title.innerHTML = `Download ${getMediaTypeLabel(
          value
        )}: <span style="color: #0ea5e9">${username}</span>`;
      }
    );

    mediaTypeGroup.appendChild(mediaTypeLabel);
    mediaTypeGroup.appendChild(mediaTypeToggle);

    const concurrentGroup = document.createElement("div");
    concurrentGroup.style.cssText = `
    display: flex;
    flex-direction: column;
    gap: 8px;
    `;

    const concurrentLabel = document.createElement("label");
    concurrentLabel.style.cssText = `
        font-size: 14px;
        font-weight: bold;
        color: ${settings.darkTheme ? "#f1f5f9" : "#334155"};
        display: flex;
        align-items: center;
    `;

    const concurrentLabelText = document.createElement("span");
    concurrentLabelText.textContent = "Batch Download Items:";
    concurrentLabel.appendChild(concurrentLabelText);

    const concurrentInfoIcon = createInfoIcon();
    const concurrentTooltip = createInfoTooltip(
      "Total item that are downloaded in a single request"
    );
    concurrentInfoIcon.addEventListener(
      "mouseenter",
      concurrentTooltip.showTooltip
    );
    concurrentInfoIcon.addEventListener(
      "mouseleave",
      concurrentTooltip.hideTooltip
    );
    concurrentLabel.appendChild(concurrentInfoIcon);

    const concurrentToggle = document.createElement("div");
    concurrentToggle.textContent = "Fixed to 50 concurrent downloads";
    concurrentToggle.style.cssText = `
        padding: 10px;
        background-color: ${settings.darkTheme ? "#374151" : "#f1f5f9"};
        color: ${settings.darkTheme ? "#f1f5f9" : "#334155"};
        border-radius: 8px;
        text-align: center;
    `;

    concurrentGroup.appendChild(concurrentLabel);
    concurrentGroup.appendChild(concurrentToggle);

    const cacheDurationGroup = document.createElement("div");
    cacheDurationGroup.style.cssText = `
    display: flex;
    flex-direction: column;
    gap: 8px;
    `;

    const cacheDurationLabel = document.createElement("label");
    cacheDurationLabel.textContent = "Cache Duration:";
    cacheDurationLabel.style.cssText = `
        font-size: 14px;
        font-weight: bold;
        color: ${settings.darkTheme ? "#f1f5f9" : "#334155"};
    `;

    const cacheDurationOptions = cacheDurations.map((duration) => {
      let label = duration.toString() + "m";
      if (duration >= 60 && duration % 60 === 0) {
        label = `${duration / 60}h`;
      }
      return { value: duration, label: label };
    });

    const cacheDurationToggle = createToggleSwitch(
      cacheDurationOptions,
      settings.cacheDuration,
      (value) => {
        const newSettings = getSettings();
        newSettings.cacheDuration = value;
        saveSettings(newSettings);
        settings.cacheDuration = value;
      }
    );
    cacheDurationGroup.appendChild(cacheDurationLabel);
    cacheDurationGroup.appendChild(cacheDurationToggle);

    const darkThemeGroup = document.createElement("div");
    darkThemeGroup.style.cssText = `
        display: flex;
        align-items: center;
        gap: 8px;
    `;
    const darkThemeLabel = document.createElement("label");
    darkThemeLabel.style.cssText = `
        font-size: 14px;
        font-weight: bold;
        color: ${settings.darkTheme ? "#f1f5f9" : "#334155"};
        flex: 1;
        display: flex;
        align-items: center;
    `;
    const darkThemeLabelText = document.createElement("span");
    darkThemeLabelText.textContent = "Dark Theme:";
    darkThemeLabel.appendChild(darkThemeLabelText);

    const darkThemeToggle = document.createElement("div");
    darkThemeToggle.style.cssText = `
        position: relative;
        width: 50px;
        height: 24px;
        background-color: ${settings.darkTheme ? "#22c55e" : "#cbd5e1"};
        border-radius: 12px;
        cursor: pointer;
        transition: background-color 0.3s;
    `;

    const darkThemeToggleHandle = document.createElement("div");
    darkThemeToggleHandle.style.cssText = `
        position: absolute;
        top: 2px;
        left: ${settings.darkTheme ? "28px" : "2px"};
        width: 20px;
        height: 20px;
        background-color: white;
        border-radius: 50%;
        transition: left 0.3s;
    `;

    darkThemeToggle.appendChild(darkThemeToggleHandle);
    darkThemeToggle.addEventListener("click", () => {
      const newSettings = getSettings();
      newSettings.darkTheme = !newSettings.darkTheme;
      saveSettings(newSettings);
      settings.darkTheme = newSettings.darkTheme;
      darkThemeToggle.style.backgroundColor = newSettings.darkTheme
        ? "#22c55e"
        : "#cbd5e1";
      darkThemeToggleHandle.style.left = newSettings.darkTheme ? "28px" : "2px";
      updateTheme();
    });

    darkThemeGroup.appendChild(darkThemeLabel);
    darkThemeGroup.appendChild(darkThemeToggle);

    const buttonsContainer = document.createElement("div");
    buttonsContainer.style.cssText = `
    display: flex;
    gap: 16px;
    margin-top: 16px;
    width: 100%;
    justify-content: center;
    align-items: center;
    `;

    const clearCacheButton = document.createElement("button");
    const trashIcon = document.createElementNS(
      "http://www.w3.org/2000/svg",
      "svg"
    );
    trashIcon.setAttribute("xmlns", "http://www.w3.org/2000/svg");
    trashIcon.setAttribute("viewBox", ICONS.CLEAR.VIEWBOX);
    trashIcon.setAttribute("width", "16");
    trashIcon.setAttribute("height", "16");
    trashIcon.style.marginRight = "8px";

    const trashPath = document.createElementNS(
      "http://www.w3.org/2000/svg",
      "path"
    );
    trashPath.setAttribute("fill", "currentColor");
    trashPath.setAttribute("d", ICONS.CLEAR.PATH);
    trashIcon.appendChild(trashPath);
    clearCacheButton.appendChild(trashIcon);
    clearCacheButton.appendChild(document.createTextNode("Clear Cache"));
    clearCacheButton.style.cssText = `
    background-color: #ef4444;
    color: white;
    border: none;
    border-radius: 6px;
    padding: 8px 16px;
    font-weight: bold;
    cursor: pointer;
    flex: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    text-align: center;
    transition: background-color 0.2s;
    `;

    const resetDefaultButton = document.createElement("button");
    const resetIcon = document.createElementNS(
      "http://www.w3.org/2000/svg",
      "svg"
    );
    resetIcon.setAttribute("xmlns", "http://www.w3.org/2000/svg");
    resetIcon.setAttribute("viewBox", ICONS.RESET.VIEWBOX);
    resetIcon.setAttribute("width", "16");
    resetIcon.setAttribute("height", "16");
    resetIcon.style.marginRight = "8px";

    const resetPath = document.createElementNS(
      "http://www.w3.org/2000/svg",
      "path"
    );
    resetPath.setAttribute("fill", "currentColor");
    resetPath.setAttribute("d", ICONS.RESET.PATH);
    resetIcon.appendChild(resetPath);

    resetDefaultButton.appendChild(resetIcon);
    resetDefaultButton.appendChild(document.createTextNode("Reset Default"));
    resetDefaultButton.style.cssText = `
    background-color: #6366f1;
    color: white;
    border: none;
    border-radius: 6px;
    padding: 8px 16px;
    font-weight: bold;
    cursor: pointer;
    flex: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    text-align: center;
    transition: background-color 0.2s;
    `;
    buttonsContainer.appendChild(clearCacheButton);
    buttonsContainer.appendChild(resetDefaultButton);

    clearCacheButton.addEventListener("mouseenter", () => {
      clearCacheButton.style.backgroundColor = "#dc2626";
    });
    clearCacheButton.addEventListener("mouseleave", () => {
      clearCacheButton.style.backgroundColor = "#ef4444";
    });

    resetDefaultButton.addEventListener("mouseenter", () => {
      resetDefaultButton.style.backgroundColor = "#5b56f4";
    });
    resetDefaultButton.addEventListener("mouseleave", () => {
      resetDefaultButton.style.backgroundColor = "#6366f1";
    });
    clearCacheButton.addEventListener("click", () => {
      createConfirmDialog("Are you sure about clearing the cache?", () => {
        cacheManager.clear();

        createInfoDialog(
          "Cache has been successfully cleared.",
          "Clear Successful"
        );
      });
    });

    resetDefaultButton.addEventListener("click", () => {
      createConfirmDialog(
        "Are you sure you want to reset all settings to default values?",
        () => {
          const currentSettings = getSettings();
          const preservedPatreonAuth = currentSettings.patreonAuth;
          const preservedAuthToken = currentSettings.authToken;

          const resetSettings = { ...defaultSettings };
          resetSettings.patreonAuth = preservedPatreonAuth;
          resetSettings.authToken = preservedAuthToken;
          saveSettings(resetSettings);

          startingBatchInput.value = defaultSettings.startingBatch;

          batchToggle.style.backgroundColor = defaultSettings.batchEnabled
            ? "#22c55e"
            : "#cbd5e1";
          batchToggleHandle.style.left = defaultSettings.batchEnabled
            ? "28px"
            : "2px";

          batchSizeGroup.style.display = defaultSettings.batchEnabled
            ? "flex"
            : "none";
          startingBatchGroup.style.display = defaultSettings.batchEnabled
            ? "flex"
            : "none";
          autoBatchGroup.style.display = defaultSettings.batchEnabled
            ? "flex"
            : "none";

          patreonAuthClearButton.style.display = preservedPatreonAuth
            ? "block"
            : "none";
          tokenClearButton.style.display = preservedAuthToken
            ? "block"
            : "none";

          const timelineTypeOptions = [
            "media",
            "timeline",
            "tweets",
            "with_replies",
          ];
          const timelineTypeIndex = timelineTypeOptions.indexOf(
            defaultSettings.timelineType
          );
          const timelineTypeSlider =
            timelineTypeToggle.querySelector("div:first-child");
          const timelineTypeContainer =
            timelineTypeToggle.querySelector("div:last-child");

          if (
            timelineTypeSlider &&
            timelineTypeContainer &&
            timelineTypeIndex !== -1
          ) {
            timelineTypeSlider.style.transform = `translateX(${
              timelineTypeIndex * 100
            }%)`;
            timelineTypeContainer.querySelectorAll("div").forEach((opt, i) => {
              opt.style.color = i === timelineTypeIndex ? "white" : "#64748b";
              const optIcon = opt.querySelector("svg");
              if (optIcon) {
                const optPaths = optIcon.querySelectorAll("path");
                optPaths.forEach((path) => {
                  path.setAttribute(
                    "fill",
                    i === timelineTypeIndex ? "white" : "#64748b"
                  );
                });
              }
            });
          }

          const mediaTypeOptions = ["all", "image", "video", "gif"];
          const mediaTypeIndex = mediaTypeOptions.indexOf(
            defaultSettings.mediaType
          );
          const mediaTypeSlider =
            mediaTypeToggle.querySelector("div:first-child");
          const mediaTypeContainer =
            mediaTypeToggle.querySelector("div:last-child");

          if (mediaTypeSlider && mediaTypeContainer && mediaTypeIndex !== -1) {
            mediaTypeSlider.style.transform = `translateX(${
              mediaTypeIndex * 100
            }%)`;
            mediaTypeContainer.querySelectorAll("div").forEach((opt, i) => {
              opt.style.color = i === mediaTypeIndex ? "white" : "#64748b";
              const optIcon = opt.querySelector("svg");
              if (optIcon) {
                const optPaths = optIcon.querySelectorAll("path");
                optPaths.forEach((path) => {
                  path.setAttribute(
                    "fill",
                    i === mediaTypeIndex ? "white" : "#64748b"
                  );
                });
              }
            });
          }

          const batchSizeIndex = batchSizes.indexOf(defaultSettings.batchSize);
          const batchSizeSlider =
            batchSizeToggle.querySelector("div:first-child");
          const batchSizeContainer =
            batchSizeToggle.querySelector("div:last-child");

          if (batchSizeSlider && batchSizeContainer && batchSizeIndex !== -1) {
            batchSizeSlider.style.transform = `translateX(${
              batchSizeIndex * 100
            }%)`;
            batchSizeContainer.querySelectorAll("div").forEach((opt, i) => {
              opt.style.color = i === batchSizeIndex ? "white" : "#64748b";
              const optIcon = opt.querySelector("svg");
              if (optIcon) {
                const optPaths = optIcon.querySelectorAll("path");
                optPaths.forEach((path) => {
                  path.setAttribute(
                    "fill",
                    i === batchSizeIndex ? "white" : "#64748b"
                  );
                });
              }
            });
          }

          const cacheDurationIndex = cacheDurations.indexOf(
            defaultSettings.cacheDuration
          );
          const cacheDurationSlider =
            cacheDurationToggle.querySelector("div:first-child");
          const cacheDurationContainer =
            cacheDurationToggle.querySelector("div:last-child");

          if (
            cacheDurationSlider &&
            cacheDurationContainer &&
            cacheDurationIndex !== -1
          ) {
            cacheDurationSlider.style.transform = `translateX(${
              cacheDurationIndex * 100
            }%)`;
            cacheDurationContainer.querySelectorAll("div").forEach((opt, i) => {
              opt.style.color = i === cacheDurationIndex ? "white" : "#64748b";
              const optIcon = opt.querySelector("svg");
              if (optIcon) {
                const optPaths = optIcon.querySelectorAll("path");
                optPaths.forEach((path) => {
                  path.setAttribute(
                    "fill",
                    i === cacheDurationIndex ? "white" : "#64748b"
                  );
                });
              }
            });
          }
          const apiServerOptions = ["default", "backup"];
          const apiServerIndex = apiServerOptions.indexOf(
            defaultSettings.apiServer
          );
          const apiServerSlider =
            apiServerToggle.querySelector("div:first-child");
          const apiServerContainer =
            apiServerToggle.querySelector("div:last-child");

          if (apiServerSlider && apiServerContainer && apiServerIndex !== -1) {
            apiServerSlider.style.transform = `translateX(${
              apiServerIndex * 100
            }%)`;
            apiServerContainer.querySelectorAll("div").forEach((opt, i) => {
              opt.style.color = i === apiServerIndex ? "white" : "#64748b";
              const optIcon = opt.querySelector("svg");
              if (optIcon) {
                const optPaths = optIcon.querySelectorAll("path");
                optPaths.forEach((path) => {
                  path.setAttribute(
                    "fill",
                    i === apiServerIndex ? "white" : "#64748b"
                  );
                });
              }
            });
          }

          darkThemeToggle.style.backgroundColor = defaultSettings.darkTheme
            ? "#22c55e"
            : "#cbd5e1";
          darkThemeToggleHandle.style.left = defaultSettings.darkTheme
            ? "28px"
            : "2px";
          settings.darkTheme = defaultSettings.darkTheme;
          updateTheme();

          const newMediaTypeLabel = getMediaTypeLabel(
            defaultSettings.mediaType
          ).toLowerCase();
          const newFetchButtonText =
            defaultSettings.mediaType === "all"
              ? "Fetch Media"
              : `Fetch ${
                  newMediaTypeLabel === "gif"
                    ? "GIF"
                    : newMediaTypeLabel.charAt(0).toUpperCase() +
                      newMediaTypeLabel.slice(1)
                }`;
          fetchButton.innerHTML = "";
          const newFetchIcon = fetchIcon.cloneNode(true);
          fetchButton.appendChild(newFetchIcon);
          fetchButton.appendChild(document.createTextNode(newFetchButtonText));
          title.innerHTML = `Download ${getMediaTypeLabel(
            defaultSettings.mediaType
          )}: <span style="color: #0ea5e9">${username}</span>`;

          setTimeout(() => {
            createInfoDialog(
              "Settings have been successfully reset to default values.",
              "Reset Successful"
            );
          }, 100);
        }
      );
    });

    const patreonLink = document.createElement("a");
    patreonLink.href = "https://www.patreon.com/exyezed";
    patreonLink.target = "_blank";
    patreonLink.style.cssText = `
        display: flex;
        align-items: center;
        justify-content: center;
        color: #64748b;
        text-decoration: none;
        margin-top: 16px;
        padding: 8px;
        border-radius: 8px;
        transition: background-color 0.2s, color 0.2s;
    `;
    patreonLink.innerHTML =
      createPatreonIcon().outerHTML + "Patreon Authentication";
    patreonLink.addEventListener("mouseenter", () => {
      patreonLink.style.backgroundColor = settings.darkTheme
        ? "#374151"
        : "#f1f5f9";
      patreonLink.style.color = "#0ea5e9";
    });
    patreonLink.addEventListener("mouseleave", () => {
      patreonLink.style.backgroundColor = "transparent";
      patreonLink.style.color = "#64748b";
    });

    const batchSizeStartingContainer = document.createElement("div");
    batchSizeStartingContainer.style.cssText = `
        display: flex;
        flex-direction: column;
        gap: 0px;
    `;
    batchSizeStartingContainer.appendChild(batchSizeGroup);
    batchSizeStartingContainer.appendChild(startingBatchGroup);

    const mediaTimelineContainer = document.createElement("div");
    mediaTimelineContainer.style.cssText = `
        display: flex;
        flex-direction: column;
        gap: 0px;
    `;
    mediaTimelineContainer.appendChild(mediaTypeGroup);
    mediaTimelineContainer.appendChild(timelineTypeGroup);

    fetchTabContent.appendChild(batchGroup);
    fetchTabContent.appendChild(autoBatchGroup);
    fetchTabContent.appendChild(batchSizeStartingContainer);
    fetchTabContent.appendChild(mediaTimelineContainer);

    authTabContent.appendChild(patreonAuthGroup);
    authTabContent.appendChild(tokenGroup);
    authTabContent.appendChild(patreonLink);

    advancedTabContent.appendChild(darkThemeGroup);

    apiServerGroup.style.marginTop = "16px";
    advancedTabContent.appendChild(apiServerGroup);

    cacheDurationGroup.style.marginTop = "0px";
    advancedTabContent.appendChild(cacheDurationGroup);

    buttonsContainer.style.marginTop = "16px";
    advancedTabContent.appendChild(buttonsContainer);

    function switchTab(activeTab, activeContent) {
      const allTabs = [fetchTab, authTab, advancedTab];
      const allContents = [fetchTabContent, authTabContent, advancedTabContent];

      allTabs.forEach((tab) => {
        if (tab && tab.style) {
          tab.style.color = "#64748b";
          tab.style.borderBottom = "none";
        }
      });

      allContents.forEach((content) => {
        if (content && content.style) {
          content.style.display = "none";
        }
      });

      if (activeTab && activeTab.style) {
        activeTab.style.color = settings.darkTheme ? "#f1f5f9" : "#0f172a";
        activeTab.style.borderBottom = "2px solid #0ea5e9";
      }
      if (activeContent && activeContent.style) {
        activeContent.style.display = "flex";
      }
    }
    fetchTab.addEventListener("click", function () {
      switchTab(fetchTab, fetchTabContent);
    });
    authTab.addEventListener("click", function () {
      switchTab(authTab, authTabContent);
    });
    advancedTab.addEventListener("click", function () {
      switchTab(advancedTab, advancedTabContent);
    });

    const settingsForm = document.createElement("div");
    settingsForm.style.cssText = `
        display: flex;
        flex-direction: column;
    `;

    settingsForm.appendChild(settingsTabs);
    settingsForm.appendChild(fetchTabContent);
    settingsForm.appendChild(authTabContent);
    settingsForm.appendChild(advancedTabContent);

    settingsContent.appendChild(settingsForm);

    mainTab.addEventListener("click", function () {
      mainTab.style.borderBottom = "2px solid #0ea5e9";
      mainTab.style.color = settings.darkTheme ? "#f1f5f9" : "#0f172a";
      settingsTab.style.borderBottom = "none";
      settingsTab.style.color = "#64748b";
      mainContent.style.display = "block";
      settingsContent.style.display = "none";
    });

    settingsTab.addEventListener("click", function () {
      settingsTab.style.borderBottom = "2px solid #0ea5e9";
      settingsTab.style.color = settings.darkTheme ? "#f1f5f9" : "#0f172a";
      mainTab.style.borderBottom = "none";
      mainTab.style.color = "#64748b";
      settingsContent.style.display = "block";
      mainContent.style.display = "none";

      switchTab(fetchTab, fetchTabContent);
    });

    modalContent.appendChild(header);
    modalContent.appendChild(tabs);
    modalContent.appendChild(mainContent);
    modalContent.appendChild(settingsContent);
    modal.appendChild(modalContent);

    const mediaData = {
      username: username,
      currentPage: getSettings().startingBatch,
      mediaItems: [],
      allMediaItems: [],
      batchData: {},
      hasMore: false,
      downloading: false,
      totalDownloaded: 0,
      totalToDownload: 0,
      totalItems: 0,
      nextBatchClicked: false,
    };

    function refreshInfoDisplays() {
      const isDark = getSettings().darkTheme;

      if (infoContainer.innerHTML && mediaData.totalItems > 0) {
        const settings = getSettings();
        const mediaTypeLabel = getMediaTypeLabel(settings.mediaType);

        if (
          settings.batchEnabled &&
          mediaData.mediaItems &&
          mediaData.mediaItems.length > 0
        ) {
          const accountIconHtml = createAccountIcon(isDark).outerHTML;
          const mediaIconHtml = createMediaTypeIcon(
            settings.mediaType,
            isDark
          ).outerHTML;
          const totalItemsIconHtml = createTotalItemsIcon(isDark).outerHTML;

          infoContainer.innerHTML = `
            <div style="margin-bottom: 8px; display: flex; align-items: center;">${accountIconHtml}<strong style="margin-right: 6px;">Account:</strong><span>${
            mediaData.username
          }</span></div>
            <div style="margin-bottom: 8px; display: flex; align-items: center;">${mediaIconHtml}<strong style="margin-right: 6px;">${mediaTypeLabel} Found:</strong><span>${formatNumber(
            mediaData.totalItems
          )}</span></div>
            <div style="margin-bottom: 8px; display: flex; align-items: center;">${totalItemsIconHtml}<strong style="margin-right: 6px;">Total Items:</strong><span>${formatNumber(
            mediaData.allMediaItems.length
          )}</span></div>
          `;
        } else if (
          mediaData.allMediaItems &&
          mediaData.allMediaItems.length > 0
        ) {
          const accountIconHtml = createAccountIcon(isDark).outerHTML;
          const mediaIconHtml = createMediaTypeIcon(
            settings.mediaType,
            isDark
          ).outerHTML;
          const totalZipIconHtml = createTotalZipIcon(isDark).outerHTML;
          const currentPart =
            Math.floor(mediaData.allMediaItems.length / 500) + 1;

          infoContainer.innerHTML = `
            <div style="margin-bottom: 8px; display: flex; align-items: center;">${accountIconHtml}<strong style="margin-right: 6px;">Account:</strong><span>${
            mediaData.username
          }</span></div>
            <div style="margin-bottom: 8px; display: flex; align-items: center;">${mediaIconHtml}<strong style="margin-right: 6px;">${mediaTypeLabel} Found:</strong><span>${formatNumber(
            mediaData.totalItems
          )}</span></div>
            <div style="margin-top: 8px; display: flex; align-items: center;">${totalZipIconHtml}<strong style="margin-right: 6px;">Total ZIP ${
            currentPart === 1 ? "File" : "Files"
          }:</strong><span>${currentPart}</span></div>
          `;
        }
      }

      if (infoContainer.innerHTML) {
        const accountIcons = infoContainer.querySelectorAll(
          'svg[viewBox="0 0 24 24"]'
        );
        accountIcons.forEach((icon) => {
          icon.style.color = isDark ? "#ffffff" : "#64748b";
        });

        const mediaIcons = infoContainer.querySelectorAll(
          'svg[viewBox="0 0 576 512"], svg[viewBox="0 0 512 512"], svg[viewBox="0 0 640 512"]'
        );
        mediaIcons.forEach((icon) => {
          icon.style.color = isDark ? "#ffffff" : "#64748b";
        });

        const totalIcons = infoContainer.querySelectorAll(
          'svg[viewBox="0 0 448 512"]'
        );
        totalIcons.forEach((icon) => {
          icon.style.color = isDark ? "#ffffff" : "#64748b";
        });
      }
    }

    function updateBatchLabels() {
      const isDark = getSettings().darkTheme;
      const settings = getSettings();

      if (currentBatchLabel && currentBatchLabel.innerHTML) {
        const currentBatchIconHtml = createCurrentBatchIcon(isDark).outerHTML;
        currentBatchLabel.innerHTML = `<div style="display: flex; align-items: center;">${currentBatchIconHtml}<strong style="margin-right: 6px; color: ${
          settings.darkTheme ? "#ffffff" : "#475569"
        };">Current Batch:</strong><span style="color: ${
          settings.darkTheme ? "#ffffff" : "#475569"
        };">${mediaData.currentPage + 1}</span></div>`;
      }

      if (
        totalBatchLabel &&
        totalBatchLabel.innerHTML &&
        mediaData.totalItems > 0
      ) {
        const totalBatches = Math.ceil(
          mediaData.totalItems / settings.batchSize
        );
        const totalZipIconHtml = createTotalZipIcon(isDark).outerHTML;
        totalBatchLabel.innerHTML = `<div style="display: flex; align-items: center;">${totalZipIconHtml}<strong style="margin-right: 6px; color: ${
          settings.darkTheme ? "#ffffff" : "#475569"
        };">Total ZIP ${
          totalBatches === 1 ? "File" : "Files"
        }:</strong><span style="color: ${
          settings.darkTheme ? "#ffffff" : "#475569"
        };">${totalBatches}</span></div>`;
      }

      if (currentBatchLabel && currentBatchLabel.innerHTML) {
        const batchIcons = currentBatchLabel.querySelectorAll("svg");
        batchIcons.forEach((icon) => {
          icon.style.color = isDark ? "#ffffff" : "#64748b";
        });
      }

      if (totalBatchLabel && totalBatchLabel.innerHTML) {
        const zipIcons = totalBatchLabel.querySelectorAll("svg");
        zipIcons.forEach((icon) => {
          icon.style.color = isDark ? "#ffffff" : "#64748b";
        });
      }
    }

    function updateTheme() {
      const isDark = getSettings().darkTheme;
      modalContent.style.backgroundColor = isDark ? "#1f2937" : "#ffffff";
      modalContent.style.color = isDark ? "#f1f5f9" : "#334155";
      header.style.borderBottom = `1px solid ${isDark ? "#374151" : "#e2e8f0"}`;
      title.style.color = isDark ? "#f1f5f9" : "#334155";
      closeButton.style.color = isDark ? "#f1f5f9" : "#0f172a";
      tabs.style.borderBottom = `1px solid ${isDark ? "#374151" : "#e2e8f0"}`;
      mainTab.style.color =
        mainContent.style.display === "block"
          ? isDark
            ? "#f1f5f9"
            : "#0f172a"
          : "#64748b";
      settingsTab.style.color =
        settingsContent.style.display === "block"
          ? isDark
            ? "#f1f5f9"
            : "#0f172a"
          : "#64748b";
      infoContainer.style.backgroundColor = isDark ? "#374151" : "#f1f5f9";
      batchInfoContainer.style.backgroundColor = isDark ? "#374151" : "#f8fafc";
      batchInfoContainer.style.border = `1px solid ${
        isDark ? "#4b5563" : "#e2e8f0"
      }`;
      settingsTabs.style.borderBottom = `1px solid ${
        isDark ? "#374151" : "#e2e8f0"
      }`;

      const allSettingTabs = [fetchTab, authTab, advancedTab];
      allSettingTabs.forEach((tab) => {
        if (tab.style.borderBottom.includes("solid")) {
          tab.style.color = isDark ? "#f1f5f9" : "#0f172a";
        }
      });
      patreonAuthLabel.style.color = isDark ? "#f1f5f9" : "#334155";
      patreonAuthInput.style.backgroundColor = isDark ? "#374151" : "#f1f5f9";
      autoBatchLabel.style.color = isDark ? "#f1f5f9" : "#334155";
      patreonAuthInput.style.color = isDark ? "#cbd5e1" : "#64748b";
      tokenLabel.style.color = isDark ? "#f1f5f9" : "#334155";
      tokenInput.style.backgroundColor = isDark ? "#374151" : "#f1f5f9";
      tokenInput.style.color = isDark ? "#cbd5e1" : "#64748b";
      apiServerLabel.style.color = isDark ? "#f1f5f9" : "#334155";
      batchLabel.style.color = isDark ? "#f1f5f9" : "#334155";
      batchSizeLabel.style.color = isDark ? "#f1f5f9" : "#334155";
      startingBatchLabel.style.color = isDark ? "#f1f5f9" : "#334155";
      startingBatchInput.style.backgroundColor = isDark ? "#374151" : "#f1f5f9";
      startingBatchInput.style.color = isDark ? "#cbd5e1" : "#64748b";
      timelineTypeLabel.style.color = isDark ? "#f1f5f9" : "#334155";
      mediaTypeLabel.style.color = isDark ? "#f1f5f9" : "#334155";
      concurrentLabel.style.color = isDark ? "#f1f5f9" : "#334155";
      cacheDurationLabel.style.color = isDark ? "#f1f5f9" : "#334155";
      darkThemeLabel.style.color = isDark ? "#f1f5f9" : "#334155";
      const toggleSwitches = [
        timelineTypeToggle,
        mediaTypeToggle,
        batchSizeToggle,
        concurrentToggle,
        cacheDurationToggle,
        apiServerToggle,
      ];
      toggleSwitches.forEach((toggle) => {
        if (toggle) {
          toggle.style.backgroundColor = isDark ? "#374151" : "#f1f5f9";
        }
      });

      if (progressBar) {
        progressBar.style.backgroundColor = isDark ? "#374151" : "#f1f5f9";
      }

      if (currentBatchLabel) {
        currentBatchLabel.style.color = isDark ? "#ffffff" : "#475569";
      }
      if (totalBatchLabel) {
        totalBatchLabel.style.color = isDark ? "#ffffff" : "#475569";
      }

      refreshInfoDisplays();

      updateBatchLabels();
    }

    fetchButton.addEventListener("click", async () => {
      const settings = getSettings();
      if (!settings.authToken) {
        createAuthTokenPopup();
        return;
      }
      if (!settings.patreonAuth) {
        createPatreonAuthPopup();
        return;
      }
      if (settings.autoBatchEnabled && !autoBatchStarted) {
        autoBatchCancelled = false;
        autoBatchStarted = true;
        const batchNums = Object.keys(mediaData.batchData).map(Number);
        mediaData.currentPage =
          batchNums.length > 0 ? Math.max(...batchNums) + 1 : 0;
      }
      if (
        !settings.autoBatchEnabled ||
        (settings.autoBatchEnabled &&
          !autoBatchStarted &&
          Object.keys(mediaData.batchData).length === 0)
      ) {
        infoContainer.style.display = "none";
        buttonContainer.style.display = "none";
        nextBatchButton.style.display = "none";
        prevBatchButton.style.display = "none";
        progressContainer.style.display = "none";
        if (settings.autoBatchEnabled) {
          autoBatchStarted = true;
          mediaData.currentPage = 0;
        }
      }
      fetchButton.disabled = true;
      fetchButton.innerHTML = "";
      fetchButton.appendChild(document.createTextNode("Fetching..."));

      try {
        const cacheKey = `${settings.timelineType}_${settings.mediaType}_${username}_${mediaData.currentPage}_${settings.batchSize}_batch_${settings.batchEnabled}`;
        let data = cacheManager.get(cacheKey);

        if (!data) {
          let url;
          if (settings.batchEnabled) {
            url = `${getServiceBaseUrl()}/metadata/${settings.timelineType}/${
              settings.batchSize
            }/${mediaData.currentPage}/${settings.mediaType}/${username}/${
              settings.authToken
            }/${settings.patreonAuth || ""}`;
          } else {
            url = `${getServiceBaseUrl()}/metadata/${settings.timelineType}/${
              settings.mediaType
            }/${username}/${settings.authToken}/${settings.patreonAuth || ""}`;
          }

          data = await fetchData(url);
          if (data && data.timeline && data.timeline.length > 0) {
            cacheManager.set(cacheKey, data, true);
          }
        }
        if (data.timeline && data.timeline.length > 0) {
          mediaData.mediaItems = data.timeline;
          mediaData.hasMore = data.metadata.has_more;
          mediaData.totalItems = data.total_urls;

          mediaData.batchData[mediaData.currentPage] = [...data.timeline];

          const currentBatchKeys = Object.keys(mediaData.batchData).map(Number);
          for (const batchNum of currentBatchKeys) {
            if (batchNum > mediaData.currentPage) {
              delete mediaData.batchData[batchNum];
            }
          }
          mediaData.allMediaItems = [];
          const maxBatch = Math.max(
            ...Object.keys(mediaData.batchData).map(Number)
          );
          for (let i = 0; i <= maxBatch; i++) {
            if (mediaData.batchData[i]) {
              mediaData.allMediaItems = [
                ...mediaData.allMediaItems,
                ...mediaData.batchData[i],
              ];
            }
          }

          const mediaTypeLabel = getMediaTypeLabel(settings.mediaType);

          if (settings.batchEnabled) {
            const accountIconHtml = createAccountIcon(
              settings.darkTheme
            ).outerHTML;
            const mediaIconHtml = createMediaTypeIcon(
              settings.mediaType,
              settings.darkTheme
            ).outerHTML;
            const totalItemsIconHtml = createTotalItemsIcon(
              settings.darkTheme
            ).outerHTML;
            infoContainer.innerHTML = `
              <div style="margin-bottom: 8px; display: flex; align-items: center;">${accountIconHtml}<strong style="margin-right: 6px;">Account:</strong><span>${
              data.account_info.name
            }</span></div>
              <div style="margin-bottom: 8px; display: flex; align-items: center;">${mediaIconHtml}<strong style="margin-right: 6px;">${mediaTypeLabel} Found:</strong><span>${formatNumber(
              data.total_urls
            )}</span></div>
              <div style="margin-bottom: 8px; display: flex; align-items: center;">${totalItemsIconHtml}<strong style="margin-right: 6px;">Total Items:</strong><span>${formatNumber(
              mediaData.allMediaItems.length
            )}</span></div>
            `;
            const currentBatchIconHtml = createCurrentBatchIcon(
              settings.darkTheme
            ).outerHTML;
            currentBatchLabel.innerHTML = `<div style="display: flex; align-items: center;">${currentBatchIconHtml}<strong style="margin-right: 6px; color: ${
              settings.darkTheme ? "#ffffff" : "#475569"
            };">Current Batch:</strong><span style="color: ${
              settings.darkTheme ? "#ffffff" : "#475569"
            };">${mediaData.currentPage + 1}</span></div>`;

            const totalBatches = Math.ceil(
              data.total_urls / settings.batchSize
            );
            const totalZipIconHtml = createTotalZipIcon(
              settings.darkTheme
            ).outerHTML;
            totalBatchLabel.innerHTML = `<div style="display: flex; align-items: center;">${totalZipIconHtml}<strong style="margin-right: 6px; color: ${
              settings.darkTheme ? "#ffffff" : "#475569"
            };">Total ZIP ${
              totalBatches === 1 ? "File" : "Files"
            }:</strong><span style="color: ${
              settings.darkTheme ? "#ffffff" : "#475569"
            };">${totalBatches}</span></div>`;
            batchInfoContainer.style.display = "flex";
            batchNavContainer.style.display = "flex";
          } else {
            const currentPart =
              Math.floor(mediaData.allMediaItems.length / 500) + 1;
            const accountIconHtml = createAccountIcon(
              settings.darkTheme
            ).outerHTML;
            const mediaIconHtml = createMediaTypeIcon(
              settings.mediaType,
              settings.darkTheme
            ).outerHTML;
            const totalZipIconHtml = createTotalZipIcon(
              settings.darkTheme
            ).outerHTML;
            infoContainer.innerHTML = `
              <div style="margin-bottom: 8px; display: flex; align-items: center;">${accountIconHtml}<strong style="margin-right: 6px;">Account:</strong><span>${
              data.account_info.name
            }</span></div>
              <div style="margin-bottom: 8px; display: flex; align-items: center;">${mediaIconHtml}<strong style="margin-right: 6px;">${mediaTypeLabel} Found:</strong><span>${formatNumber(
              data.total_urls
            )}</span></div>
              <div style="margin-top: 8px; display: flex; align-items: center;">${totalZipIconHtml}<strong style="margin-right: 6px;">Total ZIP ${
              currentPart === 1 ? "File" : "Files"
            }:</strong><span>${currentPart}</span></div>
            `;

            batchInfoContainer.style.display = "none";
            batchNavContainer.style.display = "none";
          }

          infoContainer.style.display = "block";

          if (settings.batchEnabled) {
            buttonContainer.style.display = "none";
          } else {
            buttonContainer.innerHTML = "";
            buttonContainer.appendChild(downloadButton);
            buttonContainer.style.display = "block";
          }
          if (settings.batchEnabled && mediaData.hasMore) {
            nextBatchButton.style.display = "block";
          }
          if (
            settings.batchEnabled &&
            mediaData.currentPage > getSettings().startingBatch
          ) {
            prevBatchButton.style.display = "block";
          }

          reassignDownloadHandlers();

          fetchButton.disabled = false;
          const currentMediaTypeLabel = getMediaTypeLabel(
            settings.mediaType
          ).toLowerCase();

          const updatedFetchButtonText = settings.autoBatchEnabled
            ? "Auto Fetch"
            : settings.mediaType === "all"
            ? "Fetch Media"
            : `Fetch ${
                currentMediaTypeLabel === "gif"
                  ? "GIF"
                  : currentMediaTypeLabel.charAt(0).toUpperCase() +
                    currentMediaTypeLabel.slice(1)
              }`;

          fetchButton.innerHTML = "";
          let updatedFetchIcon1 = fetchIcon.cloneNode(true);
          fetchButton.appendChild(updatedFetchIcon1);
          fetchButton.appendChild(
            document.createTextNode(updatedFetchButtonText)
          );

          console.log("Fetch success - Next Batch Debug:", {
            hasMore: mediaData.hasMore,
            nextBatchClicked: mediaData.nextBatchClicked,
            shouldShowPopup: !mediaData.hasMore && mediaData.nextBatchClicked,
            autoBatchEnabled: settings.autoBatchEnabled,
          });

          if (
            !mediaData.hasMore &&
            mediaData.nextBatchClicked &&
            !settings.autoBatchEnabled
          ) {
            console.log(
              "Showing next batch completion popup after successful fetch"
            );
            createInfoDialog(
              "Next batch has completed! All available batches have been fetched."
            );
          }

          if (mediaData.nextBatchClicked) {
            console.log(
              "Resetting nextBatchClicked flag after successful fetch"
            );
            mediaData.nextBatchClicked = false;
          }

          if (settings.autoBatchEnabled && !autoBatchCancelled) {
            prevBatchButton.style.display = "none";
            nextBatchButton.style.display = "none";
            stopFetchButton.style.display = "block";
            if (mediaData.hasMore) {
              setTimeout(() => {
                if (settings.autoBatchEnabled && !autoBatchCancelled) {
                  mediaData.currentPage++;
                  fetchButton.click();
                }
              }, 1000);
            } else {
              stopFetchButton.style.display = "none";
              prevBatchButton.style.display =
                mediaData.currentPage > 0 ? "block" : "none";
              nextBatchButton.style.display = "none";
              autoBatchStarted = false;

              createInfoDialog(
                "Auto fetch has completed! All available batches have been fetched."
              );
            }
          }
        } else {
          console.log("No media found branch:", {
            nextBatchClicked: mediaData.nextBatchClicked,
          });

          if (mediaData.nextBatchClicked) {
            console.log(
              "Showing next batch completion popup from no media found branch"
            );
            createInfoDialog(
              "Next batch has completed! All available batches have been fetched."
            );
            mediaData.nextBatchClicked = false;
          }

          infoContainer.innerHTML =
            '<div style="color: #ef4444;">No media found, invalid token, or invalid Patreon authentication.</div>';
          infoContainer.style.display = "block";
          fetchButton.disabled = false;
          const currentMediaTypeLabel = getMediaTypeLabel(
            settings.mediaType
          ).toLowerCase();
          const updatedFetchButtonText =
            settings.mediaType === "all"
              ? "Fetch Media"
              : `Fetch ${
                  currentMediaTypeLabel === "gif"
                    ? "GIF"
                    : currentMediaTypeLabel.charAt(0).toUpperCase() +
                      currentMediaTypeLabel.slice(1)
                }`;

          fetchButton.innerHTML = "";
          let updatedFetchIcon2 = fetchIcon.cloneNode(true);
          fetchButton.appendChild(updatedFetchIcon2);
          fetchButton.appendChild(
            document.createTextNode(updatedFetchButtonText)
          );
        }
      } catch (error) {
        infoContainer.innerHTML = `<div style="color: #ef4444;">Error: ${error.message}</div>`;
        infoContainer.style.display = "block";
        fetchButton.disabled = false;

        if (mediaData.currentPage > getSettings().startingBatch) {
          prevBatchButton.style.display = "block";
        }

        const currentMediaTypeLabel = getMediaTypeLabel(
          settings.mediaType
        ).toLowerCase();

        const updatedFetchButtonText =
          settings.mediaType === "all"
            ? "Fetch Media"
            : `Fetch ${
                currentMediaTypeLabel === "gif"
                  ? "GIF"
                  : currentMediaTypeLabel.charAt(0).toUpperCase() +
                    currentMediaTypeLabel.slice(1)
              }`;

        fetchButton.innerHTML = "";
        let updatedFetchIcon3 = fetchIcon.cloneNode(true);
        fetchButton.appendChild(updatedFetchIcon3);
        fetchButton.appendChild(
          document.createTextNode(updatedFetchButtonText)
        );
      }
    });

    nextBatchButton.addEventListener("click", () => {
      console.log("Next Batch Button clicked, setting flag to true");
      mediaData.nextBatchClicked = true;
      mediaData.currentPage++;

      autoBatchCancelled = true;
      autoBatchStarted = false;

      const settings = getSettings();
      const cacheKey = `${settings.timelineType}_${settings.mediaType}_${username}_${mediaData.currentPage}_${settings.batchSize}_batch_${settings.batchEnabled}`;
      const cachedData = cacheManager.get(cacheKey);

      if (cachedData && cachedData.timeline && cachedData.timeline.length > 0) {
        mediaData.mediaItems = cachedData.timeline;
        mediaData.hasMore = cachedData.metadata.has_more;
        mediaData.totalItems = cachedData.total_urls;

        mediaData.batchData[mediaData.currentPage] = [...cachedData.timeline];

        mediaData.allMediaItems = [];
        const maxBatch = Math.max(
          ...Object.keys(mediaData.batchData).map(Number)
        );
        for (let i = 0; i <= maxBatch; i++) {
          if (mediaData.batchData[i]) {
            mediaData.allMediaItems = [
              ...mediaData.allMediaItems,
              ...mediaData.batchData[i],
            ];
          }
        }

        refreshInfoDisplays();

        if (settings.batchEnabled) {
          prevBatchButton.style.display =
            mediaData.currentPage > 0 ? "block" : "none";
          nextBatchButton.style.display = mediaData.hasMore ? "block" : "none";
          infoContainer.style.display = "block";
          buttonContainer.style.display = "none";
          batchInfoContainer.style.display = "flex";
          batchNavContainer.style.display = "flex";
        }

        const currentBatchIconHtml = createCurrentBatchIcon(
          settings.darkTheme
        ).outerHTML;
        currentBatchLabel.innerHTML = `<div style="display: flex; align-items: center;">${currentBatchIconHtml}<strong style="margin-right: 6px; color: ${
          settings.darkTheme ? "#ffffff" : "#475569"
        };">Current Batch:</strong><span style="color: ${
          settings.darkTheme ? "#ffffff" : "#475569"
        };">${mediaData.currentPage + 1}</span></div>`;
        const totalBatches = Math.ceil(
          cachedData.total_urls / settings.batchSize
        );
        const totalZipIconHtml = createTotalZipIcon(
          settings.darkTheme
        ).outerHTML;
        totalBatchLabel.innerHTML = `<div style="display: flex; align-items: center;">${totalZipIconHtml}<strong style="margin-right: 6px; color: ${
          settings.darkTheme ? "#ffffff" : "#475569"
        };">Total ZIP ${
          totalBatches === 1 ? "File" : "Files"
        }:</strong><span style="color: ${
          settings.darkTheme ? "#ffffff" : "#475569"
        };">${totalBatches}</span></div>`;
        console.log("Next Batch Debug:", {
          hasMore: mediaData.hasMore,
          nextBatchClicked: mediaData.nextBatchClicked,
          shouldShowPopup: !mediaData.hasMore && mediaData.nextBatchClicked,
        });

        if (!mediaData.hasMore && mediaData.nextBatchClicked) {
          console.log("Showing next batch completion popup");
          createInfoDialog(
            "Next batch has completed! All available batches have been fetched."
          );
        }
        console.log("Resetting nextBatchClicked flag to false");
        mediaData.nextBatchClicked = false;

        reassignDownloadHandlers();
      } else {
        const wasAutoBatchEnabled = getSettings().autoBatchEnabled;

        if (wasAutoBatchEnabled) {
          const newSettings = getSettings();
          newSettings.autoBatchEnabled = false;
          saveSettings(newSettings);
        }

        fetchButton.click();

        if (wasAutoBatchEnabled) {
          setTimeout(() => {
            const newSettings = getSettings();
            newSettings.autoBatchEnabled = true;
            saveSettings(newSettings);

            autoBatchCancelled = true;
            autoBatchStarted = false;
          }, 100);
        }
      }
    });

    prevBatchButton.addEventListener("click", () => {
      if (mediaData.currentPage > 0) {
        mediaData.currentPage--;

        autoBatchCancelled = true;
        autoBatchStarted = false;

        const settings = getSettings();
        const cacheKey = `${settings.timelineType}_${settings.mediaType}_${username}_${mediaData.currentPage}_${settings.batchSize}_batch_${settings.batchEnabled}`;
        let data = cacheManager.get(cacheKey);

        if (data && data.timeline && data.timeline.length > 0) {
          mediaData.mediaItems = data.timeline;
          mediaData.hasMore = data.metadata.has_more;
          mediaData.totalItems = data.total_urls;

          mediaData.batchData[mediaData.currentPage] = [...data.timeline];

          const currentBatchKeys = Object.keys(mediaData.batchData).map(Number);
          for (const batchNum of currentBatchKeys) {
            if (batchNum > mediaData.currentPage) {
              delete mediaData.batchData[batchNum];
            }
          }
          mediaData.allMediaItems = [];
          const maxBatch = Math.max(
            ...Object.keys(mediaData.batchData).map(Number)
          );
          for (let i = 0; i <= maxBatch; i++) {
            if (mediaData.batchData[i]) {
              mediaData.allMediaItems = [
                ...mediaData.allMediaItems,
                ...mediaData.batchData[i],
              ];
            }
          }

          const mediaTypeLabel = getMediaTypeLabel(settings.mediaType);

          if (settings.batchEnabled) {
            const accountIconHtml = createAccountIcon(
              settings.darkTheme
            ).outerHTML;
            const mediaIconHtml = createMediaTypeIcon(
              settings.mediaType,
              settings.darkTheme
            ).outerHTML;
            const totalItemsIconHtml = createTotalItemsIcon(
              settings.darkTheme
            ).outerHTML;
            infoContainer.innerHTML = `
              <div style="margin-bottom: 8px; display: flex; align-items: center;">${accountIconHtml}<strong style="margin-right: 6px;">Account:</strong><span>${
              data.account_info.name
            }</span></div>
              <div style="margin-bottom: 8px; display: flex; align-items: center;">${mediaIconHtml}<strong style="margin-right: 6px;">${mediaTypeLabel} Found:</strong><span>${formatNumber(
              data.total_urls
            )}</span></div>
              <div style="margin-bottom: 8px; display: flex; align-items: center;">${totalItemsIconHtml}<strong style="margin-right: 6px;">Total Items:</strong><span>${formatNumber(
              mediaData.allMediaItems.length
            )}</span></div>
            `;

            const currentBatchIconHtml = createCurrentBatchIcon(
              settings.darkTheme
            ).outerHTML;
            currentBatchLabel.innerHTML = `<div style="display: flex; align-items: center;">${currentBatchIconHtml}<strong style="margin-right: 6px; color: ${
              settings.darkTheme ? "#ffffff" : "#475569"
            };">Current Batch:</strong><span style="color: ${
              settings.darkTheme ? "#ffffff" : "#475569"
            };">${mediaData.currentPage + 1}</span></div>`;

            const totalBatches = Math.ceil(
              data.total_urls / settings.batchSize
            );
            const totalZipIconHtml = createTotalZipIcon(
              settings.darkTheme
            ).outerHTML;
            totalBatchLabel.innerHTML = `<div style="display: flex; align-items: center;">${totalZipIconHtml}<strong style="margin-right: 6px; color: ${
              settings.darkTheme ? "#ffffff" : "#475569"
            };">Total ZIP ${
              totalBatches === 1 ? "File" : "Files"
            }:</strong><span style="color: ${
              settings.darkTheme ? "#ffffff" : "#475569"
            };">${totalBatches}</span></div>`;
            batchInfoContainer.style.display = "flex";
            batchNavContainer.style.display = "flex";
          }

          infoContainer.style.display = "block";

          if (settings.batchEnabled) {
            buttonContainer.style.display = "none";
          } else {
            buttonContainer.innerHTML = "";
            buttonContainer.appendChild(downloadButton);
            buttonContainer.style.display = "block";
          }
          if (settings.batchEnabled && mediaData.hasMore) {
            nextBatchButton.style.display = "block";
          }
          if (settings.batchEnabled && mediaData.currentPage > 0) {
            prevBatchButton.style.display = "block";
          }
          reassignDownloadHandlers();
        } else {
          fetchButton.click();
        }
      }
    });

    function chunkMediaItems(items) {
      const chunks = [];
      for (let i = 0; i < items.length; i += 500) {
        chunks.push(items.slice(i, i + 500));
      }
      return chunks;
    }

    function reassignDownloadHandlers() {
      const settings = getSettings();
      if (settings.batchEnabled) {
        downloadCurrentButton.onclick = () => downloadMedia(false);
        downloadAllButton.onclick = () => downloadMedia(true);
      }
    }

    async function downloadMedia(downloadAll) {
      if (mediaData.downloading) return;

      mediaData.downloading = true;

      const settings = getSettings();
      const timestamp = getCurrentTimestamp();

      let itemsToDownload;
      if (downloadAll) {
        itemsToDownload = mediaData.allMediaItems;
      } else {
        itemsToDownload = mediaData.mediaItems;
      }

      mediaData.totalToDownload = itemsToDownload.length;
      mediaData.totalDownloaded = 0;

      progressText.textContent = `Downloading 0/${formatNumber(
        mediaData.totalToDownload
      )}`;
      progressFill.style.width = "0%";
      progressContainer.style.display = "block";

      fetchButton.disabled = true;
      if (settings.batchEnabled) {
        downloadCurrentButton.disabled = true;
        downloadAllButton.disabled = true;
      } else {
        downloadButton.disabled = true;
      }
      nextBatchButton.disabled = true;

      const chunks = chunkMediaItems(itemsToDownload);

      for (let chunkIndex = 0; chunkIndex < chunks.length; chunkIndex++) {
        const chunk = chunks[chunkIndex];

        if (chunk.length === 1 && chunks.length === 1) {
          try {
            const item = chunk[0];
            const formattedDate = formatDate(item.date);
            const baseFilename = `${username}_${formattedDate}_${item.tweet_id}`;
            const fileExtension = item.type === "photo" ? "jpg" : "mp4";

            const filename = `${baseFilename}.${fileExtension}`;

            progressText.textContent = `Downloading 0/1`;

            const blob = await fetchBinary(item.url);

            const downloadLink = document.createElement("a");
            downloadLink.href = URL.createObjectURL(blob);
            downloadLink.download = filename;
            document.body.appendChild(downloadLink);
            downloadLink.click();
            document.body.removeChild(downloadLink);

            mediaData.totalDownloaded = 1;
            progressText.textContent = `Downloading 1/1`;
            progressFill.style.width = "100%";

            continue;
          } catch (error) {}
        }

        const zip = new JSZip();

        const hasImages = chunk.some((item) => item.type === "photo");
        const hasVideos = chunk.some((item) => item.type === "video");
        const hasGifs = chunk.some((item) => item.type === "gif");

        let imageFolder, videoFolder, gifFolder;
        if (settings.mediaType === "all") {
          if (hasImages) imageFolder = zip.folder("image");
          if (hasVideos) videoFolder = zip.folder("video");
          if (hasGifs) gifFolder = zip.folder("gif");
        }

        const filenameMap = {};

        const downloadInBatches = async (items, batchSize) => {
          for (let i = 0; i < items.length; i += batchSize) {
            const batch = items.slice(i, i + batchSize);

            const downloadPromises = batch.map(async (item) => {
              try {
                const formattedDate = formatDate(item.date);
                let baseFilename = `${username}_${formattedDate}_${item.tweet_id}`;

                if (filenameMap[baseFilename] !== undefined) {
                  filenameMap[baseFilename]++;
                  baseFilename = `${baseFilename}_${String(
                    filenameMap[baseFilename]
                  ).padStart(2, "0")}`;
                } else {
                  filenameMap[baseFilename] = 0;
                }

                const fileExtension = item.type === "photo" ? "jpg" : "mp4";
                const filename = `${baseFilename}.${fileExtension}`;

                const blob = await fetchBinary(item.url);

                if (settings.mediaType === "all") {
                  if (item.type === "photo") {
                    imageFolder.file(filename, blob);
                  } else if (item.type === "video") {
                    videoFolder.file(filename, blob);
                  } else if (item.type === "gif") {
                    gifFolder.file(filename, blob);
                  }
                } else {
                  zip.file(filename, blob);
                }

                mediaData.totalDownloaded++;
                const completedCount = mediaData.totalDownloaded;
                progressText.textContent = `Downloading ${formatNumber(
                  completedCount
                )}/${formatNumber(mediaData.totalToDownload)}`;
                progressFill.style.width = `${
                  (completedCount / mediaData.totalToDownload) * 100
                }%`;
              } catch (error) {
                console.error("Error downloading item:", error);
                mediaData.totalDownloaded++;
              }
            });

            await Promise.all(downloadPromises);
          }
        };

        await downloadInBatches(chunk, settings.concurrentDownloads);

        progressText.textContent = `Creating ZIP file ${chunkIndex + 1}/${
          chunks.length
        }...`;

        try {
          const zipBlob = await zip.generateAsync({ type: "blob" });

          let zipFilename;
          if (chunks.length === 1 && chunk.length < 500) {
            zipFilename = `${username}_${timestamp}.zip`;
          } else if (settings.batchEnabled && !downloadAll) {
            zipFilename = `${username}_${timestamp}_part_${String(
              mediaData.currentPage + 1
            ).padStart(2, "0")}.zip`;
          } else {
            zipFilename = `${username}_${timestamp}_part_${String(
              chunkIndex + 1
            ).padStart(2, "0")}.zip`;
          }

          const downloadLink = document.createElement("a");
          downloadLink.href = URL.createObjectURL(zipBlob);
          downloadLink.download = zipFilename;
          document.body.appendChild(downloadLink);
          downloadLink.click();
          document.body.removeChild(downloadLink);
        } catch (error) {
          progressText.textContent = `Error creating ZIP ${chunkIndex + 1}: ${
            error.message
          }`;
        }
      }
      mediaData.downloading = false;
      progressContainer.style.display = "none";
      fetchButton.disabled = false;
      if (settings.batchEnabled) {
        downloadCurrentButton.disabled = false;
        downloadAllButton.disabled = false;
        reassignDownloadHandlers();
      } else {
        downloadButton.disabled = false;
      }
      nextBatchButton.disabled = false;
      createInfoDialog(
        "All media downloaded successfully.",
        "Download Complete"
      );
      return;
    }

    document.body.appendChild(modal);
  }

  function extractUsername() {
    const pathParts = window.location.pathname
      .split("/")
      .filter((part) => part);
    if (pathParts.length > 0) {
      return pathParts[0];
    }
    return null;
  }

  function insertDownloadIconOnWithheld() {
    const withheldHeaders = document.querySelectorAll(
      '[data-testid="empty_state_header_text"]'
    );

    withheldHeaders.forEach((headerDiv) => {
      if (!headerDiv.querySelector(".download-icon-withheld")) {
        const accountWithheldSpan = headerDiv.querySelector("span");
        if (
          accountWithheldSpan &&
          accountWithheldSpan.textContent.includes("Account Withheld")
        ) {
          const username = extractUsername();
          if (!username) return;

          const downloadIcon = createDownloadIcon();

          const iconDiv = document.createElement("div");
          iconDiv.className =
            "download-icon-withheld css-175oi2r r-1awozwy r-xoduu5";
          iconDiv.style.cssText = `
                    display: inline-flex;
                    align-items: center;
                    margin-left: 12px;
                    gap: 6px;
                    padding: 0 3px;
                    transition: transform 0.2s, color 0.2s;
                `;
          iconDiv.appendChild(downloadIcon);

          iconDiv.addEventListener("mouseenter", () => {
            iconDiv.style.transform = "scale(1.1)";
            iconDiv.style.color = "#0ea5e9";
          });

          iconDiv.addEventListener("mouseleave", () => {
            iconDiv.style.transform = "scale(1)";
            iconDiv.style.color = "";
          });

          iconDiv.addEventListener("click", (e) => {
            e.stopPropagation();
            createModal(username);
          });

          headerDiv.appendChild(iconDiv);
        }
      }
    });
  }

  function insertDownloadIcon() {
    const usernameDivs = document.querySelectorAll('[data-testid="UserName"]');

    usernameDivs.forEach((usernameDiv) => {
      if (!usernameDiv.querySelector(".download-icon")) {
        const username = extractUsername();
        if (!username) return;

        const verifiedButton = usernameDiv
          .querySelector('[aria-label*="verified"], [aria-label*="Verified"]')
          ?.closest("button");

        const targetElement = verifiedButton
          ? verifiedButton.parentElement
          : usernameDiv.querySelector(".css-1jxf684")?.closest("span");

        if (targetElement) {
          const downloadIcon = createDownloadIcon();

          const iconDiv = document.createElement("div");
          iconDiv.className = "download-icon css-175oi2r r-1awozwy r-xoduu5";
          iconDiv.style.cssText = `
                    display: inline-flex;
                    align-items: center;
                    margin-left: 6px;
                    margin-right: 6px;
                    gap: 6px;
                    padding: 0 3px;
                    transition: transform 0.2s, color 0.2s;
                `;
          iconDiv.appendChild(downloadIcon);

          iconDiv.addEventListener("mouseenter", () => {
            iconDiv.style.transform = "scale(1.1)";
            iconDiv.style.color = "#0ea5e9";
          });

          iconDiv.addEventListener("mouseleave", () => {
            iconDiv.style.transform = "scale(1)";
            iconDiv.style.color = "";
          });

          iconDiv.addEventListener("click", (e) => {
            e.stopPropagation();
            createModal(username);
          });

          const wrapperDiv = document.createElement("div");
          wrapperDiv.style.cssText = `
                    display: inline-flex;
                    align-items: center;
                    gap: 4px;
                `;
          wrapperDiv.appendChild(iconDiv);
          targetElement.parentNode.insertBefore(
            wrapperDiv,
            targetElement.nextSibling
          );
        }
      }
    });
  }

  insertDownloadIcon();
  insertDownloadIconOnWithheld();

  function checkForUserNameElement() {
    const usernameDivs = document.querySelectorAll('[data-testid="UserName"]');
    if (usernameDivs.length > 0) {
      insertDownloadIcon();
    }

    const withheldHeaders = document.querySelectorAll(
      '[data-testid="empty_state_header_text"]'
    );
    if (withheldHeaders.length > 0) {
      insertDownloadIconOnWithheld();
    }
  }

  setInterval(checkForUserNameElement, 100);

  let lastUrl = location.href;
  let lastUsername = extractUsername();

  function checkForChanges() {
    const currentUrl = location.href;
    const currentUsername = extractUsername();

    if (currentUrl !== lastUrl || currentUsername !== lastUsername) {
      lastUrl = currentUrl;
      lastUsername = currentUsername;

      document.querySelectorAll(".download-icon").forEach((icon) => {
        const wrapper = icon.closest("div[style*='display: inline-flex']");
        if (wrapper) {
          wrapper.remove();
        }
      });

      document.querySelectorAll(".download-icon-withheld").forEach((icon) => {
        icon.remove();
      });

      setTimeout(() => {
        insertDownloadIcon();
        insertDownloadIconOnWithheld();
      }, 50);
    }
  }

  const observer = new MutationObserver(() => {
    checkForChanges();
    checkForUserNameElement();
  });

  observer.observe(document.body, {
    childList: true,
    subtree: true,
    attributes: true,
    characterData: true,
  });

  setInterval(checkForChanges, 300);

  const originalPushState = history.pushState;
  const originalReplaceState = history.replaceState;

  history.pushState = function () {
    originalPushState.apply(this, arguments);
    checkForChanges();
    insertDownloadIcon();
    insertDownloadIconOnWithheld();
  };

  history.replaceState = function () {
    originalReplaceState.apply(this, arguments);
    checkForChanges();
    insertDownloadIcon();
    insertDownloadIconOnWithheld();
  };

  window.addEventListener("popstate", () => {
    checkForChanges();
    insertDownloadIcon();
    insertDownloadIconOnWithheld();
  });
})();
长期地址
遇到问题?请前往 GitHub 提 Issues。