// ==UserScript==
// @name YouTube 會員徽章及自訂表情符號圖標下載器
// @namespace http://tampermonkey.net/
// @version 1.0
// @description 下載最佳畫質的YouTube在留言和聊天室中,顯示頻道的會員徽章及自訂表情符號。
// @author ChatGPT, kkom kbro
// @license MIT
// @match https://youtube.com/*
// @match https://www.youtube.com/*
// @require https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js
// @require https://cdnjs.cloudflare.com/ajax/libs/jszip-utils/0.0.2/jszip-utils.min.js
// @require https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.5/FileSaver.min.js
// @grant none
// ==/UserScript==
(function() {
'use strict';
window.addEventListener("load", main, false);
const replacementNames = [
"新規メンバー",
"1か月",
"2か月",
"6か月",
"1年",
"2年",
"3年",
"4年",
"5年"
];
const labels = {
"af-ZA": {
prefix: "Voorvoegsel",
suffix: "Achtervoegsel",
capitalize: "Kapitaliseer die eerste letter as dit met 'n letter begin",
download: "Ikone aflaai!"
},
"az-Latn-AZ": {
prefix: "Ön əlavə",
suffix: "Arxa əlavə",
capitalize: "Əgər hərf ilə başlayırsa, ilk hərfi böyük edin",
download: "İkonu yükləyin!"
},
"id-ID": {
prefix: "Awalan",
suffix: "Akhiran",
capitalize: "Jika dimulai dengan huruf, kapitalisasi huruf pertama",
download: "Unduh ikon!"
},
"ms-MY": {
prefix: "Awalan",
suffix: "Akhiran",
capitalize: "Jika bermula dengan huruf, besar huruf pertama",
download: "Muat turun ikon!"
},
"bs-Latn-BA": {
prefix: "Prefiks",
suffix: "Sufiks",
capitalize: "Ako počinje sa slovom, kapitalizuj prvo slovo",
download: "Preuzmi ikonu!"
},
"ca-ES": {
prefix: "Prefix",
suffix: "Sufix",
capitalize: "Si comença per una lletra, posa en majúscula la primera lletra",
download: "Descarrega la icona!"
},
"cs-CZ": {
prefix: "Předpona",
suffix: "Přípona",
capitalize: "Pokud začíná písmenem, velké první písmeno",
download: "Stáhnout ikonu!"
},
"da-DK": {
prefix: "Præfiks",
suffix: "Suffiks",
capitalize: "Hvis det starter med et bogstav, skal det første bogstav skrives med stort",
download: "Hent ikon!"
},
"de-DE": {
prefix: "Präfix",
suffix: "Suffix",
capitalize: "Wenn es mit einem Buchstaben beginnt, den ersten Buchstaben großschreiben",
download: "Symbol herunterladen!"
},
"et-EE": {
prefix: "Eesliide",
suffix: "Tagae liide",
capitalize: "Kui see algab tähega, siis suurtäht esimese tähega",
download: "Laadi ikoon alla!"
},
"en-IN": {
prefix: "Prefix",
suffix: "Suffix",
capitalize: "If it starts with a letter, capitalize the first letter",
download: "Download icon!"
},
"en-GB": {
prefix: "Prefix",
suffix: "Suffix",
capitalize: "If it starts with a letter, capitalize the first letter",
download: "Download icon!"
},
"en": {
prefix: "Prefix",
suffix: "Suffix",
capitalize: "If it starts with a letter, capitalize the first letter",
download: "Download icon!"
},
"es-ES": {
prefix: "Prefijo",
suffix: "Sufijo",
capitalize: "Si empieza con una letra, pon en mayúscula la primera letra",
download: "¡Descargar ícono!"
},
"es-419": {
prefix: "Prefijo",
suffix: "Sufijo",
capitalize: "Si empieza con una letra, pon en mayúscula la primera letra",
download: "¡Descargar ícono!"
},
"es-US": {
prefix: "Prefijo",
suffix: "Sufijo",
capitalize: "Si empieza con una letra, pon en mayúscula la primera letra",
download: "¡Descargar ícono!"
},
"eu-ES": {
prefix: "Aurkeztu",
suffix: "Atzizkia",
capitalize: "Letra batekin hasten bada, idatzi lehen letra nagusian",
download: "Ikonoa jaitsi!"
},
"fil-PH": {
prefix: "Paunang salita",
suffix: "Sugnay",
capitalize: "Kung nagsisimula ito sa isang titik, i-capitalize ang unang titik",
download: "I-download ang icon!"
},
"fr-FR": {
prefix: "Préfixe",
suffix: "Suffixe",
capitalize: "S'il commence par une lettre, mettre la première lettre en majuscule",
download: "Télécharger l'icône !"
},
"fr-CA": {
prefix: "Préfixe",
suffix: "Suffixe",
capitalize: "S'il commence par une lettre, mettre la première lettre en majuscule",
download: "Télécharger l'icône !"
},
"gl-ES": {
prefix: "Prefixo",
suffix: "Sufixo",
capitalize: "Se comeza cunha letra, escribe a primeira letra en maiúscula",
download: "Descargar o ícono!"
},
"hr-HR": {
prefix: "Prefiks",
suffix: "Sufiks",
capitalize: "Ako počinje slovom, kapitaliziraj prvo slovo",
download: "Preuzmi ikonu!"
},
"zu-ZA": {
prefix: "Isithako",
suffix: "Isiphetho",
capitalize: "Uma iqala ngeleta, khuphula ileta yokuqala",
download: "Landa isithonjana!"
},
"is-IS": {
prefix: "Forskeyti",
suffix: "Eftirskeyti",
capitalize: "Ef það byrjar á staf, skaltu stórskrifa fyrsta stafinn",
download: "Sækið táknið!"
},
"it-IT": {
prefix: "Prefisso",
suffix: "Suffisso",
capitalize: "Se inizia con una lettera, maiuscola la prima lettera",
download: "Scarica l'icona!"
},
"sw-TZ": {
prefix: "Kichwa",
suffix: "Kiambatanishi",
capitalize: "Ikiwa inaanza na herufi, inua herufi ya kwanza",
download: "Pakua ikoni!"
},
"lv-LV": {
prefix: "Priekšmets",
suffix: "Pielikums",
capitalize: "Ja tas sākas ar burtu, lielais pirmais burts",
download: "Lejupielādēt ikonu!"
},
"lt-LT": {
prefix: "Priešdėlis",
suffix: "Priedas",
capitalize: "Jei prasideda su raide, didžiuoju pirmą raide",
download: "Atsisiųsti ikoną!"
},
"hu-HU": {
prefix: "Előtag",
suffix: "Utótag",
capitalize: "Ha betűvel kezdődik, a nagybetűt az első betűvel",
download: "Ikont letölteni!"
},
"nl-NL": {
prefix: "Voorvoegsel",
suffix: "Achtervoegsel",
capitalize: "Als het begint met een letter, maak de eerste letter hoofdletter",
download: "Pictogram downloaden!"
},
"nb-NO": {
prefix: "Prefiks",
suffix: "Suffiks",
capitalize: "Hvis det begynner med en bokstav, kapitaliser den første bokstaven",
download: "Last ned ikonet!"
},
"uz-Latn-UZ": {
prefix: "Oldingi qo'shimcha",
suffix: "Yana qo'shimcha",
capitalize: "Agar harf bilan boshlangan bo'lsa, birinchi harfni katta qilib qo'ying",
download: "Ikonni yuklab oling!"
},
"pl-PL": {
prefix: "Prefiks",
suffix: "Sufiks",
capitalize: "Jeśli zaczyna się od litery, użyj wielkiej litery na pierwszej literze",
download: "Pobierz ikonę!"
},
"pt-PT": {
prefix: "Prefixo",
suffix: "Sufixo",
capitalize: "Se começar com uma letra, capitaliza a primeira letra",
download: "Baixar ícone!"
},
"pt-BR": {
prefix: "Prefixo",
suffix: "Sufixo",
capitalize: "Se começar com uma letra, capitaliza a primeira letra",
download: "Baixar ícone!"
},
"ro-RO": {
prefix: "Prefix",
suffix: "Sufix",
capitalize: "Dacă începe cu o literă, pune prima literă cu majuscule",
download: "Descarcă pictograma!"
},
"sq-AL": {
prefix: "Prefiks",
suffix: "Shtesë",
capitalize: "Nëse fillon me një shkronjë, shkruaj me shkronjë të madhe shkronjën e parë",
download: "Shkarko ikonën!"
},
"sk-SK": {
prefix: "Predpona",
suffix: "Prípona",
capitalize: "Ak začína písmenom, veľké prvé písmeno",
download: "Stiahnuť ikonu!"
},
"sl-SI": {
prefix: "Predpona",
suffix: "Pripona",
capitalize: "Če se začne z črko, z velikimi začetnicami",
download: "Prenesi ikono!"
},
"sr-Latn-RS": {
prefix: "Prefiks",
suffix: "Sufiks",
capitalize: "Ako počinje slovom, kapitalizuj prvo slovo",
download: "Preuzmi ikonu!"
},
"fi-FI": {
prefix: "Etuliite",
suffix: "Loppuliite",
capitalize: "Jos se alkaa kirjaimella, isolla ensimmäisellä kirjaimella",
download: "Lataa ikoni!"
},
"sv-SE": {
prefix: "Prefix",
suffix: "Suffix",
capitalize: "Om det börjar med en bokstav, skriv första bokstaven med stort",
download: "Ladda ner ikon!"
},
"vi-VN": {
prefix: "Tiền tố",
suffix: "Hậu tố",
capitalize: "Nếu bắt đầu bằng chữ cái, viết hoa chữ cái đầu tiên",
download: "Tải xuống biểu tượng!"
},
"tr-TR": {
prefix: "Önek",
suffix: "Sonek",
capitalize: "Eğer bir harfle başlıyorsa, ilk harfi büyük yaz",
download: "İkonu indir!"
},
"be-BY": {
prefix: "Префікс",
suffix: "Суфікс",
capitalize: "Калі пачынаецца з літары, напішыце першую літару з вялікай",
download: "Спампаваць значок!"
},
"bg-BG": {
prefix: "Префикс",
suffix: "Суфикс",
capitalize: "Ако започва с буква, направете първата буква главна",
download: "Изтеглете иконата!"
},
"ky-KG": {
prefix: "Префикс",
suffix: "Суфикс",
capitalize: "Эгер ал бугаардан башталса, биринчи харфин чоң кыл",
download: "Иконканы жүктөө!"
},
"kk-KZ": {
prefix: "Префикс",
suffix: "Суфикс",
capitalize: "Егер әріптен басталса, бірінші әріпті бас әріппен жазыңыз",
download: "Иконканы жүктеңіз!"
},
"mk-MK": {
prefix: "Префикс",
suffix: "Суфикс",
capitalize: "Ако започнува со буква, напиши го првото слово со голема буква",
download: "Преземи икона!"
},
"mn-MN": {
prefix: "Урьдчилсан",
suffix: "Дүгнэлт",
capitalize: "Хэрвээ үсэгтэй эхэлж байвал, анхны үсгийг томоор бич",
download: "Икон хуулах!"
},
"ru-RU": {
prefix: "Префикс",
suffix: "Суффикс",
capitalize: "Если оно начинается с буквы, сделайте первую букву заглавной",
download: "Скачать иконку!"
},
"sr-Cyrl-RS": {
prefix: "Префикс",
suffix: "Суфикс",
capitalize: "Ако почиње словом, капитализуј прво слово",
download: "Преузми иконку!"
},
"uk-UA": {
prefix: "Префікс",
suffix: "Суфікс",
capitalize: "Якщо починається з літери, напишіть першу літеру з великої",
download: "Завантажити іконку!"
},
"el-GR": {
prefix: "Πρόθεμα",
suffix: "Επίθημα",
capitalize: "Αν αρχίζει με γράμμα, κεφαλαιοποιήστε το πρώτο γράμμα",
download: "Κατεβάστε το εικονίδιο!"
},
"hy-AM": {
prefix: "Առաջադիմություն",
suffix: "Վերջաբան",
capitalize: "Եթե սկսվում է տառով, մեծացրեք առաջին տառը",
download: "Բեռնել սիմվոլը!"
},
"he-IL": {
prefix: "קידומת",
suffix: "סופית",
capitalize: "אם זה מתחיל באות, הכנס אות גדולה ראשונה",
download: "הורד סמל!"
},
"ur-PK": {
prefix: "پیش لفظ",
suffix: "لاحقہ",
capitalize: "اگر یہ کسی حرف سے شروع ہوتا ہے تو پہلے حرف کو بڑے حروف میں لکھیں",
download: "آئیکن ڈاؤن لوڈ کریں!"
},
"ar": {
prefix: "بادئة",
suffix: "لاحقة",
capitalize: "إذا بدأت بحرف، اجعل الحرف الأول كبيرًا",
download: "تنزيل الأيقونة!"
},
"fa-IR": {
prefix: "پیشوند",
suffix: "پسوند",
capitalize: "اگر با حرف شروع میشود، حرف اول را بزرگ کنید",
download: "دانلود آیکون!"
},
"ne-NP": {
prefix: "प्रीफिक्स",
suffix: "सफिक्स",
capitalize: "यदि यो अक्षरले सुरु हुन्छ भने, पहिलो अक्षरलाई ठूलो बनाउनुहोस्",
download: "आइकन डाउनलोड गर्नुहोस्!"
},
"mr-IN": {
prefix: "पूर्वसूचक",
suffix: "अंतसूचक",
capitalize: "जर तुमच्या अक्षराने सुरूवात केली, तर पहिल्या अक्षराला मोठा करा",
download: "आइकन डाउनलोड करा!"
},
"hi-IN": {
prefix: "पूर्ववर्ती",
suffix: "उपसर्ग",
capitalize: "यदि यह एक अक्षर से शुरू होता है, तो पहले अक्षर को बड़े अक्षर में करें",
download: "आइकन डाउनलोड करें!"
},
"as-IN": {
prefix: "পূৰ্বৰ অংশ",
suffix: "অংশ",
capitalize: "যদি এটি এখন অক্ষৰে আৰম্ভ হয়, তেনেহ'লে প্ৰথম অক্ষৰখন বৃহৎ কৰক",
download: "আইকন ডাউনলোড কৰক!"
},
"bn-BD": {
prefix: "প্রিফিক্স",
suffix: "সাফিক্স",
capitalize: "যদি এটি একটি অক্ষর দিয়ে শুরু হয় তবে প্রথম অক্ষরকে বড় করুন",
download: "আইকন ডাউনলোড করুন!"
},
"pa-Guru-IN": {
prefix: "ਪੂਰਵਲੇਖ",
suffix: "ਲਾਗੂ",
capitalize: "ਜੇ ਇਹ ਅੱਖਰ ਨਾਲ ਸ਼ੁਰੂ ਹੁੰਦਾ ਹੈ, ਤਾਂ ਪਹਿਲੇ ਅੱਖਰ ਨੂੰ ਵੱਡਾ ਕਰੋ",
download: "ਆਈਕਨ ਡਾਊਨਲੋਡ ਕਰੋ!"
},
"gu-IN": {
prefix: "પૂર્વવાહી",
suffix: "અર્થશાસ્ત્ર",
capitalize: "જો તે અક્ષરથી શરૂ થાય તો પ્રથમ અક્ષરને મોટા બનાવો",
download: "આઇકન ડાઉનલોડ કરો!"
},
"or-IN": {
prefix: "ପ୍ରିଫିକ୍ସ",
suffix: "ସୁଫିକ୍ସ",
capitalize: "ଯଦି ଏହା ଏକ ଅକ୍ଷରରୁ ଆରମ୍ଭ ହୁଏ, ତେବେ ପ୍ରଥମ ଅକ୍ଷରକୁ ବଡ ରଖନ୍ତୁ",
download: "ଆଇକନ୍ ଡାଉନଲୋଡ୍ କରନ୍ତୁ!"
},
"ta-IN": {
prefix: "முன்சொல்",
suffix: "இணைப்பு",
capitalize: "அது எழுத்தில் தொடங்கின், முதல் எழுத்தை பெரிய எழுத்தில் மாற்றவும்",
download: "அச்சொல்லை பதிவிறக்கம் செய்க!"
},
"te-IN": {
prefix: "ప్రీఫిక్స్",
suffix: "సఫిక్స్",
capitalize: "అది అక్షరంతో ప్రారంభం అయితే, మొదటి అక్షరాన్ని పెద్దదిగా చేయండి",
download: "ఐకాన్ డౌన్లోడ్ చేయండి!"
},
"kn-IN": {
prefix: "ಮೂರು",
suffix: "ಅನುಕ್ರಮ",
capitalize: "ಅದು ಅಕ್ಷರದಿಂದ ಪ್ರಾರಂಭವಾದರೆ, ಮೊದಲ ಅಕ್ಷರವನ್ನು ದೊಡ್ಡದಾಗಿ ಮಾಡಿ",
download: "ಐಕಾನ್ ಡೌನ್ಲೋಡ್ ಮಾಡಿ!"
},
"ml-IN": {
prefix: "മുൻപറഞ്ഞത്",
suffix: "ശേഷം",
capitalize: "അത് അക്ഷരത്തിൽ തുടങ്ങുകയാണെങ്കിൽ, ആദ്യ അക്ഷരം വലിയയാക്കി",
download: "അയോൺ ഡൗൺലോഡ് ചെയ്യുക!"
},
"si-LK": {
prefix: "පෙරපදය",
suffix: "අනුපදය",
capitalize: "එය අක්ෂරයකින් ආරම්භ කළහොත්, පළමු අක්ෂරය විශාල කරන්න",
download: "ලොාකීකරණය කරන්න!"
},
"th-TH": {
prefix: "คำนำ",
suffix: "คำต่อท้าย",
capitalize: "ถ้าเริ่มด้วยตัวอักษรให้ตัวอักษรตัวแรกเป็นตัวพิมพ์ใหญ่",
download: "ดาวน์โหลดไอคอน!"
},
"lo-LA": {
prefix: "ເຄື່ອນຄົນ",
suffix: "ປ່ອນອັນ",
capitalize: "ໂດຍຖ້າເລີ່ມດ້ວຍອັກສອນ ຂອງເລີ່ມສຽງດິບໃນຂອບໃນ",
download: "ດາວເລີ່ອຄອນ!"
},
"my-MM": {
prefix: "အကြောင်းအရာ",
suffix: "အဆုံးသတ်",
capitalize: "အက္ခရာဖြင့်စလွှတ်သည့်အခါ၊ ပထမဆုံးအက္ခရာကို အကြီးစီးရေးပါ",
download: "အိုင်ကွန်းဒေါင်းလုပ်လုပ်ပါ!"
},
"ka-GE": {
prefix: "წინადადება",
suffix: "საფუძველი",
capitalize: "თუ ის იწყება ასოზე, გააკეთეთ პირველი ასო დიდი",
download: "დაამატეთ აიკონი!"
},
"am-ET": {
prefix: "ቅድመ ቃል",
suffix: "መደብ",
capitalize: "እንደ የዕለት አይደለም፣ የመጀመሪያ ፊደል ይትወዳድር",
download: "መልዕክት ይላኩ!"
},
"km-KH": {
prefix: "គម្រូ",
suffix: "សរុប",
capitalize: "បើវាលើកឡើងដោយអក្សរ សូមធ្វើអក្សរដំបូងអោយធំបំផុត",
download: "ទាញយកស្លាក!"
},
"zh-Hans-CN": {
prefix: "前缀",
suffix: "后缀",
capitalize: "如果以字母开头则大写第一个字母",
download: "下载图标!"
},
"zh-Hant-TW": {
prefix: "前綴",
suffix: "後綴",
capitalize: "如果以字母開頭則大寫第一個字母",
download: "下載圖標!"
},
"zh-Hant-HK": {
prefix: "前綴",
suffix: "後綴",
capitalize: "如果以字母開頭則大寫第一個字母",
download: "下載圖標!"
},
"ja-JP": {
prefix: "接頭辞",
suffix: "接尾辞",
capitalize: "文字で始まる場合は最初の文字を大文字にする",
download: "アイコンをダウンロード!"
},
"ko-KR": {
prefix: "접두사",
suffix: "접미사",
capitalize: "문자로 시작하면 첫 글자를 대문자로 작성하세요",
download: "아이콘 다운로드!"
}
};
let jsInitChecktimer = null;
function main() {
jsInitChecktimer = setInterval(addDownloadButtons, 1000);
}
function getFullSizeImgUrl(url) {
return url.split("=").slice(0, -1).join("=") + "=s0";
}
function getIconNameFromAlt(alttext, index) {
if (alttext === "Custom badge for members") {
alttext = null;
}
return alttext || "Icon_" + (index + 1);
}
async function getExtensionFromURL(url) {
try {
let response = await fetch(url, { method: 'HEAD' });
if (response.ok) {
let contentType = response.headers.get('Content-Type');
if (contentType) {
return contentTypeToExtension(contentType);
}
}
return 'png';
} catch (error) {
console.error("Failed to fetch Content-Type for URL:", url, error);
return 'png';
}
}
function contentTypeToExtension(contentType) {
const mapping = {
'image/jpeg': 'jpg',
'image/png': 'png',
'image/gif': 'gif',
'image/webp': 'webp',
};
return mapping[contentType] || 'png';
}
function capitalizeFirstLetter(string) {
return string.charAt(0).toUpperCase() + string.slice(1);
}
function applyPrefixSuffix(filename, prefix, suffix, capitalize) {
if (capitalize && /[a-zA-Z]/.test(filename.charAt(0))) {
filename = capitalizeFirstLetter(filename);
}
return (prefix || '') + filename + (suffix || '');
}
function extractUrls(imgs, prefix, suffix, capitalize) {
const urls = [];
const metadata = []; // 用來保存alt文本、url和filename的metadata
const filenameCount = {}; // 用來記錄每個名稱出現的次數
const firstAppearanceMap = {}; // 用來記錄每個文件名第一次出現的index和對應的url信息
for (let i = 0; i < imgs.snapshotLength; i++) {
const img = imgs.snapshotItem(i);
let originalFilename = getIconNameFromAlt(img.alt, i); // 使用原始名稱
let finalFilename;
// 初始化計數器
if (!filenameCount[originalFilename]) {
filenameCount[originalFilename] = 0; // 初始化名稱計數為 0
}
// 獲取當前計數
const count = filenameCount[originalFilename];
if (count === 0) {
// 第一次出現,保留原始名稱,但記錄它的位置以便後續修改
finalFilename = applyPrefixSuffix(originalFilename, prefix, suffix, capitalize);
// 記錄第一次出現的位置和相關信息
firstAppearanceMap[originalFilename] = {
index: i, // 記錄第一次出現的 index
url: getFullSizeImgUrl(img.src) // 記錄對應的圖片URL
};
} else {
// 重複出現,則根據計數獲取 replacementNames
const replacementIndex = Math.min(count, replacementNames.length - 1); // 確保索引不超過範圍
finalFilename = applyPrefixSuffix(replacementNames[replacementIndex], prefix, suffix, capitalize); // 使用替換名稱
// 如果是第二次出現,修改第一次的文件名
if (count === 1) {
const firstAppearance = firstAppearanceMap[originalFilename];
if (firstAppearance) {
// 將第一次的文件名更新為替換名稱的第一個
urls[firstAppearance.index].filename = applyPrefixSuffix(replacementNames[0], prefix, suffix, capitalize);
metadata[firstAppearance.index].altText = applyPrefixSuffix(replacementNames[0], prefix, suffix, capitalize); // 同步更新metadata的altText
}
}
}
// 增加計數
filenameCount[originalFilename]++;
// 將網址和檔名推入數組
urls.push({
"url": getFullSizeImgUrl(img.src),
"filename": finalFilename
});
// 保存metadata信息,包括最終的文件名
metadata.push({
"altText": finalFilename,
"url": getFullSizeImgUrl(img.src)
});
}
return { urls, metadata }; // 返回圖片數據與metadata
}
function addDownloadButtons() {
const iconContainerXPath = "//ytd-sponsorships-perk-renderer[descendant::img]";
const iconContainers = document.evaluate(iconContainerXPath, document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
if (iconContainers.snapshotLength !== 0) {
clearInterval(jsInitChecktimer);
jsInitChecktimer = null;
}
// 獲取 YouTube 的介面語言
const lang = document.documentElement.lang || 'en'; // 默認為英文
const currentLabels = labels[lang] || labels['en']; // 如果沒有對應的語言則使用英文
for (let i = 0; i < iconContainers.snapshotLength; i++) {
const container = iconContainers.snapshotItem(i);
const titleResult = document.evaluate(".//yt-formatted-string[@id=\"title\"]/text()", container, null, XPathResult.STRING_TYPE, null);
const sectionTitle = titleResult.stringValue || "";
const header = sectionTitle.split(" ").slice(0, 2).join(" ") || "unknown";
if (container.querySelector('.download-icons-button')) continue;
// 創建前綴、後綴的輸入框和大寫切換
const prefixInput = document.createElement("input");
prefixInput.placeholder = currentLabels.prefix; // 設置前綴的佔位符
prefixInput.style.marginRight = "5px";
prefixInput.style.display = "block";
const capitalizeLabel = document.createElement("label");
capitalizeLabel.style.marginRight = "5px";
capitalizeLabel.style.display = "block";
const capitalizeCheckbox = document.createElement("input");
capitalizeCheckbox.type = "checkbox";
// 創建描述文本並設置綠色字體
const descriptionText = document.createElement("span");
descriptionText.textContent = currentLabels.capitalize; // 設置描述文本
// 定義函數來找到背景色
function getBackgroundColor(element) {
let bgColor = window.getComputedStyle(element).backgroundColor;
// 當背景顏色是透明或者無法檢測時,繼續查找父元素
while (element && (bgColor === 'rgba(0, 0, 0, 0)' || bgColor === 'transparent')) {
element = element.parentElement;
if (element) {
bgColor = window.getComputedStyle(element).backgroundColor;
} else {
break;
}
}
return bgColor;
}
// 獲取背景色(從 body 開始檢測)
const backgroundColor = getBackgroundColor(document.body);
// 將背景顏色轉換為 RGB 格式
function getRGBValues(color) {
if (color.startsWith('rgb')) {
// 如果是 rgb/rgba 顏色,解析 RGB 數值
return color.match(/\d+/g).map(Number);
} else if (color.startsWith('#')) {
// 如果是 hex 顏色,將 hex 轉換為 RGB
let hex = color.replace('#', '');
if (hex.length === 3) {
hex = hex.split('').map(h => h + h).join('');
}
const bigint = parseInt(hex, 16);
return [(bigint >> 16) & 255, (bigint >> 8) & 255, bigint & 255];
}
return [255, 255, 255]; // 默認為白色(避免異常情況)
}
const rgb = getRGBValues(backgroundColor);
const brightness = (0.299 * rgb[0] + 0.587 * rgb[1] + 0.114 * rgb[2]); // 計算亮度
// 根據亮度設置文本顏色
descriptionText.style.color = brightness > 186 ? "black" : "white";
capitalizeLabel.appendChild(capitalizeCheckbox);
capitalizeLabel.appendChild(descriptionText); // 添加描述文本
const suffixInput = document.createElement("input");
suffixInput.placeholder = currentLabels.suffix; // 設置後綴的佔位符
suffixInput.style.marginRight = "5px";
const btnDownload = document.createElement("button");
btnDownload.innerText = currentLabels.download; // 設置按鈕文本
btnDownload.className = 'download-icons-button';
btnDownload.style.marginTop = '10px';
btnDownload.addEventListener("click", (e) => {
const prefix = prefixInput.value;
const suffix = suffixInput.value;
const capitalize = capitalizeCheckbox.checked;
downloadIcons(container, header, prefix, suffix, capitalize);
});
container.appendChild(prefixInput);
container.appendChild(capitalizeLabel); // 把大寫選項移到前綴後面並包含描述
container.appendChild(suffixInput);
container.appendChild(btnDownload);
}
}
async function downloadIcons(container, header, prefix, suffix, capitalize) {
const channelHandleXPath = "//*[@id=\"page-header\"]/yt-page-header-renderer/yt-page-header-view-model/div/div[1]/div/yt-content-metadata-view-model/div[1]/span";
const channelHandleNode = document.evaluate(channelHandleXPath, document, null, XPathResult.STRING_TYPE, null);
let channelHandle;
if (!channelHandleNode || !channelHandleNode.stringValue) {
channelHandle = "unknown_channel";
console.log("YouTube updated the DOM again--needs update");
} else {
channelHandle = channelHandleNode.stringValue;
}
const folderName = channelHandle + "-" + header;
const imgsContainer = document.evaluate(".//img", container, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
const { urls, metadata } = extractUrls(imgsContainer, prefix, suffix, capitalize);
const zip = new JSZip();
const iconZip = zip.folder(folderName);
const promises = [];
// 生成 metadata.json,保留原始的冒號 ':'
const metadataJson = JSON.stringify(metadata, null, 2);
iconZip.file("metadata.json", metadataJson);
for (let i = 0; i < urls.length; i++) {
const iconObj = urls[i];
const url = iconObj.url;
const filename = iconObj.filename; // 保留原始文件名
const promise = new Promise(async (resolve, reject) => {
try {
const extension = await getExtensionFromURL(url);
// 將文件名中的特殊字符替換為可顯示字符
const sanitizedFilename = filename
.replace(/\//g, '⧸')
.replace(/\\/g, '\')
.replace(/\?/g, '?')
.replace(/%/g, '%')
.replace(/\*/g, '*')
.replace(/:/g, ':')
.replace(/\|/g, '|')
.replace(/</g, '<')
.replace(/>/g, '>');
const fullFilename = sanitizedFilename + "." + extension; // 最終文件名
JSZipUtils.getBinaryContent(url, function (err, data) {
if (err) {
reject(err);
} else {
iconZip.file(fullFilename, data);
resolve();
}
});
} catch (error) {
reject(error);
}
});
promises.push(promise);
}
Promise.all(promises).then(function () {
zip.generateAsync({
type: "blob",
compression: "DEFLATE"
}).then(function (blob) {
saveAs(blob, folderName + ".zip");
}, function (err) {
alert("Failed. See the log for details.");
console.log(err);
});
}).catch(function(error) {
alert("An error occurred during the download process. See the log for details.");
console.error(error);
});
}
})();