XEnhancer

XEnhancer nâng cao trải nghiệm duyệt Twitter/X — lưu phương tiện chỉ với một cú nhấp, định dạng dấu thời gian rõ ràng và tối ưu hóa luồng xã hội của bạn dễ dàng.

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

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

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

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

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

You will need to install a user script manager extension to install this script.

(Tôi đã có Trình quản lý tập lệnh người dùng, hãy cài đặt nó!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name        XEnhancer
// @name:ar     XEnhancer
// @name:bg     XEnhancer
// @name:ckb    XEnhancer
// @name:cs     XEnhancer
// @name:da     XEnhancer
// @name:de     XEnhancer
// @name:el     XEnhancer
// @name:en     XEnhancer
// @name:eo     XEnhancer
// @name:es     XEnhancer
// @name:es-419 XEnhancer
// @name:fi     XEnhancer
// @name:fr     XEnhancer
// @name:fr-CA  XEnhancer
// @name:he     XEnhancer
// @name:hr     XEnhancer
// @name:hu     XEnhancer
// @name:id     XEnhancer
// @name:it     XEnhancer
// @name:ja     XEnhancer
// @name:ka     XEnhancer
// @name:ko     XEnhancer
// @name:nb     XEnhancer
// @name:nl     XEnhancer
// @name:pl     XEnhancer
// @name:pt-BR  XEnhancer
// @name:ro     XEnhancer
// @name:ru     XEnhancer
// @name:sk     XEnhancer
// @name:sr     XEnhancer
// @name:sv     XEnhancer
// @name:th     XEnhancer
// @name:tr     XEnhancer
// @name:uk     XEnhancer
// @name:ug     XEnhancer
// @name:vi     XEnhancer
// @description        XEnhancer empowers your Twitter/X browsing — save media in one click, format timestamps for clarity, and streamline your social feed with ease.
// @description:ar     XEnhancer يعزز تجربتك على تويتر (X) — احفظ الوسائط بنقرة واحدة، نسق الطوابع الزمنية لتوضيح أفضل، ونظّم تغذيتك الاجتماعية بسهولة.
// @description:bg     XEnhancer подобрява разглеждането на Twitter (X) — запазвайте медии с едно кликване, форматирайте времевите отметки за яснота и оптимизирайте социалния си поток лесно.
// @description:ckb    XEnhancer بەرز دەکاتەوە بەرەوپێشگای تیوتر (X) — وێنە و ڤیدیۆکان بە یەک کرتە پاشکەوت بکە، کاتی ڕووداوەکان ڕوون بکەرەوە، و فیدەکانی کۆمەڵایەتی بە ئاسانیدا ڕێکبخە.
// @description:cs     XEnhancer vylepšuje prohlížení Twitteru (X) — ukládejte média jedním kliknutím, formátujte časová razítka pro přehlednost a snadno optimalizujte svůj sociální feed.
// @description:da     XEnhancer forbedrer din oplevelse på Twitter (X) — gem medier med et enkelt klik, formater tidsstempler for klarhed, og strømlin din sociale feed med lethed.
// @description:de     XEnhancer verbessert Ihr Twitter/X-Erlebnis — speichern Sie Medien mit einem Klick, formatieren Sie Zeitstempel zur besseren Übersicht und optimieren Sie Ihren Social-Feed mühelos.
// @description:el     Το XEnhancer ενισχύει την περιήγησή σας στο Twitter (X) — αποθηκεύστε πολυμέσα με ένα κλικ, μορφοποιήστε χρονικές σημάνσεις για μεγαλύτερη σαφήνεια και οργανώστε εύκολα το κοινωνικό σας feed.
// @description:en     XEnhancer empowers your Twitter/X browsing — save media in one click, format timestamps for clarity, and streamline your social feed with ease.
// @description:eo     XEnhancer plibonigas vian retumadon de Twitter (X) — konservu amaskomunikilojn per unu klako, formatu tempstampon por pli granda klareco, kaj faciligu vian socian fluon.
// @description:es     XEnhancer mejora tu experiencia en Twitter (X): guarda medios con un clic, formatea las marcas de tiempo para mayor claridad y optimiza tu feed social con facilidad.
// @description:es-419 XEnhancer mejora tu navegación en Twitter (X) — guarda medios con un clic, formatea las marcas de tiempo para mayor claridad y agiliza tu feed social fácilmente.
// @description:fi     XEnhancer parantaa Twitter (X) -selailuasi — tallenna media yhdellä napsautuksella, muotoile aikaleimat selkeyden lisäämiseksi ja tehosta sosiaalista syötettäsi vaivattomasti.
// @description:fr     XEnhancer améliore votre navigation sur Twitter (X) — enregistrez des médias en un clic, formatez les horodatages pour plus de clarté et optimisez facilement votre flux social.
// @description:fr-CA  XEnhancer améliore votre expérience sur Twitter (X) — sauvegardez les médias en un clic, formatez les horodatages pour plus de clarté et simplifiez votre flux social facilement.
// @description:he     XEnhancer משדרג את הגלישה שלך ב-Twitter (X) — שמור מדיה בלחיצה אחת, עצב חותמות זמן להבהרה וייעל את הפיד החברתי שלך בקלות.
// @description:hr     XEnhancer poboljšava pregledavanje Twittera (X) — spremite medije jednim klikom, formatirajte vremenske oznake radi preglednosti i pojednostavite svoj društveni feed.
// @description:hu     Az XEnhancer fokozza a Twitter/X böngészést — egy kattintással mentheted a médiát, formázhatod az időbélyegeket az átláthatóság érdekében, és könnyedén optimalizálhatod a közösségi hírcsatornát.
// @description:id     XEnhancer meningkatkan pengalaman menjelajahi Twitter/X — simpan media dengan satu klik, format timestamp untuk kejelasan, dan permudah feed sosial Anda.
// @description:it     XEnhancer potenzia la navigazione su Twitter/X — salva i media con un clic, formatta i timestamp per maggiore chiarezza e ottimizza il tuo feed sociale con facilità.
// @description:ja     XEnhancer は Twitter (X) の閲覧を強化します — メディアをワンクリックで保存し、タイムスタンプを見やすくフォーマットし、ソーシャルフィードを簡単に整理できます。
// @description:ka     XEnhancer აძლიერებს Twitter/X-ს — დაარეგისტრირე მედია ერთ ক্লიკზე, დააწყობ დროის ნიშანებს გასაგებად და გამარტივე სოციალური ფიდი.
// @description:ko     XEnhancer는 Twitter/X 탐색을 강화합니다 — 미디어를 한 번의 클릭으로 저장하고, 타임스탬프를 명확하게 포맷하며, 소셜 피드를 쉽게 정리할 수 있습니다.
// @description:nb     XEnhancer forbedrer din Twitter/X-opplevelse — lagre medier med ett klikk, formater tidsstempler for klarhet, og strømlin feeden din enkelt.
// @description:nl     XEnhancer verbetert je Twitter/X-ervaring — sla media op met één klik, formatteer tijdstempels voor duidelijkheid en stroomlijn je sociale feed eenvoudig.
// @description:pl     XEnhancer usprawnia przeglądanie Twittera (X) — zapisuj media jednym kliknięciem, formatuj znaczniki czasu dla przejrzystości i usprawnij swój feed społecznościowy.
// @description:pt-BR  XEnhancer potencializa sua navegação no Twitter/X — salve mídias com um clique, formate os timestamps para maior clareza e organize seu feed social com facilidade.
// @description:ro     XEnhancer îmbunătățește navigarea pe Twitter/X — salvează media cu un singur clic, formatează timestamp-urile pentru claritate și optimizează-ți feedul social cu ușurință.
// @description:ru     XEnhancer улучшает просмотр Twitter/X — сохраняйте медиа в один клик, форматируйте временные метки для наглядности и упрощайте вашу социальную ленту.
// @description:sk     XEnhancer zlepšuje prehliadanie Twitteru (X) — ukladajte médiá jedným kliknutím, formátujte časové značky pre prehľadnosť a zjednodušte svoj sociálny feed.
// @description:sr     XEnhancer unapređuje pregledanje Twittera (X) — sačuvajte medije jednim klikom, formatirajte vremenske oznake radi preglednosti i olakšajte svoj društveni feed.
// @description:sv     XEnhancer förbättrar din Twitter/X-upplevelse — spara media med ett klick, formatera tidsstämplar för tydlighet och effektivisera ditt sociala flöde enkelt.
// @description:th     XEnhancer ช่วยเพิ่มประสิทธิภาพการใช้งาน Twitter/X — บันทึกสื่อด้วยคลิกเดียว, จัดรูปแบบเวลาสำหรับความชัดเจน, และปรับปรุงฟีดโซเชียลของคุณอย่างง่ายดาย
// @description:tr     XEnhancer, Twitter/X deneyiminizi güçlendirir — medyaları tek tıkla kaydedin, zaman damgalarını netlik için biçimlendirin ve sosyal akışınızı kolayca düzenleyin.
// @description:uk     XEnhancer покращує перегляд Twitter/X — зберігайте медіа одним кліком, форматування часових міток для наочності та спрощення вашої соціальної стрічки.
// @description:ug     XEnhancer Twitter/X تەجرىبىسىڭىزنى كۈچەيتىدۇ — ۋىدىئولارنى ۋە رەسىملەرنى بىر قېتىملىق چېكىش بىلەن ساقلاڭ، ۋاقىت بەلگىلىرىنى ئاچچىق-ئاشكارا قىلىپ بەلگىلەڭ، ۋە ئىجتىمائىي فېدڭىزنى ئاسانلاشتۇرۇڭ.
// @description:vi     XEnhancer nâng cao trải nghiệm duyệt Twitter/X — lưu phương tiện chỉ với một cú nhấp, định dạng dấu thời gian rõ ràng và tối ưu hóa luồng xã hội của bạn dễ dàng.
// @namespace levivi_myself
// @version   1.0.1
// @author    PeterParker, Levivi
// @icon      data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAEBklEQVR4AeyZXVbjMAyFy2wJ3ikro6wMeIc1de7nOh7LlmM7yRwOHDhR4h9ZuleSnbb8OX3zv18CX53An5WBh4eHywY5H5kF+T9LXByeH5OB6/X6eL1en6+T4hneMgZw+X6VVBhk701SXYbA5+fnkzRcRY23rvP9/f1ra3J0PIJ/9vTv7u5ePj4+XFyGAItR5jkpIe2Ta5K6wJ+JehrIGuAR+Es2ZJoVASm/scho3TqMP2lukZfb8O0OAIDcenN3rXUzKF9EvgkeLxUBBkWCRWXKwmbV3FuUSkdA3BLAZkta5TcCHpsuASa8/VACxAm6mUztB2WMIITAZDZoEiTmaK9KkwCregDJhKcTgWGiKeiUAVmUY/CW7upzlcAIQOlcShIAE0AvsgGM5i7ohE5xky1OwmK03V0lwLIRgOhI1+yZFsAeeNkydmR39eoSYLWMUo/GsACak0ORM6eS1lX7oQOeE8f4kI3uNUQAKw7AU36CiCTHbEUC0KzXc/NZz/qWDBOIAMv6NC8w6TT3Q5mxBRCBYd3Sn30OE8CwHFVRFrBnoss8Ip1uuaGH7AWPjSkCLGgBzEkADN2ODJ/1a3amCWDMO6fJBHOISJKpstyYSuLZSJMTjU0EsK8olwDL/QCJclOz9OSsDeNbbhMErPkYZQOQLKiU2ANBWTq0q6NR49VYWLDhtpkAvgSkAhhJpLewom1Isi4/funvkV0EcBxr2UQUEswhIkkprZYbeltlNwEcO1Hu7gdIqtxSprCzRQ4hEKNsSiUCpMQCLunQLjP1upfEIQRAKMCPPHPRmHnJOZk6oZOvmW0fQkBRJLpuOeQAlYXD98NuAoDPQToRNJ9KIwmv3NwAOPbM0C4CA+AXZ+WmJmOH7IfNBNbAU+vIgp4nWdKaFOV4/DKVBJ3UGWxsIgCQljOAq0wuiDBUUdZYuqS7+/2wiYDAm29jCyIB4lsV5RGG6IdGdsvfwiLJpt61H6YJ5AAyXHxAM+CZiwBXoywdCFeZIsvY6MkUgQg+1XFmvPnZXgC7Ud6zH4YJKCJEygN/8gBk5E4iwdrVKKvcVjOV28vbQwQAr7p3fzZ0HOf2U9sjmdsUyVamIJ/slI0ugR54HJdGW32H7Mj7wXwcKW2vEuiAZ9OasiiNl33IioR36qQox0wZu3mmSptNAgL/X37HEQnAVgDlL/1bqQSpvsmU+ulqEhDrobM+WZpotKIsn+FfSzJVHRbMQVJz5nIJxOPSKNIh/TGCdHcJtmYNRBKGXEUggjdK0VHzrI/z/x4DLQWiOnUGllXfHwwBpeisyLxLXkqJaR/xMawjEuGnyNJXp/+eOzAEZDBEWc/wYSx/5ouObOc+Rtu5f0Mgn/gu7V8CX52pvwAAAP//pQZ+UwAAAAZJREFUAwBCEOZ/U8dgbwAAAABJRU5ErkJggg==
// @include   https://x.com/*
// @include   https://twitter.com/*
// @exclude   *://x.com/i/flow/*
// @license   MIT
// @run-at    document-start
// @noframes
// @grant     GM_registerMenuCommand
// @grant     GM_openInTab
// @grant     GM.openInTab
// @grant     GM_addStyle
// @grant     GM_setValue
// @grant     GM_getValue
// @grant     GM_deleteValue
// @grant     GM_xmlhttpRequest
// @grant     GM_download
// ==/UserScript==
(function () {
  'use strict';

  
  /*!
  * Copyright (c) 2026 - 2026, Levivi. All rights reserved.
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and associated documentation files (the "Software"), to deal
  * in the Software without restriction, including without limitation the rights
  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  * copies of the Software, and to permit persons to whom the Software is
  * furnished to do so, subject to the following conditions:
  *
  * The above copyright notice and this permission notice shall be included in
  * all copies or substantial portions of the Software.
  *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  *
  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */


  var css_248z$1 = "li[role=listitem]>div>div>div>div:not(:last-child){filter:none}li[role=listitem]>div>div>div>div+div:last-child{display:none}";

  var css_248z = ".x-master-dl{margin-left:12px;order:99}.x-master-dl:hover>div>div>div>div{color:#1da1f2}.x-master-dl:hover>div>div>div>div>div{background-color:rgba(29,161,242,.1)}.x-master-dl:active>div>div>div>div>div{background-color:rgba(29,161,242,.2)}.x-master-dl:hover svg{color:#1da1f2}.x-master-dl:hover div:first-child:not(:last-child){background-color:rgba(29,161,242,.1)}.x-master-dl:active div:first-child:not(:last-child){background-color:rgba(29,161,242,.2)}.x-master-dl.tmd-media{position:absolute;right:0}.x-master-dl.tmd-media>div{border-radius:99px;display:flex;margin:2px}.x-master-dl.tmd-media>div>div{color:#fff;display:flex;margin:6px}.x-master-dl.tmd-media:hover>div{background-color:hsla(0,0%,100%,.6)}.x-master-dl.tmd-media:hover>div>div{color:#1da1f2}.x-master-dl.tmd-media:not(:hover)>div>div{filter:drop-shadow(0 0 1px #000)}.x-master-dl g{display:none}.x-master-dl.completed g.completed,.x-master-dl.download g.download,.x-master-dl.failed g.failed,.x-master-dl.loading g.loading{display:unset}.x-master-dl.loading svg{animation:spin 1s linear infinite}.x-master-dl.download g.download{color:#1da1f2}.tmd-btn{background-color:#1da1f2;border-radius:99px;color:#fff;padding:0 20px}.tmd-btn,.tmd-tag{display:inline-block}.tmd-tag{background-color:#fff;border:1px solid #1da1f2;border-radius:10px;color:#1da1f2;font-weight:700;margin:5px;padding:0 10px}.tmd-btn:hover{background-color:rgba(29,161,242,.9)}.tmd-tag:hover{background-color:rgba(29,161,242,.1)}.tmd-notifier{background:#fff;border:1px solid #ccc;border-radius:8px;bottom:16px;color:#000;display:none;left:16px;padding:4px;position:fixed}.tmd-notifier.running{align-items:center;display:flex}.tmd-notifier label{align-items:center;display:inline-flex;margin:0 8px}.tmd-notifier label:before{background-position:50%;background-repeat:no-repeat;content:\" \";height:16px;width:32px}.tmd-notifier label:first-child:before{background-image:url(\"data:image/svg+xml;charset=utf8,<svg xmlns=%22http://www.w3.org/2000/svg%22 width=%2216%22 height=%2216%22 viewBox=%220 0 24 24%22><path d=%22M3,14 v5 q0,2 2,2 h14 q2,0 2,-2 v-5 M7,10 l4,4 q1,1 2,0 l4,-4 M12,3 v11%22 fill=%22none%22 stroke=%22%23666%22 stroke-width=%222%22 stroke-linecap=%22round%22 /></svg>\")}.tmd-notifier label:nth-child(2):before{background-image:url(\"data:image/svg+xml;charset=utf8,<svg xmlns=%22http://www.w3.org/2000/svg%22 width=%2216%22 height=%2216%22 viewBox=%220 0 24 24%22><path d=%22M12,2 a1,1 0 0 1 0,20 a1,1 0 0 1 0,-20 M12,5 v7 h6%22 fill=%22none%22 stroke=%22%23999%22 stroke-width=%222%22 stroke-linejoin=%22round%22 stroke-linecap=%22round%22 /></svg>\")}.tmd-notifier label:nth-child(3):before{background-image:url(\"data:image/svg+xml;charset=utf8,<svg xmlns=%22http://www.w3.org/2000/svg%22 width=%2216%22 height=%2216%22 viewBox=%220 0 24 24%22><path d=%22M12,0 a2,2 0 0 0 0,24 a2,2 0 0 0 0,-24%22 fill=%22%23f66%22 stroke=%22none%22 /><path d=%22M14.5,5 a1,1 0 0 0 -5,0 l0.5,9 a1,1 0 0 0 4,0 z M12,17 a2,2 0 0 0 0,5 a2,2 0 0 0 0,-5%22 fill=%22%23fff%22 stroke=%22none%22 /></svg>\")}.x-master-dl.tmd-img{bottom:0;display:none!important;position:absolute;right:0}.x-master-dl.tmd-img>div{background-color:hsla(0,0%,100%,.6);border-radius:99px;display:flex;margin:2px}.x-master-dl.tmd-img>div>div{color:#fff!important;display:flex;margin:6px}.x-master-dl.tmd-img:not(:hover)>div>div{filter:drop-shadow(0 0 1px #000)}.x-master-dl.tmd-img:hover>div>div{color:#1da1f2}.tmd-img.completed,.tmd-img.failed,.tmd-img.loading,:hover>.x-master-dl.tmd-img{display:block!important}.tweet-detail-action-item{width:20%!important}@keyframes spin{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}";

  const language = {
    "zh": {
      "dateFormat": {
        "week": ["日", "一", "二", "三", "四", "五", "六"]
      },
      "download": {
        "download": "下载",
        "completed": "下载完成",
        "tip": "点击下载视频",
        "preparing": "正在准备下载(如果失败,请手动操作)"
      },
      "menuCommand": {
        "settings": "设置",
        "titleDateFormat": "时间格式设置:",
        "buttonClose": "关闭",
        "simplifyMode": "简化模式",
        "turnOn": "打开",
        "turnOff": "关闭"
      }
    },
    "en": {
      "dateFormat": {
        "week": ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]
      },
      "download": {
        "download": "Download",
        "completed": "Download Completed",
        "tip": "Click to download video",
        "preparing": "Preparing to download (if failed, please do it manually)"
      },
      "menuCommand": {
        "settings": "Settings",
        "titleDateFormat": "Time format settings:",
        "buttonClose": "Close",
        "simplifyMode": "Simplify Mode",
        "turnOn": "Turn on",
        "turnOff": "Turn off"
      }
    },
    "ja": {
      "dateFormat": {
        "week": ["日", "月", "火", "水", "木", "金", "土"]
      },
      "download": {
        "download": "ダウンロード",
        "completed": "ダウンロード完了",
        "tip": "クリックしてビデオをダウンロード",
        "preparing": "ダウンロードの準備中(失敗する場合は手動で行ってください)"
      },
      "menuCommand": {
        "settings": "設定",
        "titleDateFormat": "時刻形式の設定:",
        "buttonClose": "閉鎖",
        "simplifyMode": "簡易モード",
        "turnOn": "オン",
        "turnOff": "オフ"
      }
    },
    "fr": {
      "dateFormat": {
        "week": ["Dim", "Lun", "Mar", "Mer", "Jeu", "Ven", "Sam"]
      },
      "download": {
        "download": "télécharger",
        "completed": "éléchargement terminé",
        "tip": "Cliquez pour télécharger la vidéo",
        "preparing": "Préparation du téléchargement (en cas d'échec, veuillez le faire manuellement)"
      },
      "menuCommand": {
        "settings": "installation",
        "titleDateFormat": "Paramètres du format de l'heure :",
        "buttonClose": "fermeture",
        "simplifyMode": "Mode simplifié",
        "turnOn": "Activer",
        "turnOff": "Désactiver"
      }
    },
    "de": {
      "dateFormat": {
        "week": ["Son", "Mon", "Die", "Mit", "Don", "Fre", "Sam"]
      },
      "download": {
        "download": "herunterladen",
        "completed": "Download abgeschlossen",
        "tip": "Klicken Sie hier, um das Video herunterzuladen",
        "preparing": "Vorbereitung für den Download (falls der Download fehlschlägt, führen Sie ihn bitte manuell durch)"
      },
      "menuCommand": {
        "settings": "aufstellen",
        "titleDateFormat": "Einstellungen für das Zeitformat:",
        "buttonClose": "Schließung",
        "simplifyMode": "Vereinfachter Modus",
        "turnOn": "Einschalten",
        "turnOff": "Ausschalten"
      }
    },
    "it": {
      "dateFormat": {
        "week": ["Dom", "Lun", "Mar", "Mer", "Gio", "Ven", "Sab"]
      },
      "download": {
        "download": "scaricamento",
        "completed": "Download completato",
        "tip": "Fare clic per scaricare il video",
        "preparing": "Preparazione per il download (se fallisce, eseguilo manualmente)"
      },
      "menuCommand": {
        "settings": "impostare",
        "titleDateFormat": "Impostazioni del formato dell'ora:",
        "buttonClose": "chiusura",
        "simplifyMode": "Modalità semplificata",
        "turnOn": "Attiva",
        "turnOff": "Disattiva"
      }
    },
    "ko": {
      "dateFormat": {
        "week": ["일", "월", "화", "수", "목", "금", "토"]
      },
      "download": {
        "download": "다운로드",
        "completed": "다운로드 완료",
        "tip": "비디오를 다운로드하려면 클릭하세요",
        "preparing": "다운로드 준비 중 (실패할 경우 수동으로 진행해주세요)"
      },
      "menuCommand": {
        "settings": "설정",
        "titleDateFormat": "시간 형식 설정:",
        "buttonClose": "폐쇄",
        "simplifyMode": "간소화 모드",
        "turnOn": "켜기",
        "turnOff": "끄기"
      }
    },
    "ru": {
      "dateFormat": {
        "week": ["ВС", "ПН", "ВТ", "СР", "ЧТ", "ПТ", "СБ"]
      },
      "download": {
        "download": "скачать",
        "completed": "Загрузка завершена",
        "tip": "Нажмите, чтобы скачать видео",
        "preparing": "Подготовка к загрузке (если не получается, сделайте это вручную)"
      },
      "menuCommand": {
        "settings": "настраивать",
        "titleDateFormat": "Настройки формата времени:",
        "buttonClose": "закрытие",
        "simplifyMode": "Упрощенный режим",
        "turnOn": "Включить",
        "turnOff": "Выключить"
      }
    },
    "pt": {
      "dateFormat": {
        "week": ["Dom", "Seg", "Ter", "Qua", "Qui", "Sex", "Sáb"]
      },
      "download": {
        "download": "descargar",
        "completed": "Descarga completa",
        "tip": "Clique para baixar o vídeo",
        "preparing": "Preparação para download (se falhar, faça-o manualmente)"
      },
      "menuCommand": {
        "settings": "configuración",
        "titleDateFormat": "Configuración de formato de hora:",
        "buttonClose": "cierre",
        "simplifyMode": "Modo simplificado",
        "turnOn": "Ativar",
        "turnOff": "Desativar"
      }
    },
    "es": {
      "dateFormat": {
        "week": ["DOM", "LUN", "MAR", "MIER", "JUE", "VIE", "SÁB"]
      },
      "download": {
        "download": "descargar",
        "completed": "Descarga completa",
        "tip": "Haga clic para descargar el vídeo",
        "preparing": "Preparándose para la descarga (si falla, hágalo manualmente)"
      },
      "menuCommand": {
        "settings": "configuración",
        "titleDateFormat": "Configuración de formato de hora:",
        "buttonClose": "cierre",
        "simplifyMode": "Modo simplificado",
        "turnOn": "Activar",
        "turnOff": "Desactivar"
      }
    },
    "th": {
      "dateFormat": {
        "week": ["วันอาทิตย์", "วันจันทร์", "วันอังคาร", "วันพุธ", " วันพฤหัสบดี", "วันศุกร์ ", "วันเสาร์ "]
      },
      "download": {
        "download": "ดาวน์โหลด",
        "completed": "ดาวน์โหลดเสร็จสมบูรณ์",
        "tip": "คลิกเพื่อดาวน์โหลดวิดีโอ",
        "preparing": "กำลังเตรียมการดาวน์โหลด (หากล้มเหลว กรุณาดำเนินการด้วยตนเอง)"
      },
      "menuCommand": {
        "settings": "ตั้งค่า",
        "titleDateFormat": "การตั้งค่ารูปแบบเวลา:",
        "buttonClose": "ปิด",
        "simplifyMode": "โหมดแบบย่อ",
        "turnOn": "เปิด",
        "turnOff": "ปิด"
      }
    },
    "tr": {
      "dateFormat": {
        "week": ["Pazar", "Pazartesi", "Salı", "Çarşamba", "Perşembe", "Cuma", "Cumartesi"]
      },
      "download": {
        "download": "indirmek",
        "completed": "İndirme tamamlandı",
        "tip": "Videoyu indirmek için tıklayın",
        "preparing": "İndirmeye hazırlanıyor (başarısız olursa lütfen manuel olarak yapın)"
      },
      "menuCommand": {
        "settings": "kurmak",
        "titleDateFormat": "Saat formatı ayarları:",
        "buttonClose": "kapatma",
        "simplifyMode": "Basitleştirilmiş mod",
        "turnOn": "Aç",
        "turnOff": "Kapat"
      }
    },
    "nl": {
      "dateFormat": {
        "week": ["zondag", "maandag", "dinsdag", "woensdag", "donderdag", "vrijdag", "zaterdag"]
      },
      "download": {
        "download": "downloaden",
        "completed": "Downloaden voltooid",
        "tip": "Klik om video te downloaden",
        "preparing": "Voorbereiden voor downloaden (als dit mislukt, doe dit dan handmatig)"
      },
      "menuCommand": {
        "settings": "opgezet",
        "titleDateFormat": "Instellingen tijdformaat:",
        "buttonClose": "sluiting",
        "simplifyMode": "Vereenvoudigde modus",
        "turnOn": "Inschakelen",
        "turnOff": "Uitschakelen"
      }
    }
  };
  const lang = (navigator.language || navigator.userLanguage || "").slice(0, 2).toLowerCase() || "en";
  const Commonlanguage = language[lang] ?? language["en"];

  const FMT = 16;
  const XSettingsDialog = {
    number: Math.ceil(Math.random() * 1e8),
    formats: [
      { format: "Do nothing", example: "N/A" },
      { format: "ISO 8601 T", example: "2025-07-09T22:57:30" },
      { format: "ISO 8601 (space + s)", example: "2025-07-09 22:57:30" },
      { format: "ISO 8601 (space, no s)", example: "2025-07-09 22:57" },
      { format: "US: MMM d, yyyy h:mm A", example: "Jul 9, 2025, 10:57 PM" },
      { format: "US: EEE, MMM d, yyyy h:mm A", example: "Wed, Jul 9, 2025, 10:57 PM" },
      { format: "US: MM/dd/yyyy h:mm A", example: "07/09/2025 10:57 PM" },
      { format: "US: MM/dd/yyyy HH:mm", example: "07/09/2025 22:57" },
      { format: "EU/UK: dd/MM/yyyy HH:mm", example: "09/07/2025 22:57" },
      { format: "DE: dd.MM.yyyy, HH:mm", example: "09.07.2025, 22:57" },
      { format: "EU long: d MMMM yyyy, HH:mm", example: "9 July 2025, 22:57" },
      { format: "CN: yyyy年M月d日 HH:mm", example: "2025年7月9日 22:57" },
      { format: "East Asia: yyyy/MM/dd HH:mm", example: "2025/07/09 22:57" },
      { format: "UK short: EEE d MMM yyyy HH:mm", example: "Wed 9 Jul 2025 22:57" },
      { format: "Unix ctime (en)", example: "Wed Jul  9 22:57:30 2025" },
      { format: "US full: EEEE, MMMM d, yyyy h:mm:ss A", example: "Wednesday, July 9, 2025, 10:57:30 PM" },
      { format: "Compact: hh.mm A·mmm d,yy", example: "10.57 PM·Jul 9,25" },
      { format: "TW ROC: Myyy-MM-dd HH:mm", example: "M114-07-09 22:57" }
    ],
    make() {
      const dialog = document.createElement("div");
      dialog.className = "dialog_u_" + this.number;
      dialog.style.all = "initial";
      dialog.style.backgroundColor = "#fff";
      dialog.style.border = "1px solid #e1e8ed";
      dialog.style.borderRadius = "10px";
      dialog.style.boxShadow = "0 16px 48px rgba(15, 20, 25, 0.14), 0 4px 16px rgba(15, 20, 25, 0.08)";
      dialog.style.fontFamily = "monospace";
      dialog.style.fontSize = "12px";
      dialog.style.width = "640px";
      dialog.style.maxWidth = "calc(100vw - 24px)";
      dialog.style.boxSizing = "border-box";
      dialog.style.paddingLeft = "8px";
      dialog.style.paddingRight = "8px";
      dialog.style.paddingTop = "8px";
      dialog.style.paddingBottom = "8px";
      dialog.style.position = "fixed";
      dialog.style.right = "8px";
      dialog.style.top = "8px";
      dialog.style.zIndex = "2147483647";
      dialog.style.overflow = "auto";
      const escHtml = (s) => String(s).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
      let formatsHtml = `<table style="width:100%;border: 1px solid #c0bfbf;border-collapse: collapse;">`;
      for (let i = 1; i <= this.formats.length; i++) {
        if (i % 2 !== 0) {
          formatsHtml += `<tr style="width:100%;border: 1px solid #c0bfbf;">`;
        }
        const item = this.formats[i - 1];
        const exTitle = String(item.example).replace(/"/g, "&quot;");
        const fmtLine = escHtml(item.format);
        const exLine = escHtml(item.example);
        formatsHtml += `<td width="50%" style="border: 1px solid #c0bfbf;padding: 5px;vertical-align:top;" title="${exTitle}"><div><div style="color:#000;font-size:13px;"><input type="radio" name="fmt" value="${i - 1}" class="top_r" /><b>【${i}】${fmtLine}</b></div><div style="color:#555;font-size:11px;margin-top:4px;padding-left:20px;">${exLine}</div></div></td>`;
        if (i % 2 === 0) {
          formatsHtml += `</tr>`;
        }
      }
      if (this.formats.length % 2 !== 0) {
        formatsHtml += `</tr>`;
      }
      formatsHtml += `</table>`;
      const btnLabel = escHtml(Commonlanguage.menuCommand.buttonClose);
      const titleText = escHtml(Commonlanguage.menuCommand.titleDateFormat);
      dialog.innerHTML = `<div style="font-size:17px;font-weight:700;margin:15px auto;padding:0 4px;text-align:center;font-family:system-ui,-apple-system,'Segoe UI',Roboto,sans-serif;color:#0f1419;">` + titleText + `</div><div>` + formatsHtml + `</div><div style="margin-top:15px;text-align:center;"><button type="button" name="closex" style="appearance:none;-webkit-appearance:none;margin:0;border:1px solid #1d9bf0;border-radius:999px;padding:5px 18px;font-size:14px;font-weight:600;font-family:system-ui,-apple-system,'Segoe UI',Roboto,sans-serif;background:linear-gradient(180deg,#1d9bf0 0%,#1a8cd8 100%);color:#fff;cursor:pointer;box-shadow:0 2px 10px rgba(29,155,240,0.35);">` + btnLabel + `</button></div>`;
      dialog.style.display = "none";
      return dialog;
    },
    addEvent(dialog) {
      dialog.querySelector("button[name='closex']").addEventListener(
        "click",
        () => {
          for (const e of dialog.querySelectorAll('input[name="fmt"]')) {
            if (e.checked) {
              fmt = e.value;
              break;
            }
          }
          GM_setValue("fmt", fmt);
          dialog.style.display = "none";
        },
        false
      );
    },
    init() {
      const dialog = this.make();
      this.addEvent(dialog);
      document.body.appendChild(dialog);
      GM_registerMenuCommand(Commonlanguage.menuCommand.settings, () => {
        if (dialog.style.display !== "none")
          return;
        const input = dialog.querySelector(
          `input[name="fmt"][value="${String(fmt)}"]`
        );
        if (input)
          input.checked = true;
        dialog.style.display = "block";
      });
      const isSimplifyMode = GM_getValue("x_simplify_mode", "") === "true";
      GM_registerMenuCommand(Commonlanguage.menuCommand.simplifyMode + `(${isSimplifyMode ? Commonlanguage.menuCommand.turnOff : Commonlanguage.menuCommand.turnOn})`, () => {
        if (isSimplifyMode) {
          GM_deleteValue("x_simplify_mode");
        } else {
          GM_setValue("x_simplify_mode", "true");
        }
        location.reload(true);
      });
    }
  };
  let fmt = GM_getValue("fmt", FMT);
  (function syncFmtIndex() {
    const max = XSettingsDialog.formats.length - 1;
    const v = parseInt(String(fmt), 10);
    if (Number.isNaN(v) || v < 0 || v > max) {
      const next = Math.min(Math.max(parseInt(String(FMT), 10), 0), max);
      fmt = String(next);
      GM_setValue("fmt", fmt);
    } else {
      fmt = String(v);
    }
  })();
  const XDateFormat = {
    df: function(date, f) {
      const WEEK_FULL = [
        "Sunday",
        "Monday",
        "Tuesday",
        "Wednesday",
        "Thursday",
        "Friday",
        "Saturday"
      ];
      const MONTH_SHORT = [
        "Jan",
        "Feb",
        "Mar",
        "Apr",
        "May",
        "Jun",
        "Jul",
        "Aug",
        "Sep",
        "Oct",
        "Nov",
        "Dec"
      ];
      const MONTH_FULL = [
        "January",
        "February",
        "March",
        "April",
        "May",
        "June",
        "July",
        "August",
        "September",
        "October",
        "November",
        "December"
      ];
      const pad = (num) => ("0" + num).slice(-2);
      const YE = date.getFullYear();
      const YE2 = YE.toString().slice(-2);
      const YM = YE - 1911;
      const MO = pad(date.getMonth() + 1);
      const MO_IDX = date.getMonth();
      const MO_NAME = MONTH_SHORT[MO_IDX];
      const MO_NAME_FULL = MONTH_FULL[MO_IDX];
      const DA = pad(date.getDate());
      const dNum = parseInt(DA, 10);
      const weekAbbr = () => WEEK_FULL[date.getDay()].slice(0, 3);
      const HO = pad(date.getHours());
      const MI = pad(date.getMinutes());
      const SE = pad(date.getSeconds());
      const h12 = date.getHours() % 12 || 12;
      const HO12 = pad(h12);
      const AMPM = date.getHours() >= 12 ? "PM" : "AM";
      const F = [
        `${YE}-${MO}-${DA}T${HO}:${MI}:${SE}`,
        `${YE}-${MO}-${DA} ${HO}:${MI}:${SE}`,
        `${YE}-${MO}-${DA} ${HO}:${MI}`,
        `${MO_NAME} ${dNum}, ${YE}, ${HO12}:${MI} ${AMPM}`,
        `${weekAbbr()}, ${MO_NAME} ${dNum}, ${YE}, ${HO12}:${MI} ${AMPM}`,
        `${MO}/${DA}/${YE} ${HO12}:${MI} ${AMPM}`,
        `${MO}/${DA}/${YE} ${HO}:${MI}`,
        `${DA}/${MO}/${YE} ${HO}:${MI}`,
        `${DA}.${MO}.${YE}, ${HO}:${MI}`,
        `${dNum} ${MO_NAME_FULL} ${YE}, ${HO}:${MI}`,
        `${YE}年${MO_IDX + 1}月${dNum}日 ${HO}:${MI}`,
        `${YE}/${MO}/${DA} ${HO}:${MI}`,
        `${weekAbbr()} ${dNum} ${MO_NAME} ${YE} ${HO}:${MI}`,
        `${weekAbbr()} ${MO_NAME} ${String(dNum).padStart(2, " ")} ${HO}:${MI}:${SE} ${YE}`,
        `${WEEK_FULL[date.getDay()]}, ${MO_NAME_FULL} ${dNum}, ${YE}, ${HO12}:${MI}:${SE} ${AMPM}`,
        `${HO12}.${MI} ${AMPM}·${MO_NAME} ${dNum},${YE2}`,
        `M${YM}-${MO}-${DA} ${HO}:${MI}`
      ];
      return F[f] ?? F[0];
    },
    repldatetime: function() {
      const MYNAME = "peter_parker_x1190";
      const SEL = 'main div[data-testid="primaryColumn"] section article time[datetime*=":"]';
      const SEL_2 = 'div[aria-labelledby="modal-header"] div[data-testid^="User-Name"] time[datetime]';
      const SEL_3 = 'div[aria-labelledby="modal-header"] div[aria-label] time[datetime]';
      const SEL_4 = 'main section[aria-labelledby="detail-header"] article div[data-testid^="User-Name"] time[datetime]';
      const SEL_5 = 'main section div[data-testid="conversation"] div[aria-label] time[datetime]';
      document.querySelectorAll(
        SEL + ", " + SEL_2 + ", " + SEL_3 + ", " + SEL_4 + ", " + SEL_5
      ).forEach((e) => {
        if (fmt != 0) {
          const SEL_ADD = "span.us-" + MYNAME;
          let d = e.getAttribute("datetime");
          let df = this.df(new Date(d), fmt - 1);
          let pe = e.parentNode;
          let old = pe.querySelectorAll(SEL_ADD);
          if (!old.length) {
            let span = document.createElement("span");
            span.className = "us-" + MYNAME;
            span.setAttribute("datetime", d);
            span.setAttribute("local-datetime", df);
            span.textContent = df;
            span.style = e.style;
            e.style.setProperty("display", "none");
            pe.appendChild(span);
          } else if (old[0].getAttribute("local-datetime") != df) {
            old[0].setAttribute("local-datetime", df);
            old[0].textContent = df;
            old[0].style = e.style;
          }
        }
      });
    }
  };
  const XDownloadUI = {
    showSensitive: true,
    svg: `
        <g class="download"><path d="M11.99 16l-5.7-5.7L7.7 8.88l3.29 3.3V2.59h2v9.59l3.3-3.3 1.41 1.42-5.71 5.7zM21 15l-.02 3.51c0 1.38-1.12 2.49-2.5 2.49H5.5C4.11 21 3 19.88 3 18.5V15h2v3.5c0 .28.22.5.5.5h12.98c.28 0 .5-.22.5-.5L19 15h2z" /></g>
        <g class="completed"><path d="M3,14 v5 q0,2 2,2 h14 q2,0 2,-2 v-5 M7,10 l3,4 q1,1 2,0 l8,-11" fill="none" stroke="#1DA1F2" stroke-width="2" stroke-linecap="round" /></g>
        <g class="loading"><circle cx="12" cy="12" r="10" fill="none" stroke="#1DA1F2" stroke-width="4" opacity="0.4" /><path d="M12,2 a10,10 0 0 1 10,10" fill="none" stroke="#1DA1F2" stroke-width="4" stroke-linecap="round" /></g>
        <g class="failed"><circle cx="12" cy="12" r="11" fill="#f33" stroke="currentColor" stroke-width="2" opacity="0.8" /><path d="M14,5 a1,1 0 0 0 -4,0 l0.5,9.5 a1.5,1.5 0 0 0 3,0 z M12,17 a2,2 0 0 0 0,4 a2,2 0 0 0 0,-4" fill="#fff" stroke="none" /></g>
    `,
    isTweetdeck: () => window.location.host.includes("tweetdeck"),
    extractStatusId: (url) => url ? (url.match(/\/status\/(\d+)/) || [null, null])[1] : null,
    uniqueArray: (arr) => Array.from(new Set(arr)),
    getExtension: (url) => new URL(url).pathname.split(".").pop() || null,
    sanitizeFilename: (filename) => filename.replace(/[\/\\\?\%\*\:\|\\"<>\r\n]/g, "_"),
    setStatus: function(btn, classnames, title, style) {
      if (classnames) {
        btn.classList.remove("download", "completed", "loading", "failed");
        btn.classList.add(...classnames);
      }
      if (title)
        btn.title = title;
      if (style)
        btn.style.cssText = style;
    },
    clickDownloadEvent: function(btn, statusIds) {
      const filenameTemplate = "{name}";
      const uniqueStatusIds = this.uniqueArray(statusIds);
      const handleDownload = (url, filename, defaultExt) => {
        return new Promise((resolve, reject) => {
          const finalFilename = filename + "." + (this.getExtension(url) ?? defaultExt);
          GM_download({
            url,
            name: finalFilename,
            onload: () => {
              XDownloadUI.setStatus(btn, ["completed"], "下载完成");
              resolve();
            },
            onerror: (error) => {
              reject(error);
            }
          });
        });
      };
      this.setStatus(btn, ["loading"], "正在准备下载...");
      const downloadPromises = uniqueStatusIds.map((statusId) => {
        const media = XDownload.mediaMap.get(statusId);
        if (!media) {
          return Promise.reject(
            `Media data not found for status ID: ${statusId}`
          );
        }
        const { entityId, video, photo, text } = media;
        const filename = filenameTemplate.replace(
          "{name}",
          this.sanitizeFilename(text) || entityId
        );
        if (video) {
          return handleDownload(video, filename, "mp4");
        } else if (photo) {
          return handleDownload(photo, filename, "jpg");
        }
        return Promise.reject("No media to download");
      });
      Promise.allSettled(downloadPromises).then((results) => {
        const anySuccess = results.some(
          (result) => result.status === "fulfilled"
        );
        if (anySuccess) {
          this.setStatus(btn, ["completed"], "下载成功");
        } else {
          this.setStatus(btn, ["failed"], "未找到媒体资源");
        }
      }).catch((error) => {
        this.setStatus(btn, ["failed"], "下载失败");
      });
    },
    addButtonTo: function(article) {
      if (article.dataset.detected)
        return;
      article.dataset.detected = "true";
      const statusIds = Array.from(
        article.querySelectorAll('a[href*="/status/"]')
      ).map((el) => this.extractStatusId(el.href)).filter((id) => id);
      if (statusIds.length === 0)
        return;
      const mediaSelector = [
        'a[href*="/photo/1"]',
        'div[role="progressbar"]',
        'button[data-testid="playButton"]',
        'div[data-testid="videoComponent"]',
        'a[href="/settings/content_you_see"]',
        "div.media-image-container",
        "div.media-preview-container",
        'div[aria-labelledby]>div:first-child>div[role="button"][tabindex="0"]'
      ];
      const hasMedia = article.querySelector(mediaSelector.join(","));
      if (hasMedia) {
        const btnGroup = article.querySelector(
          'div[role="group"]:last-of-type, ul.tweet-actions, ul.tweet-detail-actions'
        );
        if (btnGroup) {
          const btnShare = Array.from(
            btnGroup.querySelectorAll(
              ":scope>div>div, li.tweet-action-item>a, li.tweet-detail-action-item>a"
            )
          ).pop().parentNode;
          const btnDownload = btnShare.cloneNode(true);
          btnDownload.style.marginLeft = "10px";
          btnDownload.querySelector("button")?.removeAttribute("disabled");
          const svgContainer = this.isTweetdeck() ? btnDownload.firstElementChild : btnDownload.querySelector("svg");
          if (svgContainer) {
            if (this.isTweetdeck()) {
              svgContainer.innerHTML = `<svg viewBox="0 0 20 20" width="15" height="15">${this.svg}</svg>`;
              svgContainer.removeAttribute("rel");
              btnDownload.classList.replace("pull-left", "pull-right");
            } else {
              svgContainer.innerHTML = this.svg;
            }
          }
          this.setStatus(btnDownload, ["x-master-dl", "download"], "下载媒体");
          btnGroup.insertBefore(btnDownload, btnShare.nextSibling);
          btnDownload.onclick = () => this.clickDownloadEvent(btnDownload, statusIds);
          if (this.showSensitive) {
            article.querySelector(
              'div[aria-labelledby] div[role="button"][tabindex="0"]:not([data-testid]) > div[dir] > span > span'
            )?.click();
          }
        }
      }
      const imgs = article.querySelectorAll('a[href*="/photo/"]');
      if (imgs.length > 1) {
        imgs.forEach((img) => {
          const imgStatusId = this.extractStatusId(img.href);
          if (!imgStatusId || img.parentNode.querySelector(".x-master-dl.tmd-img"))
            return;
          const btnDownload = document.createElement("div");
          btnDownload.style.cssText = "position: absolute; top: 0; right: 0; z-index: 10; margin: 5px;";
          btnDownload.innerHTML = `<div><div><svg viewBox="0 0 20 20" width="15" height="15">${this.svg}</svg></div></div>`;
          this.setStatus(
            btnDownload,
            ["x-master-dl", "tmd-img", "download"],
            "下载图片"
          );
          img.parentNode.appendChild(btnDownload);
          btnDownload.onclick = (e) => {
            e.preventDefault();
            e.stopPropagation();
            this.clickDownloadEvent(btnDownload, [imgStatusId]);
          };
        });
      }
    },
    addButtonToMedia: function(listitems) {
      listitems.forEach((li) => {
        if (li.dataset.detected)
          return;
        li.dataset.detected = "true";
        const statusElement = li.querySelector('a[href*="/status/"]');
        const statusId = statusElement ? this.extractStatusId(statusElement.href) : null;
        if (!statusId)
          return;
        const btnDownload = document.createElement("div");
        btnDownload.innerHTML = `<div><div><svg viewBox="0 0 20 20" width="15" height="15">${this.svg}</svg></div></div>`;
        this.setStatus(
          btnDownload,
          ["x-master-dl", "tmd-media", "download"],
          "下载媒体"
        );
        li.appendChild(btnDownload);
        btnDownload.onclick = () => this.clickDownloadEvent(btnDownload, [statusId]);
      });
    },
    detect: function(node) {
      const article = node.tagName === "ARTICLE" && node || node.tagName === "DIV" && (node.querySelector("article") || node.closest("article"));
      if (article) {
        this.addButtonTo(article);
      }
      const listitems = node.tagName === "LI" && node.getAttribute("role") === "listitem" ? [node] : node.tagName === "DIV" && node.querySelectorAll('li[role="listitem"]');
      if (listitems) {
        this.addButtonToMedia(listitems);
      }
    }
  };
  const XDownload = {
    mediaMap: /* @__PURE__ */ new Map(),
    findParent: function(obj, targetKey) {
      let result = [];
      if (typeof obj === "object" && obj !== null) {
        for (let key in obj) {
          if (obj.hasOwnProperty(key)) {
            if (key === targetKey) {
              result.push(obj);
            }
            result = result.concat(this.findParent(obj[key], targetKey));
          }
        }
      } else if (Array.isArray(obj)) {
        for (let item of obj) {
          result = result.concat(this.findParent(item, targetKey));
        }
      }
      return result;
    },
    extractMediaFromResponse: function(url, responseText) {
      try {
        const data = JSON.parse(responseText);
        const entities = this.findParent(data, "extended_entities");
        if (entities.length === 0) {
          return;
        }
        for (let entity of entities) {
          if (!entity.extended_entities)
            continue;
          const entityId = entity.id_str || entity.conversation_id_str;
          (entity.extended_entities.media || []).filter((m) => ["video", "animated_gif", "photo"].includes(m.type)).forEach((m) => {
            const bestVideo = m.video_info?.variants?.filter((v) => v.content_type === "video/mp4").sort((a, b) => b.bitrate - a.bitrate)[0];
            const text = (entity.full_text || "").split("https://t.co")[0]?.trim()?.slice(0, 50) || entityId;
            const mediaItem = {
              entityId,
              id: m.id_str,
              thumbnail: m.media_url_https?.split(".jpg")[0],
              video: bestVideo?.url,
              photo: m.media_url_https,
              text
            };
            this.mediaMap.set(entityId, mediaItem);
          });
        }
      } catch (e) {
      }
    },
    hookXMLHttpRequest: function() {
      const self = this;
      const originalOpen = XMLHttpRequest.prototype.open;
      const originalSend = XMLHttpRequest.prototype.send;
      XMLHttpRequest.prototype.open = function(method, url) {
        this._url = url;
        return originalOpen.apply(this, arguments);
      };
      XMLHttpRequest.prototype.send = function() {
        this.addEventListener("load", function() {
          if (this._url && this.response) {
            try {
              if (this.responseType === "" || this.responseType === "text") {
                self.extractMediaFromResponse(this._url, this.responseText);
              }
            } catch (e) {
            }
          }
        });
        return originalSend.apply(this, arguments);
      };
    },
    init: function() {
      this.hookXMLHttpRequest();
      GM_addStyle(
        css_248z + (this.showSensitive ? css_248z$1 : "")
      );
    }
  };
  const X = {
    start: function() {
      XDownload.init();
      XSettingsDialog.init();
      const observer = new MutationObserver((mutations) => {
        mutations.forEach((mutation) => {
          mutation.addedNodes.forEach((node) => {
            if (node.nodeType === 1) {
              XDownloadUI.detect(node);
              XDateFormat.repldatetime();
            }
          });
        });
      });
      observer.observe(document.body, {
        childList: true,
        subtree: true
      });
    }
  };

  (function() {
    const enabled = GM_getValue("x_simplify_mode", "") === "true";
    if (!enabled) {
      return;
    }
    function update() {
      const width = Math.min(document.documentElement.offsetWidth || 800, 800);
      if (window.innerWidth === width && document.documentElement.clientWidth === width) {
        return;
      }
      window.__defineGetter__("innerWidth", () => width);
      document.documentElement.__defineGetter__("clientWidth", () => width);
      if (window.visualViewport) {
        window.visualViewport.__defineGetter__("width", () => width);
      }
      window.dispatchEvent(new Event("resize"));
      if (window.visualViewport) {
        window.visualViewport.dispatchEvent(new Event("resize"));
      }
    }
    window.addEventListener("load", update);
    window.addEventListener("resize", update);
    if (window.visualViewport) {
      window.visualViewport.addEventListener("resize", update);
    }
    document.addEventListener("visibilitychange", update);
    GM_addStyle(`
    #react-root main {
      -webkit-flex-grow: 1 !important;
      flex-grow: 1 !important;
    }

    @media (min-width: 800px) {
      [role='listbox'] {
        max-width: 500px !important;
      }
    }
  `);
    update();
  })();

  const Init = {
    async start() {
      X.start();
    }
  };
  Init.start();

}());