diff --git a/src/main.js b/src/main.js index abe0508..bfdee1a 100644 --- a/src/main.js +++ b/src/main.js @@ -2,6 +2,7 @@ var zip; var imageList = []; var imageData = []; +var infoObj; var infoStr; var origin = window.location.origin; var setting = null; @@ -53,6 +54,7 @@ var ehDownloadRegex = { IPBanExpires: /The ban expires in \d+ hours?( and \d+ minutes?)?/, donatorPower: /Donations<\/td>([+-]?[\d\.]+)<\/td>/, postedTime: /Posted:<\/td>(.*?)<\/td>/, + Language: /Language:<\/td>([A-Za-z]+).*?<\/td>/, categoryTag: /g\/c\/(\w+)\./, slashOnly: /^[\\/]*$/, originalImagePattern: /\/fullimg(?:\.php\?|\/)/ @@ -373,6 +375,11 @@ function isInPeakHours() { return (day === 0 ? hour >= 5 : hour >= 14) && hour < 20; } +function PostedTime() { + var galleryTime = (document.documentElement.innerHTML.match(ehDownloadRegex.postedTime) || [])[1]; + return Date.parse(galleryTime + '+0000'); +} + function isRecentGallery() { // 2022-06-07 // A couple of minor tweaks to the "Download source image" changes, since it dropped the utilization by a lot more than we needed it to: @@ -381,18 +388,14 @@ function isRecentGallery() { // 2022-11-25 // - One of two new image servers that replace the current oldest image server is now live. (The second one will probably go live in about a month.) // - Increased the cutoff for how old a gallery has to be before it charges GP for original file downloads during peak hours from 30 days to 90 days. - var galleryTime = (document.documentElement.innerHTML.match(ehDownloadRegex.postedTime) || [])[1]; - var time = Date.parse(galleryTime + '+0000'); - return Date.now() - time < 90 * 24 * 60 * 60 * 1000; + return Date.now() - PostedTime() < 90 * 24 * 60 * 60 * 1000; } function isAncientGallery() { // 2023-07-07 // - Galleries posted more than 1 year ago now always require GP to use the "download original image" links. As before, galleries uploaded 3-12 months ago can still use this function with the image quota outside of peak hours, while galleries uploaded less than 3 months ago can do this even during peak hours. // (This still doesn't apply to donators, whose image limits are tied to account rather than IP address and thus cannot be "refreshed" just by switching IP) - var galleryTime = (document.documentElement.innerHTML.match(ehDownloadRegex.postedTime) || [])[1]; - var time = Date.parse(galleryTime + '+0000'); - return Date.now() - time >= 365 * 24 * 60 * 60 * 1000; + return Date.now() - PostedTime() >= 365 * 24 * 60 * 60 * 1000; } function isDonator() { @@ -541,7 +544,7 @@ function storeRes(res, index) { updateTotalStatus(); if (!isPausing) checkFailed(); - + res = null; } @@ -568,6 +571,53 @@ function generateZip(isFromFS, fs, isRetry, forced){ if (setting['save-info'] === 'file' || !setting['save-info']) { (dirName && !ehDownloadRegex.slashOnly.test(dirName) ? zip.folder(dirName) : zip).file('info.txt', infoStr.replace(/\n/gi, '\r\n')); + } else if (setting['save-info'] === 'comicinfo_xml') { + // https://anansi-project.github.io/docs/comicinfo/intro + const doc = document.implementation.createDocument("", "", null); + const ComicInfo = doc.createElement("ComicInfo"); + function appendElement(field, value){ + if (value === undefined) return; + const el = doc.createElement(field); + el.textContent = value; + ComicInfo.appendChild(el); + } + if (infoObj["SubTitle"]) // No consensus for element SubTitle in ComicInfo yet + appendElement("Title", infoObj["SubTitle"]); + else + appendElement("Title", infoObj["Title"]); + appendElement("Summary", infoObj["Uploader Comment"]); + const PosetedDate = new Date(PostedTime()); + appendElement("Year", PosetedDate.getUTCFullYear()); + appendElement("Month", PosetedDate.getUTCMonth()); + appendElement("Day", PosetedDate.getUTCDay()); + var artist = infoObj["Tags"].find(tag => tag.startsWith("artist:")); + if (artist !== undefined) + appendElement("Writer", artist.slice(7)); + else { + artist = infoObj["Tags"].find(tag => tag.startsWith("艺术家:")) + if (artist !== undefined) + appendElement("Writer", artist.slice(4)); + } + appendElement("Publisher", infoObj["Uploader"]); + appendElement("Genre", infoObj["Category"]); + appendElement("Tags", infoObj["Tags"].join(",")); + appendElement("Web", window.location.href); + const language = (document.documentElement.innerHTML.match(ehDownloadRegex.Language) || [])[1]; // use regex to avoid conflict with EhSyringe + if (language !== undefined) { + const LanguageCode = new Map([["Afrikaans", "af"], ["Albanian", "sq"], ["Arabic", "ar"], ["Aramaic", "arc"], ["Armenian", "hy"], ["Bengali", "bn"], ["Bosnian", "bs"], ["Bulgarian", "bg"], ["Burmese", "my"], ["Catalan", "ca"], ["Cebuano", "ceb"], ["Chinese", "zh"], ["Cree", "cr"], ["Croatian", "hr"], ["Czech", "cs"], ["Danish", "da"], ["Dutch", "nl"], ["English", "en"], ["Esperanto", "eo"], ["Estonian", "et"], ["Finnish", "fi"], ["French", "fr"], ["Georgian", "ka"], ["German", "de"], ["Greek", "el"], ["Gujarati", "gu"], ["Hebrew", "he"], ["Hindi", "hi"], ["Hmong", "hmn"], ["Hungarian", "hu"], ["Icelandic", "is"], ["Indonesian", "id"], ["Irish", "ga"], ["Italian", "it"], ["Japanese", "ja"], ["Javanese", "jv"], ["Kannada", "kn"], ["Kazakh", "kk"], ["Khmer", "km"], ["Korean", "ko"], ["Kurdish", "ku"], ["Ladino", "lad"], ["Lao", "lo"], ["Latin", "la"], ["Latvian", "lv"], ["Marathi", "mr"], ["Mongolian", "mn"], ["Nepali", "ne"], ["Norwegian", "no"], ["Oromo", "om"], ["Papiamento", "pap"], ["Pashto", "ps"], ["Persian", "fa"], ["Polish", "pl"], ["Portuguese", "pt"], ["Punjabi", "pa"], ["Romanian", "ro"], ["Russian", "ru"], ["Sango", "sg"], ["Sanskrit", "sa"], ["Serbian", "sr"], ["Shona", "sn"], ["Slovak", "sk"], ["Slovenian", "sl"], ["Somali", "so"], ["Spanish", "es"], ["Swahili", "sw"], ["Swedish", "sv"], ["Tagalog", "tl"], ["Tamil", "ta"], ["Telugu", "te"], ["Thai", "th"], ["Tibetan", "bo"], ["Tigrinya", "ti"], ["Turkish", "tr"], ["Ukrainian", "uk"], ["Urdu", "ur"], ["Vietnamese", "vi"], ["Welsh", "cy"], ["Yiddish", "yi"], ["Zulu", "zu"]]); + if (LanguageCode.has(language)) appendElement("LanguageISO", LanguageCode.get(language)); + } + var characters = infoObj["Tags"].filter(tag => tag.startsWith("character:")); + if (characters.length != 0) { + appendElement("Characters", characters.map(tag => tag.slice(10)).join(',')); + } else { + characters = infoObj["Tags"].filter(tag => tag.startsWith("角色:")); + if (characters.length != 0) appendElement("Characters", characters.map(tag => tag.slice(3)).join(',')); + } + appendElement("EHD_info", infoStr); + doc.appendChild(ComicInfo); + const serializer = new XMLSerializer(); + zip.file('ComicInfo.xml', serializer.serializeToString(doc)); // ComicInfo.xml must be at top-level } } @@ -1230,7 +1280,7 @@ function fetchOriginalImage(index, nodeList) { if (byteLength === 28) { // 'An error has occurred. (403)' Length console.log('[EHD] #' + (index + 1) + ': An error has occurred. (403)'); console.log('[EHD] #' + (index + 1) + ': RealIndex >', imageList[index]['realIndex'], ' | ReadyState >', res.readyState, ' | Status >', res.status, ' | StatusText >', res.statusText + '\nRequest URL >', requestURL, '\nFinal URL >', res.finalUrl, '\nResposeHeaders >' + res.responseHeaders); - + updateProgress(nodeList, { status: 'Failed! (Error 403)', progress: '0', @@ -1442,7 +1492,7 @@ If you want to reset your limits by paying your GPs or credits right now, or exc for (var i in res) { delete res[i]; } - + if (setting['force-resized'] || confirm('Your account has been suspended.\n\n\ Your account is suspended by E-Hentai, and you can check your unblock time on E-Hentai forum. At this time, you cannot access to any user-related page and download original images.\n\ You can still access to resized images, would you like to switch to download resized images?')) { @@ -1872,7 +1922,7 @@ function getAllPagesURL() { pushDialog('Succeed!'); curPage++; - + if (!pagesLength) { // can't get pagesLength correctly before pagesLength = responseText.match(ehDownloadRegex.pagesLength)[1] - 0; } @@ -1952,6 +2002,7 @@ function initEHDownload() { isPausing = false; zip = new JSZip(); infoStr = ''; + infoObj = {}; fetchPagesXHR.abort(); if (setting['recheck-file-name']) { @@ -2001,7 +2052,7 @@ function initEHDownload() { // Chrome can use about 10% of free space of disk where Chrome User Data stored in as TEMPORARY File System Storage. if (navigator.webkitTemporaryStorage) { // if support navigator.webkitTemporaryStorage to check usable space - // use `queryUsageAndQuota` instead of `requestQuota` to check storage space, + // use `queryUsageAndQuota` instead of `requestQuota` to check storage space, // because `requestQuota` is incorrect when harddisk is full, says have about 5GB storage navigator.webkitTemporaryStorage.queryUsageAndQuota(function (usage, quota) { console.log('[EHD] Free TEMPORARY File System Space >', quota - usage); @@ -2032,6 +2083,8 @@ function initEHDownload() { // Array.prototype.some() is a bit ugly, so we use toString().indexOf() lol var infoNeeds = setting['save-info-list'].toString(); if (infoNeeds.indexOf('title') >= 0) { + infoObj["Title"] = document.getElementById('gn').textContent; + infoObj["SubTitle"] = document.getElementById('gj').textContent; infoStr += replaceHTMLEntites( document.getElementById('gn').textContent + '\n' + document.getElementById('gj').textContent + '\n' + @@ -2040,8 +2093,10 @@ function initEHDownload() { } if (infoNeeds.indexOf('metas') >= 0) { - infoStr += 'Category: ' + document.querySelector('#gdc .cs').textContent.trim() + '\n' + - 'Uploader: ' + replaceHTMLEntites(document.querySelector('#gdn').textContent) + '\n'; + infoObj["Category"] = document.querySelector('#gdc .cs').textContent.trim() + infoObj["Uploader"] = replaceHTMLEntites(document.querySelector('#gdn').textContent) + infoStr += 'Category: ' + infoObj["Category"] + '\n' + + 'Uploader: ' + infoObj["Uploader"] + '\n'; } var metaNodes = document.querySelectorAll('#gdd tr'); for (var i = 0; i < metaNodes.length; i++) { @@ -2052,6 +2107,7 @@ function initEHDownload() { if (infoNeeds.indexOf('metas') >= 0) infoStr += 'Rating: ' + unsafeWindow.average_rating + '\n\n'; if (infoNeeds.indexOf('tags') >= 0) { + infoObj["Tags"] = []; infoStr += 'Tags:\n'; var tagsList = document.querySelectorAll('#taglist tr'); @@ -2060,6 +2116,9 @@ function initEHDownload() { infoStr += '> ' + tds[0].textContent + ' '; var tags = tds[1].querySelectorAll('a'); + for (var tag of tags) { + infoObj["Tags"].push(tds[0].textContent + tag.textContent); + } infoStr += Array.prototype.map.call(tags, function(e){ return e.textContent; }).join(', ') + '\n'; @@ -2069,7 +2128,8 @@ function initEHDownload() { } if (infoNeeds.indexOf('uploader-comment') >= 0 && document.getElementById('comment_0')) { - infoStr += 'Uploader Comment:\n' + document.getElementById('comment_0').innerHTML.replace(/
|
/gi, '\n') + '\n\n'; + infoObj["Uploader Comment"] = document.getElementById('comment_0').innerHTML.replace(/
|
/gi, '\n'); + infoStr += 'Uploader Comment:\n' + infoObj["Uploader Comment"] + '\n\n'; } isDownloading = true; pushDialog(infoStr); @@ -2089,7 +2149,7 @@ function initEHDownload() { function initVisibilityListener() { var hidden, visibilityChange; - if (typeof document.hidden !== 'undefined') { // Opera 12.10 and Firefox 18 and later support + if (typeof document.hidden !== 'undefined') { // Opera 12.10 and Firefox 18 and later support hidden = 'hidden'; visibilityChange = 'visibilitychange'; } @@ -2146,7 +2206,7 @@ function initProgressTable(){ function requestDownload(ignoreFailed){ if (isPausing) return; - + if (setting['delay-request']) { var curTime = Date.now(); if (delayTime < curTime) { @@ -2354,7 +2414,7 @@ function getPageData(index) { }); fetchCount--; imageData[index] = null; - + updateTotalStatus(); } else { @@ -2474,7 +2534,7 @@ function showSettings() {
\
(4)
\
(5)
\ -
\ +
\
...which includes
\
\
\