// ==UserScript== // @name 快看漫画一键保存 // @namespace http://tampermonkey.net/ // @version 1.0 // @description 单击保存即可下载漫画 // @author You // @icon https://www.kuaikanmanhua.com/favicon.ico // @grant none // @license MIT // @include https://www.kuaikanmanhua.com/web/comic/* // @downloadURL none // ==/UserScript== (function () { 'use strict'; async function AsyncErgodic( data, callBack ) { const ps = new Set() if ('forEach' in data) { data.forEach((a, b, c) => { ps.add(callBack(a, b, c)) }) } else { for (const k in data) { ps.add(callBack(data[k], k, data)) } } return await Promise.all(ps) } let count = 0 function run() { const el = document.querySelector('.bodyContent') if (!el) { count++ if (count >= 100) { console.log(`多次重启失败`) return } setTimeout(run, 50) console.log(`第${count}次尝试重启`) return } // 下载进度条 el.insertAdjacentHTML('afterbegin', `
正在准备
`) document.body.insertAdjacentHTML('afterbegin', ``) // 顶部下载 document.querySelector('.titleBox>.tab>div').insertAdjacentHTML('beforebegin', `
下载
`) const fc_sop = document.querySelector('.fc_sop') /**@type {HTMLDivElement} */ const fc_sop_line_l = document.querySelector('.fc_sop_line_l') const fc_sop_info = document.querySelector('.fc_sop_info') function cancel() { fc_sop.classList.remove('fc_show') fc_sop_line_l.style.width = '0%' fc_sop_info.innerText = '正在准备' } // 防重锁 let lock = false function downloadCvs(cvs, ext) { console.log(`download canvas`) let a = document.createElement('a') a.href = cvs.toDataURL('image/png') a.download = document.title.replace('漫画全集在线观看-快看', '') + ext + '.png' a.click() cancel() lock = false a.remove() } function drawCvs(imgs, width, height) { console.log(`draw canvas`) const cvs = document.createElement('canvas') cvs.id = 'fc_cvs' // document.querySelector('.bodyContent').append(cvs) cvs.width = width cvs.height = height let th = 0 const ctx = cvs.getContext('2d') for (let i = 0; i < imgs.length; i++) { const e = imgs[i] th += e.height ctx.drawImage(e, 0, th) } return cvs } async function download() { if (lock) return lock = true fc_sop.classList.add('fc_show') const imgEles = [...document.querySelectorAll('.imgList .img-box .img[data-src]')] if (imgEles.length === 0) return console.log('空元素异常') console.log(`准备下载${imgEles.length}张图片`) const imgUrls = imgEles.map(e => e.getAttribute('data-src')) /** @type {HTMLImageElement[]} */ const imgObjs = await AsyncErgodic(imgUrls, (e, i) => new Promise(n => { const el = document.createElement('img') el.onload = () => { const b = (i + 1) / imgUrls.length * 100 fc_sop_line_l.style.width = b + '%' n(el) } el.src = e el.setAttribute("crossOrigin", 'Anonymous') })) const width = imgObjs[0].width const height = imgObjs.reduce((v, e) => v + e.height, 0) console.log(`图片尺寸${width}×${height}`) // 分组,保证每组图片高度不超过32000 const groups = [{height: 0, items: []}] for (let i = 0; i < imgObjs.length; i++) { const e = imgObjs[i] const pi = groups[groups.length - 1] if (pi.height + e.height > 32000) { groups.push({ height: e.height, items: [e] }) continue } pi.height += e.height pi.items.push(e) } console.log(groups) if (groups.length === 1) { const cvs = drawCvs(groups[0].items, width, groups[0].height) downloadCvs(cvs) } else { if (height > 30000) { const info = `图片高度超过限制,将拆分为${groups.length}个文件` console.log(info) fc_sop_info.innerText = info } groups.forEach((e, i) => { const cvs = drawCvs(e.items, width, e.height) downloadCvs(cvs, '_' + i) }) } } document.querySelectorAll('.fc_dl').forEach(e => e.addEventListener('click', download)) } setTimeout(run, 20) })();