// ==UserScript==
// @name [E/Ex-Hentai] Downloader
// @name:zh-TW [E/Ex-Hentai] 下載器
// @name:zh-CN [E/Ex-Hentai] 下载器
// @name:ja [E/Ex-Hentai] ダウンローダー
// @name:ko [E/Ex-Hentai] 다운로더
// @name:en [E/Ex-Hentai] Downloader
// @version 0.0.16-Beta6
// @author Canaan HS
// @description 漫畫頁面創建下載按鈕, 可切換 (壓縮下載 | 單圖下載), 無須複雜設置一鍵點擊下載, 自動獲取(非原圖)進行下載
// @description:zh-TW 漫畫頁面創建下載按鈕, 可切換 (壓縮下載 | 單圖下載), 無須複雜設置一鍵點擊下載, 自動獲取(非原圖)進行下載
// @description:zh-CN 漫画页面创建下载按钮, 可切换 (压缩下载 | 单图下载), 无须复杂设置一键点击下载, 自动获取(非原图)进行下载
// @description:ja マンガページにダウンロードボタンを作成し、(圧缩ダウンロード | シングルイメージダウンロード)を切り替えることができ、复雑な设定は必要なく、ワンクリックでダウンロードできます。自动的に(オリジナルではない)画像を取得してダウンロードします
// @description:ko 만화 페이지에 다운로드 버튼을 만들어 (압축 다운로드 | 단일 이미지 다운로드)를 전환할 수 있으며, 복잡한 설정이 필요하지 않고, 원클릭 다운로드 기능으로 (원본이 아닌) 이미지를 자동으로 가져와 다운로드합니다
// @description:en Create download buttons on manga pages, switchable between (compressed download | single image download), without the need for complex settings, one-click download capability, automatically fetches (non-original) images for downloading
// @connect *
// @match *://e-hentai.org/g/*
// @match *://exhentai.org/g/*
// @icon https://e-hentai.org/favicon.ico
// @license MIT
// @namespace https://greasyfork.org/users/989635
// @run-at document-body
// @grant window.close
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_download
// @grant GM_addElement
// @grant GM_xmlhttpRequest
// @grant GM_registerMenuCommand
// @grant GM_unregisterMenuCommand
// @require https://update.greasyfork.org/scripts/473358/1237031/JSZip.js
// @require https://update.greasyfork.org/scripts/495339/1456526/ObjectSyntax_min.js
// @require https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.5/FileSaver.min.js
// ==/UserScript==
(async () => {
/* 使用者配置 */
const Config = {
Dev: false, // 開發模式 (會顯示除錯訊息)
ReTry: 10, // 下載錯誤重試次數, 超過這個次數該圖片會被跳過
Original: false, // 是否下載原圖
ResetScope: true, // 下載完成後 重置範圍設置
CompleteClose: false, // 下載完成自動關閉
};
/* 下載配置 (不清楚不要修改) */
const DConfig = {
Compr_Level: 5,
MIN_CONCURRENCY: 5,
MAX_CONCURRENCY: 16,
TIME_THRESHOLD: 1e3,
MAX_Delay: 3e3,
Home_ID: 100,
Home_ND: 80,
Image_ID: 34,
Image_ND: 28,
Download_IT: 6,
Download_ID: 800,
Download_ND: 400,
Lock: false,
SortReverse: false,
Scope: undefined,
DisplayCache: undefined,
CurrentDownloadMode: undefined,
KeyCache: undefined,
GetKey: function () {
if (!this.KeyCache) this.KeyCache = `DownloadCache_${Syn.Device.Path.split("/").slice(2, 4).join("")}`;
return this.KeyCache;
},
Dynamic: function (Time, Delay, Thread = null, MIN_Delay) {
let ResponseTime = Date.now() - Time, delay, thread;
if (ResponseTime > this.TIME_THRESHOLD) {
delay = Math.floor(Math.min(Delay * 1.1, this.MAX_Delay));
if (Thread != null) {
thread = Math.floor(Math.max(Thread * (this.TIME_THRESHOLD / ResponseTime), this.MIN_CONCURRENCY));
return [delay, thread];
} else {
return delay;
}
} else {
delay = Math.ceil(Math.max(Delay * .9, MIN_Delay));
if (Thread != null) {
thread = Math.ceil(Math.min(Thread * 1.2, this.MAX_CONCURRENCY));
return [delay, thread];
} else {
return delay;
}
}
}
};
const Url = Syn.Device.Url.split("?p=")[0];
let Lang, OriginalTitle, CompressMode, ModeDisplay;
class DownloadCore {
constructor(Button) {
this.Button = Button;
this.ComicName = null;
this.Worker = Syn.WorkerCreation(`
let queue = [], processing = false;
onmessage = function(e) {
queue.push(e.data);
!processing && (processing = true, processQueue());
}
async function processQueue() {
if (queue.length > 0) {
const {index, url, time, delay} = queue.shift();
FetchRequest(index, url, time, delay);
setTimeout(processQueue, delay);
} else {processing = false}
}
async function FetchRequest(index, url, time, delay) {
try {
const response = await fetch(url);
const html = await response.text();
postMessage({index, url, html, time, delay, error: false});
} catch {
postMessage({index, url, html, time, delay, error: true});
}
}
`);
this.GetTotal = page => Math.ceil(+page[page.length - 2].textContent.replace(/\D/g, "") / 20);
this.GetHomeData();
}
async Reset() {
Config.CompleteClose && window.close();
Config.ResetScope && (DConfig.Scope = false);
const Button = Syn.$$("#ExDB");
DConfig.Lock = false;
Button.disabled = false;
Button.textContent = `✓ ${ModeDisplay}`;
}
async GetHomeData() {
const Name = Syn.NameFilter((Syn.$$("#gj").textContent || Syn.$$("#gn").textContent).trim());
const CacheData = Syn.Storage(DConfig.GetKey());
const ImgSet = Syn.$$("#gdc .ct6");
DConfig.CurrentDownloadMode = CompressMode;
this.ComicName = Name;
if (ImgSet) {
const yes = confirm(Lang.Transl("檢測到圖片集 !!\n\n是否反轉排序後下載 ?"));
yes ? DConfig.SortReverse = true : DConfig.SortReverse = false;
}
if (CacheData) {
this.StartTask(CacheData);
return;
}
const Pages = this.GetTotal(Syn.$$("#gdd td.gdt2", {
all: true
}));
let Delay = DConfig.Home_ID;
this.Worker.postMessage({
index: 0,
url: Url,
time: Date.now(),
delay: Delay
});
for (let index = 1; index < Pages; index++) {
this.Worker.postMessage({
index: index,
url: `${Url}?p=${index}`,
time: Date.now(),
delay: Delay
});
}
this.Worker.onmessage = e => {
const {
index,
url,
html,
time,
delay,
error
} = e.data;
Delay = DConfig.Dynamic(time, delay, null, DConfig.Home_ND);
error ? this.Worker.postMessage({
index: index,
url: url,
time: time,
delay: delay
}) : GetLink(index, Syn.DomParse(html));
};
const self = this;
const HomeData = new Map();
let Task = 0;
function GetLink(index, page) {
try {
const Cache = [];
for (const link of Syn.$$("#gdt a", {
all: true,
root: page
})) {
Cache.push(link.href);
}
HomeData.set(index, Cache);
DConfig.DisplayCache = `[${++Task}/${Pages}]`;
document.title = DConfig.DisplayCache;
self.Button.textContent = `${Lang.Transl("獲取頁面")}: ${DConfig.DisplayCache}`;
if (Task === Pages) {
const Cache = [];
for (let index = 0; index < HomeData.size; index++) {
Cache.push(...HomeData.get(index));
}
const Processed = [...new Set(Cache)];
Syn.Log(Lang.Transl("內頁跳轉數據"), `${Name}\n${JSON.stringify(Processed, null, 4)}`, {
dev: Config.Dev
});
self.GetImageData(Processed);
}
} catch (error) {
alert(Lang.Transl("請求錯誤重新加載頁面"));
location.reload();
}
}
}
async GetImageData(JumpList) {
const Pages = JumpList.length;
let Delay = DConfig.Image_ID;
let Task = 0;
for (let index = 0; index < Pages; index++) {
this.Worker.postMessage({
index: index,
url: JumpList[index],
time: Date.now(),
delay: Delay
});
}
this.Worker.onmessage = e => {
const {
index,
url,
html,
time,
delay,
error
} = e.data;
Delay = DConfig.Dynamic(time, delay, null, DConfig.Image_ND);
error ? this.Worker.postMessage({
index: index,
url: url,
time: time,
delay: delay
}) : GetLink(index, url, Syn.DomParse(html));
};
const self = this;
const ImageData = [];
function GetLink(index, url, page) {
try {
const Resample = Syn.$$("#img", {
root: page
});
const Original = Syn.$$("#i6 div:last-of-type a", {
root: page
});
if (!Resample) {
self.Worker.postMessage({
index: index,
url: url,
time: Date.now(),
delay: Delay
});
return;
}
const Link = Config.Original ? Original.href ?? Resample.src ?? Resample.href : Resample.src ?? Resample.href;
ImageData.push([index, {
PageUrl: url,
ImageUrl: Link
}]);
DConfig.DisplayCache = `[${++Task}/${Pages}]`;
document.title = DConfig.DisplayCache;
self.Button.textContent = `${Lang.Transl("獲取連結")}: ${DConfig.DisplayCache}`;
if (Task === Pages) {
ImageData.sort((a, b) => a[0] - b[0]);
const Processed = new Map(ImageData);
Syn.Storage(DConfig.GetKey(), {
value: Processed
});
self.StartTask(Processed);
}
} catch (error) {
Syn.Log(null, error, {
dev: Config.Dev,
type: "error"
});
Task++;
}
}
}
ReGetImageData(Index, Url) {
function GetLink(index, url, page) {
const Resample = Syn.$$("#img", {
root: page
});
const Original = Syn.$$("#i6 div:last-of-type a", {
root: page
});
if (!Resample) return false;
const Link = Config.Original ? Original.href ?? Resample.src ?? Resample.href : Resample.src ?? Resample.href;
return [index, url, Link];
}
let Token = Config.ReTry;
return new Promise((resolve, reject) => {
this.Worker.postMessage({
index: Index,
url: Url,
time: Date.now(),
delay: DConfig.Image_ID
});
this.Worker.onmessage = e => {
const {
index,
url,
html,
time,
delay,
error
} = e.data;
if (error) {
this.Worker.postMessage({
Index: Index,
url: Url,
time: time,
delay: delay
});
} else {
if (Token <= 0) reject(false);
const result = GetLink(index, url, Syn.DomParse(html));
if (result) resolve(result); else {
this.Worker.postMessage({
Index: Index,
url: Url,
time: time,
delay: delay
});
Token - 1;
}
}
};
});
}
StartTask(DataMap) {
Syn.Log(Lang.Transl("圖片連結數據"), `${this.ComicName}\n${JSON.stringify([...DataMap], null, 4)}`, {
dev: Config.Dev
});
if (DConfig.Scope) {
DataMap = new Map(Syn.ScopeParsing(DConfig.Scope, [...DataMap]));
}
if (DConfig.SortReverse) {
const Size = DataMap.size - 1;
DataMap = new Map([...DataMap.entries()].map(([index, url]) => [Size - index, url]));
}
Syn.Log(Lang.Transl("任務配置"), {
ReTry: Config.ReTry,
Original: Config.Original,
ResetScope: Config.ResetScope,
CompleteClose: Config.CompleteClose,
SortReverse: DConfig.SortReverse,
DownloadMode: DConfig.CurrentDownloadMode,
CompressionLevel: DConfig.Compr_Level
}, {
dev: Config.Dev
});
this.Button.textContent = Lang.Transl("開始下載");
DConfig.CurrentDownloadMode ? this.PackDownload(DataMap) : this.SingleDownload(DataMap);
}
async PackDownload(Data) {
const self = this;
const Zip = new JSZip();
let Total = Data.size;
const Fill = Syn.GetFill(Total);
let Enforce = false;
let ClearCache = false;
let ReTry = Config.ReTry;
let Task, Progress, Thread, Delay;
function Init() {
Task = 0;
Progress = 0;
Delay = DConfig.Download_ID;
Thread = DConfig.Download_IT;
}
function Force() {
if (Total > 0) {
const SortData = [...Data].sort((a, b) => a[0] - b[0]);
SortData.splice(0, 0, {
ErrorPage: SortData.map(item => ++item[0]).join(",")
});
Syn.Log(Lang.Transl("下載失敗數據"), JSON.stringify(SortData, null, 4), {
type: "error"
});
}
Enforce = true;
Init();
self.Compression(Zip);
}
function RunClear() {
if (!ClearCache) {
ClearCache = true;
sessionStorage.removeItem(DConfig.GetKey());
Syn.Log(Lang.Transl("清理警告"), Lang.Transl("下載數據不完整將清除緩存, 建議刷新頁面後重載"), {
type: "warn"
});
}
}
function StatusUpdate(time, index, purl, iurl, blob, error = false) {
if (Enforce) return;
[Delay, Thread] = DConfig.Dynamic(time, Delay, Thread, DConfig.Download_ND);
Data.delete(index);
if (error && typeof iurl === "string") {
Data.set(index, {
PageUrl: purl,
ImageUrl: iurl
});
} else {
Zip.file(`${self.ComicName}/${Syn.Mantissa(index, Fill, "0", iurl)}`, blob);
}
DConfig.DisplayCache = `[${++Progress}/${Total}]`;
document.title = DConfig.DisplayCache;
self.Button && (self.Button.textContent = `${Lang.Transl("下載進度")}: ${DConfig.DisplayCache}`);
--Task;
if (Progress === Total) {
Total = Data.size;
if (Total > 0 && ReTry-- > 0) {
DConfig.DisplayCache = Lang.Transl("等待失敗重試...");
document.title = DConfig.DisplayCache;
self.Button.textContent = DConfig.DisplayCache;
setTimeout(() => { Start(Data, true) }, 2e3);
} else Force();
} else if (Progress > Total) Init();
}
function Request(Index, Purl, Iurl) {
if (Enforce) return;
if (typeof Iurl !== "undefined") {
const time = Date.now();
let timeout = null;
++Task;
GM_xmlhttpRequest({
url: Iurl,
timeout: 2e4,
method: "GET",
responseType: "blob",
onload: response => {
clearTimeout(timeout);
const blob = response.response;
response.status == 200 && blob instanceof Blob && blob.size > 0 ? StatusUpdate(time, Index, Purl, Iurl, blob) : StatusUpdate(time, Index, Purl, Iurl, null, true);
},
onerror: () => {
clearTimeout(timeout);
StatusUpdate(time, Index, Purl, Iurl, null, true);
}
});
timeout = setTimeout(() => {
StatusUpdate(time, Index, Purl, Iurl, null, true);
}, 2e4);
} else {
RunClear();
StatusUpdate(time, Index, null, null, true);
}
}
async function Start(DataMap, ReGet = false) {
if (Enforce) return;
Init();
for (const [Index, Uri] of DataMap.entries()) {
if (Enforce) break;
if (ReGet) {
Syn.Log(Lang.Transl("重新取得數據"), {
Uri: Uri.PageUrl
}, {
dev: Config.Dev
});
const Result = await self.ReGetImageData(Index, Uri.PageUrl);
Syn.Log(Lang.Transl("取得結果"), {
Result: Result
}, {
dev: Config.Dev
});
if (Result) {
const [Index, Purl, Iurl] = Result;
Request(Index, Purl, Iurl);
} else {
RunClear();
Request(Index, Uri.PageUrl, Uri.ImageUrl);
}
} else {
while (Task >= Thread) {
await Syn.Sleep(Delay);
}
Request(Index, Uri.PageUrl, Uri.ImageUrl);
}
}
}
Start(Data);
Syn.Menu({
[Lang.Transl("📥 強制壓縮下載")]: {
func: () => Force(),
hotkey: "d"
}
}, "Enforce");
}
async SingleDownload(Data) {
const self = this;
let Total = Data.size;
const Fill = Syn.GetFill(Total);
const TaskPromises = [];
let Task = 0;
let Progress = 0;
let RetryDelay = 1e3;
let ClearCache = false;
let ReTry = Config.ReTry;
let Delay = DConfig.Download_ID;
let Thread = DConfig.Download_IT;
function RunClear() {
if (!ClearCache) {
ClearCache = true;
sessionStorage.removeItem(DConfig.GetKey());
Syn.Log(Lang.Transl("清理警告"), Lang.Transl("下載數據不完整將清除緩存, 建議刷新頁面後重載"), {
type: "warn"
});
}
}
async function Request(Index, Purl, Iurl, Retry) {
return new Promise((resolve, reject) => {
if (typeof Iurl !== "undefined") {
const time = Date.now();
++Task;
GM_download({
url: Iurl,
name: `${self.ComicName}-${Syn.Mantissa(Index, Fill, "0", Iurl)}`,
onload: () => {
[Delay, Thread] = DConfig.Dynamic(time, Delay, Thread, DConfig.Download_ND);
DConfig.DisplayCache = `[${++Progress}/${Total}]`;
document.title = DConfig.DisplayCache;
self.Button && (self.Button.textContent = `${Lang.Transl("下載進度")}: ${DConfig.DisplayCache}`);
--Task;
resolve();
},
onerror: () => {
if (Retry > 0) {
[Delay, Thread] = DConfig.Dynamic(time, Delay, Thread, DConfig.Download_ND);
Syn.Log(null, `[Delay:${Delay}|Thread:${Thread}|Retry:${Retry}] : [${Iurl}]`, {
dev: Config.Dev,
type: "error"
});
--Task;
setTimeout(() => {
self.ReGetImageData(Index, Purl).then(data => {
const [Index, Purl, Iurl] = data;
Request(Index, Purl, Iurl, Retry - 1);
reject();
}).catch(err => {
RunClear();
reject();
});
}, RetryDelay += 1e3);
} else {
--Task;
reject(new Error("Request error"));
}
}
});
} else {
RunClear();
reject();
}
});
}
for (const [Index, Uri] of Data.entries()) {
while (Task >= Thread) {
await Syn.Sleep(Delay);
}
TaskPromises.push(Request(Index, Uri.PageUrl, Uri.ImageUrl, ReTry));
}
await Promise.allSettled(TaskPromises);
this.Button.textContent = Lang.Transl("下載完成");
this.Button = null;
setTimeout(() => {
document.title = `✓ ${OriginalTitle}`;
this.Reset();
}, 3e3);
}
async Compression(Zip) {
const self = this;
GM_unregisterMenuCommand("Enforce-1");
function ErrorProcess(result) {
document.title = OriginalTitle;
DConfig.DisplayCache = Lang.Transl("壓縮失敗");
self.Button.textContent = DConfig.DisplayCache;
Syn.Log(DConfig.DisplayCache, result, {
dev: Config.Dev,
type: "error",
collapsed: false
});
setTimeout(() => {
self.Button.disabled = false;
self.Button.textContent = ModeDisplay;
self.Button = null;
}, 4500);
}
if (Object.keys(Zip.files).length == 0) {
ErrorProcess("無數據可壓縮");
return;
}
Zip.generateAsync({
type: "blob",
compression: "DEFLATE",
compressionOptions: {
level: DConfig.Compr_Level
}
}, progress => {
DConfig.DisplayCache = `${progress.percent.toFixed(1)} %`;
document.title = DConfig.DisplayCache;
this.Button.textContent = `${Lang.Transl("壓縮進度")}: ${DConfig.DisplayCache}`;
}).then(zip => {
saveAs(zip, `${this.ComicName}.zip`);
document.title = `✓ ${OriginalTitle}`;
this.Button.textContent = Lang.Transl("壓縮完成");
this.Button = null;
setTimeout(() => {
this.Reset();
}, 3e3);
}).catch(result => {
ErrorProcess(result);
});
}
}
class ButtonCore {
constructor() {
this.E = /https:\/\/e-hentai\.org\/g\/\d+\/[a-zA-Z0-9]+/;
this.Ex = /https:\/\/exhentai\.org\/g\/\d+\/[a-zA-Z0-9]+/;
this.Allow = (Uri = Url) => this.E.test(Uri) || this.Ex.test(Uri);
this.InitStyle = () => {
const Position = `
.Download_Button {
float: right;
width: 12rem;
cursor: pointer;
font-weight: 800;
line-height: 20px;
border-radius: 5px;
position: relative;
padding: 5px 5px;
font-family: arial, helvetica, sans-serif;
}
`;
const E_Style = `
.Download_Button {
color: #5C0D12;
border: 2px solid #9a7c7e;
background-color: #EDEADA;
}
.Download_Button:hover {
color: #8f4701;
border: 2px dashed #B5A4A4;
}
.Download_Button:disabled {
color: #B5A4A4;
border: 2px dashed #B5A4A4;
cursor: default;
}
`;
const Ex_Style = `
.Download_Button {
color: #b3b3b3;
border: 2px solid #34353b;
background-color: #2c2b2b;
}
.Download_Button:hover {
color: #f1f1f1;
border: 2px dashed #4f535b;
}
.Download_Button:disabled {
color: #4f535b;
border: 2px dashed #4f535b;
cursor: default;
}
`;
const Style = Syn.Device.Host === "e-hentai.org" ? E_Style : Ex_Style;
Syn.AddStyle(`${Position}${Style}`, "Button-style", false);
};
}
async DownloadModeSwitch() {
CompressMode ? Syn.Store("s", "CompressedMode", false) : Syn.Store("s", "CompressedMode", true);
Syn.$$("#ExDB").remove();
this.ButtonCreation();
}
async DownloadRangeSetting() {
let scope = prompt(Lang.Transl("範圍設置")) || false;
if (scope) {
const yes = confirm(`${Lang.Transl("確認設置範圍")}:\n${scope}`);
if (yes) DConfig.Scope = scope;
}
}
async ButtonCreation() {
CompressMode = Syn.Store("g", "CompressedMode", []);
ModeDisplay = CompressMode ? Lang.Transl("壓縮下載") : Lang.Transl("單圖下載");
const download_button = GM_addElement(Syn.$$("#gd2"), "button", {
id: "ExDB",
class: "Download_Button"
});
download_button.disabled = DConfig.Lock ? true : false;
download_button.textContent = DConfig.Lock ? Lang.Transl("下載中鎖定") : ModeDisplay;
Syn.AddListener(download_button, "click", () => {
DConfig.Lock = true;
download_button.disabled = true;
download_button.textContent = Lang.Transl("開始下載");
this.TaskInstance = new DownloadCore(download_button);
}, {
capture: true,
passive: true
});
}
static async Init() {
const Core = new ButtonCore();
if (Core.Allow()) {
Core.InitStyle();
OriginalTitle = document.title;
Lang = Language(Syn.Device.Lang);
Core.ButtonCreation();
if (Syn.Storage(DConfig.GetKey())) {
const menu = GM_registerMenuCommand(Lang.Transl("🚮 清除數據緩存"), () => {
sessionStorage.removeItem(DConfig.GetKey());
GM_unregisterMenuCommand(menu);
});
}
Syn.Menu({
[Lang.Transl("🔁 切換下載模式")]: {
func: () => Core.DownloadModeSwitch()
},
[Lang.Transl("⚙️ 下載範圍設置")]: {
func: () => Core.DownloadRangeSetting()
}
});
}
}
}
function Language(lang) {
const Word = {
Traditional: {
"範圍設置": "下載完成後自動重置\n\n單項設置: 1. 2, 3\n範圍設置: 1~5, 6-10\n排除設置: !5, -10\n"
},
Simplified: {
"🚮 清除數據緩存": "🚮 清除数据缓存",
"🔁 切換下載模式": "🔁 切换下载模式",
"⚙️ 下載範圍設置": "⚙️ 下载范围设置",
"📥 強制壓縮下載": "📥 强制压缩下载",
"⛔️ 終止下載": "⛔️ 终止下载",
"壓縮下載": "压缩下载",
"單圖下載": "单图下载",
"下載中鎖定": "下载中锁定",
"開始下載": "开始下载",
"獲取頁面": "获取页面",
"獲取連結": "获取链接",
"下載進度": "下载进度",
"壓縮進度": "压缩进度",
"壓縮完成": "压缩完成",
"壓縮失敗": "压缩失败",
"下載完成": "下载完成",
"清理警告": "清理警告",
"任務配置": "任务配置",
"取得結果": "取得结果",
"重新取得數據": "重新取得数据",
"確認設置範圍": "确认设置范围",
"剩餘重載次數": "剩余重载次数",
"下載失敗數據": "下载失败数据",
"內頁跳轉數據": "内页跳转数据",
"圖片連結數據": "图片链接数据",
"等待失敗重試...": "等待失败重试...",
"請求錯誤重新加載頁面": "请求错误重新加载页面",
"檢測到圖片集 !!\n\n是否反轉排序後下載 ?": "检测到图片集 !!\n\n是否反转排序后下载?",
"下載數據不完整將清除緩存, 建議刷新頁面後重載": "下载数据不完整将清除缓存, 建议刷新页面后重载",
"找不到圖片元素, 你的 IP 可能被禁止了, 請刷新頁面重試": "找不到图片元素, 你的 IP 可能被禁止了, 请刷新页面重试",
"範圍設置": "下载完成后自动重置\n\n单项设置: 1. 2, 3\n范围设置: 1~5, 6-10\n排除设置: !5, -10\n"
},
English: {
"🚮 清除數據緩存": "🚮 Clear data cache",
"🔁 切換下載模式": "🔁 Switch download mode",
"⚙️ 下載範圍設置": "⚙️ Download range settings",
"📥 強制壓縮下載": "📥 Force compressed download",
"⛔️ 終止下載": "⛔️ Terminate download",
"壓縮下載": "Compressed download",
"單圖下載": "Single image download",
"下載中鎖定": "Locked during download",
"開始下載": "Start download",
"獲取頁面": "Fetch page",
"獲取連結": "Fetch link",
"下載進度": "Download progress",
"壓縮進度": "Compression progress",
"壓縮完成": "Compression complete",
"壓縮失敗": "Compression failed",
"下載完成": "Download complete",
"清理警告": "Clean up warning",
"任務配置": "Task Configuration",
"取得結果": "Fetch Results",
"重新取得數據": "Refetch Data",
"確認設置範圍": "Confirm range settings",
"剩餘重載次數": "Remaining reload attempts",
"下載失敗數據": "Failed download data",
"內頁跳轉數據": "Inner page redirection data",
"圖片連結數據": "Image link data",
"等待失敗重試...": "Waiting for failed retry...",
"請求錯誤重新加載頁面": "Request error, reload the page",
"檢測到圖片集 !!\n\n是否反轉排序後下載 ?": "Image collection detected !!\n\nWould you like to reverse the order and download?",
"下載數據不完整將清除緩存, 建議刷新頁面後重載": "Download data is incomplete, cache will be cleared, it's recommended to refresh the page and reload",
"找不到圖片元素, 你的 IP 可能被禁止了, 請刷新頁面重試": "Image element not found, your IP might be blocked, please refresh the page and try again",
"範圍設置": "Automatically reset after download completion\n\nSingle item settings: 1. 2, 3\nRange settings: 1~5, 6-10\nExclusion settings: !5, -10\n"
},
Korea: {
"🚮 清除數據緩存": "🚮 데이터 캐시 삭제",
"🔁 切換下載模式": "🔁 다운로드 모드 전환",
"⚙️ 下載範圍設置": "⚙️ 다운로드 범위 설정",
"📥 強制壓縮下載": "📥 강제 압축 다운로드",
"⛔️ 終止下載": "⛔️ 다운로드 중단",
"壓縮下載": "압축 다운로드",
"單圖下載": "단일 이미지 다운로드",
"下載中鎖定": "다운로드 중 잠금",
"開始下載": "다운로드 시작",
"獲取頁面": "페이지 가져오기",
"獲取連結": "링크 가져오기",
"下載進度": "다운로드 진행",
"壓縮進度": "압축 진행",
"壓縮完成": "압축 완료",
"壓縮失敗": "압축 실패",
"下載完成": "다운로드 완료",
"清理警告": "경고 정리",
"任務配置": "작업 설정",
"取得結果": "결과 가져오기",
"重新取得數據": "데이터 다시 가져오기",
"確認設置範圍": "설정 범위 확인",
"剩餘重載次數": "남은 재시도 횟수",
"下載失敗數據": "다운로드 실패 데이터",
"內頁跳轉數據": "내부 페이지 리디렉션 데이터",
"圖片連結數據": "이미지 링크 데이터",
"等待失敗重試...": "실패 재시도를 기다리는 중...",
"請求錯誤重新加載頁面": "요청 오류, 페이지를 다시 로드하십시오",
"檢測到圖片集 !!\n\n是否反轉排序後下載 ?": "이미지 모음이 감지되었습니다 !!\n\n역순으로 정렬하여 다운로드하시겠습니까?",
"下載數據不完整將清除緩存, 建議刷新頁面後重載": "다운로드 데이터가 불완전합니다. 캐시가 지워집니다. 페이지를 새로고침하고 다시 로드하는 것이 좋습니다",
"找不到圖片元素, 你的 IP 可能被禁止了, 請刷新頁面重試": "이미지 요소를 찾을 수 없습니다. 귀하의 IP가 차단되었을 수 있습니다. 페이지를 새로고침하고 다시 시도하십시오",
"範圍設置": "다운로드 완료 후 자동 재설정\n\n단항 설정: 1. 2, 3\n범위 설정: 1~5, 6-10\n제외 설정: !5, -10\n"
},
Japan: {
"🚮 清除數據緩存": "🚮 データキャッシュを削除",
"🔁 切換下載模式": "🔁 ダウンロードモードの切り替え",
"⚙️ 下載範圍設置": "⚙️ ダウンロード範囲設定",
"📥 強制壓縮下載": "📥 強制圧縮ダウンロード",
"⛔️ 終止下載": "⛔️ ダウンロードを中止",
"壓縮下載": "圧縮ダウンロード",
"單圖下載": "単一画像のダウンロード",
"下載中鎖定": "ダウンロード中にロック",
"開始下載": "ダウンロードを開始",
"獲取頁面": "ページを取得",
"獲取連結": "リンクを取得",
"下載進度": "ダウンロード進行",
"壓縮進度": "圧縮進行",
"壓縮完成": "圧縮完了",
"壓縮失敗": "圧縮失敗",
"下載完成": "ダウンロード完了",
"清理警告": "警告のクリーニング",
"任務配置": "タスク設定",
"取得結果": "結果を取得",
"重新取得數據": "データを再取得",
"確認設置範圍": "設定範囲の確認",
"剩餘重載次數": "残りのリロード回数",
"下載失敗數據": "ダウンロード失敗データ",
"內頁跳轉數據": "内部ページリダイレクトデータ",
"圖片連結數據": "画像リンクデータ",
"等待失敗重試...": "失敗したリトライを待機中...",
"請求錯誤重新加載頁面": "リクエストエラー、ページを再読み込みしてください",
"檢測到圖片集 !!\n\n是否反轉排序後下載 ?": "画像集が検出されました !!\n\n逆順に並べ替えてダウンロードしますか?",
"下載數據不完整將清除緩存, 建議刷新頁面後重載": "ダウンロードデータが不完全です。キャッシュがクリアされます。ページをリフレッシュしてリロードすることをお勧めします",
"找不到圖片元素, 你的 IP 可能被禁止了, 請刷新頁面重試": "画像要素が見つかりません。あなたのIPがブロックされた可能性があります。ページをリフレッシュして再試行してください",
"範圍設置": "ダウンロード完了後に自動リセット\n\n単項設定: 1. 2, 3\n範囲設定: 1~5, 6-10\n除外設定: !5, -10\n"
}
}, Match = {
ko: Word.Korea,
ja: Word.Japan,
"en-US": Word.English,
"zh-CN": Word.Simplified,
"zh-SG": Word.Simplified,
"zh-TW": Word.Traditional,
"zh-HK": Word.Traditional,
"zh-MO": Word.Traditional
}, ML = Match[lang] ?? Match["en-US"];
return {
Transl: Str => ML[Str] ?? Str
};
}
ButtonCore.Init();
})();