Download Images From Newtoki
// ==UserScript== // @name NewtokiRipper // @namespace adrian // @author adrian // @match https://newtoki468.com/webtoon/* // @include /^https:\/\/newtoki[0-9]+\.com\/webtoon/.*$/ // @version 1.5 // @description Download Images From Newtoki // @require https://cdn.jsdelivr.net/npm/@violentmonkey/shortcut@1 // @require https://unpkg.com/@zip.js/[email protected]/dist/zip-full.min.js // @grant GM_registerMenuCommand // @license MIT // ==/UserScript== // helpers const fetchImage = async (url) => { const blob = await fetch(url, { headers: { accept: "image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8", "accept-language": "en-US,en;q=0.9", "sec-ch-ua": '"Chromium";v="135", "Not-A.Brand";v="8"', "sec-ch-ua-mobile": "?0", "sec-ch-ua-platform": '"macOS"', "sec-fetch-dest": "image", "sec-fetch-mode": "no-cors", "sec-fetch-site": "cross-site", "sec-fetch-storage-access": "active", }, referrer: "https://newtoki469.com/", referrerPolicy: "strict-origin-when-cross-origin", body: null, method: "GET", mode: "cors", credentials: "omit", }).then((r) => r.blob()); return blob; }; function getImages() { return [...document.getElementsByTagName("img")].flatMap((img) => { const attr = [...img.attributes].find((a) => /^data-[a-zA-Z0-9]{1,20}/.test(a.name), ); const src = attr?.value; return src?.startsWith("https://img") && src?.includes("newtoki") ? [src] : []; }); } // UI const UI_ID = Array.from( { length: 12 }, () => "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"[ Math.floor(Math.random() * 52) ], ).join(""); const css = ` @import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;700&display=swap'); #${UI_ID}-btn { position: fixed; right: 16px; bottom: 16px; z-index: 999999; padding: 8px 14px; background: #e00; color: #fff; font: 700 13px/1 'JetBrains Mono', monospace; letter-spacing: 0.05em; border: none; border-radius: 4px; box-shadow: 0 2px 0 #900, 0 4px 12px rgba(220,0,0,.3); cursor: pointer; transition: box-shadow .15s, transform .1s, background .15s; } #${UI_ID}-btn:hover { background: #cc0000; box-shadow: 0 2px 0 #900, 0 6px 18px rgba(220,0,0,.45); } #${UI_ID}-btn:active { transform: translateY(2px); box-shadow: 0 0 0 #900, 0 2px 8px rgba(220,0,0,.3); } #${UI_ID}-btn:disabled { background: #2a2a2a; color: #555; box-shadow: 0 2px 0 #111; cursor: not-allowed; transform: none; } #${UI_ID}-toast { position: fixed; left: 50%; bottom: 60px; transform: translateX(-50%); z-index: 999999; min-width: 260px; max-width: 380px; background: rgba(10,10,10,.97); color: #e0e0e0; font-family: 'JetBrains Mono', monospace; padding: 14px 16px; border-radius: 4px; border: 1px solid #2a2a2a; border-left: 2px solid #e00; box-shadow: 0 8px 32px rgba(0,0,0,.6); pointer-events: none; opacity: 0; transition: opacity .2s; } #${UI_ID}-toast.visible { opacity: 1; } #${UI_ID}-toast-title { font-size: 13px; font-weight: 700; color: #fff; margin-bottom: 4px; } #${UI_ID}-toast-title::before { content: '> '; color: #e00; } #${UI_ID}-toast-msg { font-size: 12px; font-weight: 400; color: #666; padding-left: 14px; } #${UI_ID}-toast-bar-wrap { background: #1a1a1a; border-radius: 2px; height: 3px; margin-top: 10px; overflow: hidden; display: none; } #${UI_ID}-toast-bar { height: 100%; width: 0%; background: #e00; border-radius: 2px; transition: width .2s; box-shadow: 0 0 6px rgba(220,0,0,.6); } `; function injectStyles() { const style = document.createElement("style"); style.textContent = css; document.head.appendChild(style); } function createUI() { const btn = document.createElement("button"); btn.id = `${UI_ID}-btn`; btn.textContent = "⬇ Download"; btn.addEventListener("click", downloadImages); document.body.appendChild(btn); const toast = document.createElement("div"); toast.id = `${UI_ID}-toast`; toast.innerHTML = ` <div id="${UI_ID}-toast-title"></div> <div id="${UI_ID}-toast-msg"></div> <div id="${UI_ID}-toast-bar-wrap"><div id="${UI_ID}-toast-bar"></div></div> `; document.body.appendChild(toast); } let toastTimeout; const ui = { get btn() { return document.getElementById(`${UI_ID}-btn`); }, get toast() { return document.getElementById(`${UI_ID}-toast`); }, get title() { return document.getElementById(`${UI_ID}-toast-title`); }, get msg() { return document.getElementById(`${UI_ID}-toast-msg`); }, get barWrap() { return document.getElementById(`${UI_ID}-toast-bar-wrap`); }, get bar() { return document.getElementById(`${UI_ID}-toast-bar`); }, show(title, msg = "", { progress = null, autohide = 0 } = {}) { clearTimeout(toastTimeout); this.title.textContent = title; this.msg.textContent = msg; if (progress !== null) { this.barWrap.style.display = "block"; this.bar.style.width = `${progress}%`; } else { this.barWrap.style.display = "none"; } this.toast.classList.add("visible"); if (autohide) toastTimeout = setTimeout(() => this.hide(), autohide); }, hide() { this.toast.classList.remove("visible"); }, setProgress(done, total) { this.show(`Downloading… ${done}/${total}`, "", { progress: (done / total) * 100, }); }, setBusy(busy) { this.btn.disabled = busy; this.btn.textContent = busy ? "Downloading…" : "⬇ Download"; }, }; // main shit const downloadImages = async () => { ui.setBusy(true); ui.show("Starting…"); try { const images = getImages(); if (!images.length) { ui.show("Nothing to download", "No images found.", { autohide: 5000 }); return; } ui.show(`Found ${images.length} images`, "Starting download…", { progress: 0, }); const zipWriter = new zip.ZipWriter(new zip.BlobWriter("application/zip"), { bufferedWrite: true, }); let done = 0; await Promise.all( images.map(async (url, i) => { const blob = await fetchImage(url); await zipWriter.add(`${i + 1}.jpg`, new zip.BlobReader(blob)); ui.setProgress(++done, images.length); }), ); ui.show("Generating zip…", "", { progress: 100 }); const blobURL = URL.createObjectURL(await zipWriter.close()); const link = document.createElement("a"); link.href = blobURL; link.download = `${document.title}.zip`; link.click(); ui.show("Done!", `${images.length} images ripped.`, { autohide: 5000 }); } catch (e) { console.error(e); ui.show("Error", e.message, { autohide: 5000 }); } finally { ui.setBusy(false); } }; // init shit injectStyles(); createUI(); GM_registerMenuCommand("Download Images (Ctrl/Cmd + S)", downloadImages); VM.shortcut.register("cm-s", downloadImages); VM.shortcut.enable();