您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
下载comic-walker网站的免费漫画
当前为
// ==UserScript== // @name comic-walker 漫画下载 // @namespace shadows // @version 0.1.5 // @description 下载comic-walker网站的免费漫画 // @author shadows // @license MIT License // @copyright Copyright (c) 2021 shadows // @icon https://dimg04.c-ctrip.com/images/0391j120008r0n8a84D94.png // @icon64 https://static.yximgs.com/bs2/adInnovationResource/367c797d005b4b1ab180f0a361a7ef43.png // @match https://comic-walker.com/contents/detail/* // @require https://gcore.jsdelivr.net/npm/[email protected]/dist/jszip.min.js // @require https://gcore.jsdelivr.net/npm/[email protected]/dist/FileSaver.min.js // ==/UserScript== "use strict"; addButton() function addButton() { let targets = document.querySelectorAll(".acBacknumber-item-leftbox"); console.log(targets); for (let elem of targets) { if (elem.parentNode.querySelector(".download-button")) continue; let name = elem.querySelector(".acBacknumber-title").textContent; let subtitle = elem.querySelector(".acBacknumber-subtitle").textContent if ( subtitle != "") name += ( " " + subtitle ); let href = new URL(elem.parentNode.querySelector('a').href); let params = href.searchParams; let cid = params.get('cid'); elem.after(creatButton(name, cid)); } } function creatButton(name, cid) { let button = document.createElement('button'); button.classList.add("download-button"); button.textContent = "Download"; button.style.cssText = `z-index: 2; background: linear-gradient(135deg, #6e8efb, #a777e3); color: white; padding: 3px 3px; margin: 4px 0px; text-align: center; border-radius: 3px; border-width: 0px;`; button.dataset.cid = cid; button.dataset.name = name; button.onclick = download; return button; } async function download(event) { event.stopPropagation(); event.preventDefault(); let elem = event.target; let name = elem.dataset.name; let cid = elem.dataset.cid; let imagesData = await getImageSData(cid); let images = await downloadImages(imagesData, name); let zip = new JSZip(); let targetLength = images.length.toString().length; for (let image of images) { let decrypted = decryptImage(image); zip.file(`${image.id.toString().padStart(targetLength,'0')}.jpg`, decrypted); } zip.generateAsync({ type: "blob", base64: true }).then(content => saveAs(content, `${name}.zip`)); } async function getImageSData(cid) { return fetch(`https://comicwalker-api.nicomanga.jp/api/v1/comicwalker/episodes/${cid}/frames`) .then(resp => resp.json()) .then(json => { let dataArray = json.data.result; return dataArray.map((item, index) => ({ drm_hash: item.meta.drm_hash, source_url: item.meta.source_url, id: index + 1 })) }); } function downloadImages(imagesData, name) { async function downloadSingleImage(item) { return fetch(item.source_url).then(resp => resp.arrayBuffer()).then(arrayBuffer => { console.log(`${name}-${item.id} have downloaded.`); //返回包含id drm_hash与图片数据的对象 return { id: item.id, drm_hash: item.drm_hash, content: arrayBuffer }; }); } let images = asyncPool(10, imagesData, downloadSingleImage); return images; } /** * 解密图片 * @param {Object} image * @param {String} image.drm_hash 解密密钥 * @param {ArrayBuffer} image.content 图片数据 * @returns {Blob} 已解密的图片Blob对象 */ function decryptImage({ drm_hash, content }) { if (drm_hash == null) return new Blob([content], { type: "image/jpeg" }); let key = generateKey(drm_hash); let contentUint8Array = arrayBufferToUint8(content); let decryptedUin8Array = xor(contentUint8Array, key); return new Blob([decryptedUin8Array], { type: "image/jpeg" }); } function generateKey(drm_hash) { drm_hash = drm_hash.slice(0, 16).match(/[\da-f]{2}/gi); return new Uint8Array(drm_hash.map(i => parseInt(i, 16))); } /** * xor解密 * @param {Uint8Array} content 待解密的内容 * @param {Uint8Array} key 密钥 * @returns Uint8Array 结果 */ function xor(content, key) { let result = new Uint8Array(content.length); let keyLength = key.length; for (let i = 0; i < content.length; i++) { result[i] = content[i] ^ key[i % keyLength]; } return result; } function arrayBufferToUint8(arrayBuffer) { return new Uint8Array(arrayBuffer); } /** * @param poolLimit 并发控制数 (>= 1) * @param array 参数数组 * @param iteratorFn 异步任务,返回 promise 或是 async 方法 * https://www.luanzhuxian.com/post/60c2c548.html */ function asyncPool(poolLimit, array, iteratorFn) { let i = 0 const ret = [] // Promise.all(ret) 的数组 const executing = [] const enqueue = function() { // array 遍历完,进入 Promise.all 流程 if (i === array.length) { return Promise.resolve() } // 每调用一次 enqueue,就初始化一个 promise,并放入 ret 队列 const item = array[i++] const p = Promise.resolve().then(() => iteratorFn(item, array)) ret.push(p) // 插入 executing 队列,即正在执行的 promise 队列,并且 promise 执行完毕后,会从 executing 队列中移除 const e = p.then(() => executing.splice(executing.indexOf(e), 1)) executing.push(e) // 每当 executing 数组中 promise 数量达到 poolLimit 时,就利用 Promise.race 控制并发数,完成的 promise 会从 executing 队列中移除,并触发 Promise.race 也就是 r 的回调,继续递归调用 enqueue,继续 加入新的 promise 任务至 executing 队列 let r = Promise.resolve() if (executing.length >= poolLimit) { r = Promise.race(executing) } // 递归,链式调用,直到遍历完 array return r.then(() => enqueue()) } return enqueue().then(() => Promise.all(ret)) }
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址