`, ); } let base = url; const matchVendors = html.match(/src="(.*?\/)vendors(.*?)js"/); console.log(matchVendors); if (matchVendors) { base = matchVendors[1]; } html = html.replace('
', `
`); const iframe = document.createElement('iframe'); iframe.srcdoc = html; iframe.style.display = 'none'; document.body.appendChild(iframe); return new Promise((resolve) => { iframe.onload = () => { resolve({ iframe, base }); }; }); } async function extract(url) { btn.innerText = 'Fetching Page...'; const { iframe: frame, base } = await loadPageInIframe(url); frame.contentWindow.regeneratorRuntime = regeneratorRuntime; btn.innerText = 'Extracting Data...'; let modules = {}; if (frame.contentWindow.cachedModules) { modules = { ...frame.contentWindow.loadedModules, }; for (const i of frame.contentWindow.cachedModules) { modules = { ...modules, ...i[1], }; } } else { const webpackJsonp = frame.contentWindow.webpackJsonp; console.log('found WebpackJsonp', webpackJsonp); const vendors = webpackJsonp.find((e) => e[0].includes('vendors')); if (!vendors) { btn.innerText = 'Load vendors.js faild!'; return; } const Index = webpackJsonp.find((e) => e[0].includes('index')); if (!Index) { btn.innerText = 'Load index.js faild!'; return; } const Runtime = webpackJsonp.find((e) => e[0].includes('runtime')); modules = { ...vendors[1], ...Index[1], ...(Runtime ? Runtime[1] : {}) }; } const spineres = extractSpine(modules, new URL('.', base).toString(), frame.contentWindow); console.log('Got Spine Data', spineres); const staticres = extractStaticFiles(modules, new URL('.', base).toString()); console.log('Got Static Files', staticres); btn.innerText = 'Preparing resources...'; const fn = (url.match(/event\/(.*?)\//) || ['', ''])[1].split('-')[0] || Date.now().toString(); const fileStream = streamSaver.createWriteStream(fn + '.zip'); const readableZipStream = new ZIP({ async start(ctrl) { btn.innerText = 'Download started...'; const savedIds = []; // save spine json & atlas for (const i of Object.keys(spineres.SPINE_MANIFEST)) { const dir = spineres.SPINE_MANIFEST[i].module || ''; const atlas = new File([spineres.SPINE_MANIFEST[i].atlas], dir + '/' + i + '.atlas', { type: 'text/plain', }); ctrl.enqueue(atlas); const j = spineres.SPINE_MANIFEST[i].json; if (typeof j === 'string' && j.indexOf('http') === 0) { savedIds.push(j); ctrl.enqueue(await fetchToZip(dir + '/' + i + '.json', j)); } else { const json = new File([JSON.stringify(j, null, 4)], dir + '/' + i + '.json', { type: 'application/json', }); ctrl.enqueue(json); } } // save images const promises = Object.values(spineres.MAIN_MANIFEST).map((e) => { const dir = e.module || ''; const fn = dir + '/' + e.id + '.' + extname(e.src); savedIds.push(e.src); return fetchToZip(fn, e.src).then((res) => ctrl.enqueue(res)); }); // save other static let otherlen = 0; const staticPromises = staticres.map((e) => { //skip things in savedIds if (savedIds.includes(e.src)) { return Promise.resolve(); } const dir = '_other_resources'; const fn = dir + '/' + e.id + '.' + extname(e.src); savedIds.push(e.src); otherlen++; return fetchToZip(fn, e.src).then((res) => ctrl.enqueue(res)); }); desc.innerText = `Extracted ${Object.keys(spineres.SPINE_MANIFEST).length} spine(s), ` + `${Object.keys(spineres.MAIN_MANIFEST).length} render-related image(s), ` + `${otherlen} other resource(s)`; await Promise.all(promises.concat(staticPromises)); ctrl.close(); }, }); if (window.WritableStream && readableZipStream.pipeTo) { await readableZipStream.pipeTo(fileStream); btn.innerText = 'Done'; } else { btn.innerText = 'FileWriter Unsupported!'; } } async function clk() { btn.disabled = true; try { await extract(url.value); } catch (e) { console.error(e); btn.innerText = 'Error!'; } btn.disabled = false; } const href = location.href.replace(location.search, '').replace(location.hash, ''); if (href.endsWith('.html') && href.includes('event/')) { url.value = href .replace(location.origin, 'https://webstatic.mihoyo.com') .replace('/webstatic-extractor/', '/'); clk(); }