Last active
November 26, 2022 18:49
-
-
Save GHolk/1d125debefe6714d186190df421c7eb4 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var downloadJob = tri.downloadJob = { | |
browser: tri.browserBg || browser, | |
os: 'windows', | |
native: false, | |
downloadPath: 'kwk', | |
stop: false, | |
ex: tri.excmds, | |
idList: [], | |
dlhandlerwrap(...args) { | |
return tri.downloadJob.dlhandler(...args) | |
}, | |
dlhandler() { | |
}, | |
async init() { | |
const tab = await this.browser.tabs.query({active: true, currentWindow: true}) | |
this.tabId = tab[0].id | |
this.windowId = tab[0].windowId | |
return | |
this.dlhandle = function (e) { | |
const i = this.idList.findIndex(x => x.id == e.id) | |
if (i == -1) return | |
const job = this.idList[i] | |
let state | |
if (typeof e.state == 'object') state = e.state.current | |
else state = e.state | |
switch (e.state.current) { | |
case 'complete': | |
job.resolve() | |
case 'interrupted': | |
let error = new Error('download fail') | |
error.codeName = 'download-error' | |
job.reject(error) | |
this.idList.splice(i, 1) | |
default: | |
true | |
} | |
} | |
this.browser.downloads.onChanged.addListener(this.dlhandlerwrap) | |
this.browser.downloads.onCreated.addListener(this.dlhandlerwrap) | |
}, | |
destroy() { | |
this.browser.downloads.onChanged.removeListener(this.dlhandlerwrap) | |
this.browser.downloads.onCreated.removeListener(this.dlhandlerwrap) | |
}, | |
async download(url, file) { | |
let id; | |
try { | |
id = await this.browser.downloads.download({ | |
filename: file, | |
url: url | |
}); | |
} catch (fail) { | |
fail.codeName = 'download-error' | |
throw fail; | |
} | |
return { | |
id: id, | |
url: url, | |
file: file | |
} | |
}, | |
getTitle(url) { | |
const path = url.pathname; | |
let title; | |
if (path.match(/index.php/)) { | |
title = url.searchParams.get("title"); | |
} else title = decodeURIComponent(url.pathname.slice(1)); | |
return title.replace(/[*"'.?/:]/g, c => "%" + c.charCodeAt().toString(16).toUpperCase()); | |
}, | |
async downloadFromAnchor(a) { | |
const url = new URL(a.href); | |
const title = this.getTitle(url); | |
const file = title + ".html"; | |
const cacheNode = a.nextElementSibling.querySelector('a[href^="https://webcache"]'); | |
if (!cacheNode) return 'no-cache'; | |
if (this.native) { | |
if (await this.fileExist(file)) return 'skip' | |
} | |
const cache = cacheNode.href; | |
return await this.download(cache, `${this.downloadPath}/${file}`); | |
}, | |
downloadCurrentHtml() { | |
function doctypeToString(node = document.doctype) { | |
if (!node) return '' | |
return '<!DOCTYPE ' + node.name | |
+ (node.publicId ? ` PUBLIC "${node.publicId}"` : '') | |
+ (!node.publicId && node.systemId ? ' SYSTEM' : '') | |
+ (node.systemId ? ` "${node.systemId}"` : '') | |
+ '>' + '\n' | |
} | |
function cleanCopy(root) { | |
const deep = true | |
const copy = root.cloneNode(deep) | |
copy.querySelectorAll('iframe[src ^= moz-extension') | |
.forEach(e => e.remove()) | |
fixRelativeUrl(copy) | |
fixEncode(copy) | |
return copy | |
} | |
function fixEncode(root) { | |
if (document.characterSet == 'UTF-8') return | |
const list = root.querySelectorAll( | |
'meta[http-equiv=content-type],' + | |
'meta[http-equiv=Content-Type],' + | |
'meta[charset]' | |
) | |
if (list.length == 0) { | |
if (!confirm('not UTF-8 and no charset tag found, add one?')) return | |
const encodeNode = document.createElement('meta') | |
encodeNode.setAttribute('charset', 'utf-8') | |
encodeNode.dataset.gholkOriginalCharset = '' | |
const head = root.querySelector('head') | |
if (head) head.prepend(encodeNode) | |
else root.prepend(encodeNode) | |
} | |
else { | |
list.forEach(encodeNode => { | |
if (encodeNode.hasAttribute('charset')) { | |
const original = encodeNode.getAttribute('charset') | |
encodeNode.dataset.gholkOriginalCharset = original | |
encodeNode.setAttribute('charset', 'utf-8') | |
} | |
else if (encodeNode.hasAttribute('http-equiv')) { | |
encodeNode.dataset.gholkOriginalContentType = encodeNode.content | |
encodeNode.content = 'text/html; charset=UTF-8' | |
} | |
else alert(`unknown error while fix encode node: ${encodeNode.outerHTML}`) | |
}) | |
} | |
} | |
function fixRelativeUrl(root) { | |
let base = root.querySelector('base') | |
if (base) { | |
const relative = base.getAttribute('href') | |
base.dataset.gholkOriginalHref = relative | |
base.setAttribute('href', base.href) | |
} | |
else { | |
base = document.createElement('base') | |
base.href = root.baseURI | |
base.dataset.gholkOriginalHref = '' | |
let head = root.querySelector('head') | |
if (head) head.prepend(base) | |
else root.prepend(base) | |
} | |
/* | |
copy.querySelectorAll('[href], [src]').forEach(e => { | |
const abs = /^\w+:\/\// | |
if ('href' in e && !abs.test(e.getAttribute('href'))) { | |
e.setAttribute('href', e.href) | |
} | |
else if ('src' in e && !abs.test(e.getAttribute('src'))) { | |
e.setAttribute('src', e.src) | |
} | |
}) | |
*/ | |
} | |
const html = doctypeToString() + cleanCopy(document.documentElement).outerHTML | |
const blob = new Blob([html], {type: 'text/html'}) | |
const download = document.createElement('a') | |
download.download = document.title + '.html' | |
download.href = URL.createObjectURL(blob) | |
document.body.appendChild(download) | |
download.click() | |
download.remove() | |
URL.revokeObjectURL(blob) | |
}, | |
async downloadFromPath(path) { | |
const title = path.slice(1) | |
const url = `https://wiki.komica.org/${title}` | |
const cache = 'http://webcache.googleusercontent.com/search?q=cache:' + url | |
this.cacheUrl = cache | |
const file = title.replace(/[*"'.?/:]/g, c => encodeURIComponent(c)) + '.html' | |
if (this.native && await this.fileExist(file)) return 'skip'; | |
return await this.download(cache, this.downloadPath + '/' + file) | |
}, | |
async fileExist(file) { | |
const ex = this.ex | |
const safe = await ex.shellescape(file) | |
let exist | |
if (this.os == 'windows') { | |
exist = await ex.exclaim_quiet(`if exist ..\\Downloads\\${this.downloadPath}\\${safe} echo exist`) | |
} | |
else exist = await ex.exclaim_quiet(`test -f $HOME/Downloads/${this.downloadPath}/${safe} && echo -n exist`); | |
return Boolean(exist) | |
}, | |
async queryIdList(list) { | |
const ql = list.map(id => this.browser.downloads.search({id})) | |
return (await Promise.all(ql)).flat() | |
}, | |
async dlax(method, n = 0) { | |
let l | |
let backupGoogleSearch = false | |
if (method == 'downloadFromPath') { | |
l = tri.state.komica_wiki_data | |
await this.init() | |
} | |
else if (method == 'downloadFromAnchor') { | |
l = $all('a>h3').map(h => h.parentNode) | |
} | |
else throw new Error('unknown method') | |
for (let i=n; i<l.length && !this.stop; true) { | |
console.log('line ' + i) | |
let errorDownload | |
let dl | |
try { | |
dl = await this[method](l[i]) | |
} | |
catch (error) { | |
if (error.codeName != 'download-error') throw error | |
errorDownload = error | |
} | |
if (dl == 'skip') { | |
console.log('skip') | |
i++ | |
continue | |
} | |
if (dl == 'no-cache') { | |
backupGoogleSearch = true | |
i++ | |
continue | |
} | |
let retry = false | |
while (true) { | |
const state = (await this.browser.downloads.search({id: dl.id}))[0] | |
if (state.state == 'complete') { | |
await this.ex.sleep(3000) | |
break | |
} | |
if (state.state == 'in_progress') { | |
await this.ex.sleep(500) | |
continue | |
} | |
const tabp = await this.tabopen(dl.url) | |
const tab = await tabp | |
const is404p = this.check404(tab.id) | |
const first = await Promise.race([ | |
is404p, | |
this.sleep(3).then(x => 'timeout') | |
]) | |
if (first == 'error-404') { | |
await this.browser.tabs.remove(tab.id) | |
backupGoogleSearch = true | |
} | |
if (first != 'timeout') break | |
retry = await this.tabEval( | |
`confirm('retry ${i}? (or skip)')` | |
) | |
if (!retry) { | |
try { | |
await this.browser.tabs.remove(tab.id) | |
} | |
catch (error) { | |
console.error(error) | |
console.log('tab is already closed') | |
} | |
} | |
break | |
} | |
if (!retry) i++ | |
} | |
if (backupGoogleSearch) { | |
this.downloadCurrentHtml() | |
await this.sleep(1) | |
} | |
if (method == 'downloadFromAnchor') { | |
if (confirm(`next?`)) $("#pnnext").click(); | |
} | |
}, | |
async dlap(n = 0) { | |
await this.init() | |
const l = tri.state.komica_wiki_data | |
for (let i=n; i<l.length && !this.stop; true) { | |
console.log('line ' + i) | |
let errorDownload | |
let dl | |
try { | |
dl = await this.downloadFromPath(l[i]) | |
} | |
catch (error) { | |
if (error.codeName != 'download-error') throw error | |
errorDownload = error | |
} | |
if (dl == 'skip') { | |
console.log('skip') | |
i++ | |
continue | |
} | |
let retry = false | |
while (true) { | |
const state = (await this.browser.downloads.search({id: dl.id}))[0] | |
if (state.state == 'complete') { | |
await this.ex.sleep(3000) | |
break | |
} | |
if (state.state == 'in_progress') { | |
await this.ex.sleep(500) | |
continue | |
} | |
const tabp = await this.tabopen(dl.url) | |
const tab = await tabp | |
const is404p = this.check404(tab.id) | |
const first = await Promise.race([ | |
is404p, | |
this.sleep(3).then(x => 'timeout') | |
]) | |
if (first == 'error-404') { | |
await this.browser.tabs.remove(tab.id) | |
} | |
if (first != 'timeout') break | |
retry = await this.tabEval( | |
`confirm('retry ${i}? (or skip)')` | |
) | |
if (!retry) { | |
try { | |
await this.browser.tabs.remove(tab.id) | |
} | |
catch (error) { | |
console.error(error) | |
console.log('tab is already closed') | |
} | |
} | |
break | |
} | |
if (!retry) i++ | |
} | |
}, | |
sleep(s) { | |
return new Promise(wake => setTimeout(wake, s*1000)) | |
}, | |
async check404(tabId) { | |
while (true) { | |
let tab | |
try { | |
tab = await this.browser.tabs.get(tabId) | |
} | |
catch (closed) { | |
return 'close' | |
} | |
if (tab.title.match(/^Error 404/)) return 'error-404' | |
await this.ex.sleep(200) | |
} | |
}, | |
async tabEval(code) { | |
if (tri.browserBg) return eval(code) | |
const resultList = await this.browser.tabs.executeScript( | |
this.tabId, | |
{code} | |
) | |
return resultList[0] | |
}, | |
async tabopen(url) { | |
return await this.browser.tabs.create({ | |
active: true, | |
url: url, | |
windowId: this.windowId | |
}) | |
}, | |
async dlan(list = $all("a>h3")) { | |
for (const h of list) { | |
await this.downloadFromAnchor(h.parentNode) && | |
await tri.excmds.sleep(5e3); | |
} | |
if (confirm("next?")) $("#pnnext").click(); | |
}, | |
Defer: function Defer() { | |
this.promise = new Promise((resolve, reject) => { | |
this.resolve = resolve | |
this.reject = reject | |
}) | |
}, | |
async readListFromClipboardToState(x) { | |
let l | |
if (!x) x = await this.ex.getclip('clipboard') | |
if (typeof x == 'string') l = x.split('\n') | |
else l = x | |
tri.state.komica_wiki_data = l | |
} | |
}; | |
// eb tri.downloadJob.stop = true |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment