Greasy Fork

Greasy Fork is available in English.

Twitter/X Media Batch Downloader

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

当前为 2025-06-23 提交的版本,查看 最新版本

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

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

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

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

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

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

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

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

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

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

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

// ==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.0
// @author       afkarxyz
// @namespace    https://github.com/afkarxyz/userscripts/
// @supportURL   https://github.com/afkarxyz/userscripts/issues
// @license      MIT
// @match        https://twitter.com/*
// @match        https://x.com/*
// @grant        GM_xmlhttpRequest
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_download
// @connect      api.gallerydl.web.id
// @connect      backup.gallerydl.web.id
// @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.gallerydl.web.id",
    BACKUP: "https://backup.gallerydl.web.id"
  }

  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"
      }
    }
  }

  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 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: 16px auto;
        width: 50%;
        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)

    mainContent.appendChild(fetchButton)
    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: 16px;
    `

    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: 16px;
    `

    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) {
            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"
        }
    })

    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" },
      { value: "timeline", label: "Post" },
      { value: "tweets", label: "Tweets" },
      { value: "with_replies", label: "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";
    });
    
    fetchTabContent.appendChild(batchGroup)
    fetchTabContent.appendChild(autoBatchGroup)
    fetchTabContent.appendChild(batchSizeGroup)
    fetchTabContent.appendChild(startingBatchGroup)
    fetchTabContent.appendChild(mediaTypeGroup)
    fetchTabContent.appendChild(timelineTypeGroup)

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

    advancedTabContent.appendChild(darkThemeGroup);
    advancedTabContent.appendChild(apiServerGroup);
    advancedTabContent.appendChild(cacheDurationGroup);
    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 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()

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

  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()
        }
      })

      setTimeout(insertDownloadIcon, 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()
  }

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

  window.addEventListener("popstate", () => {
    checkForChanges()
    insertDownloadIcon()
  })
})()