// ==UserScript== // @name daum漫画图片下载 // @namespace http://weibo.com/liangxiafengge/ // @version 0.3 // @description 一键下载Daum的整话漫画 // @author CW2012 // @icon http://s1.daumcdn.net/photo-section/-cartoon10/favicon/201312/favicon.ico // @match http*://webtoon.daum.net/webtoon/view/* // @match http*://webtoon.daum.net/webtoon/viewer/* // @connect http*://*.daum.net // @grant GM_xmlhttpRequest // @grant GM_download // @run-at document-end // @downloadURL none // ==/UserScript== let picCount = 0; // 下载一话时,这一话的图片数目 (function() { 'use strict'; let tmpStr = location.href.split('\/'); if(tmpStr[4]=='view'){ // 漫画列表页 downloadFromEposideList(); }else if(tmpStr[4]=='viewer'){ // 漫画阅读页 downloadFromRead(); } })(); function downloadFromEposideList(){ // 考虑到页面加载数据还需要一段时间,如果需要依附的元素还没生成,下面的添加按钮的动作将无法执行 if(document.querySelectorAll('.clear_g.list_update>li').length == 0){ // 这里上列表还没加载进来时的逻辑 // 奇怪,就算不请求任何数据,这个元素也是存在的,为什么没有触发元素改变的事件??? let eposideList = document.querySelector('.clear_g.list_update'); // 监听eposideList元素,如果数据加载进来了,它的li子元素的个数会大于0 // 监听子元素变化需要开启childList,attributes是监听本元素的变化,如class变化等 new MutationObserver(eposideListChanged).observe(eposideList, { attributes: false, childList: true, subtree: false }); return; } } // 发生数据加载时,添加按钮 function eposideListChanged(){ // 等待一秒钟,让元素全部添加到网页上后,再添加我们的按钮 setTimeout(()=>{ let smallPicList= document.querySelectorAll('.clear_g.list_update>li'); if(smallPicList.length == 0){ return; } // 按钮应该出现的位置:缩略图所在的元素 smallPicList.forEach((item,index)=>{ let btnParent = item.lastElementChild; btnParent.style.display='flex'; btnParent.style.diapley = 'flex'; btnParent.style.justifyContent = 'space-between'; // 分割线 let line = document.createElement('span'); line.className = 'ico_comm ico_bar'; btnParent.appendChild(line); // 问题来了,收费的篇章是不能下载的,那么,不如收费篇章就不要添加下载按钮好了 if(btnParent.children.length>2){ return; } // 创建一个“下载”锚点(a),并美化亿下 let btn = document.createElement('a'); btn.innerText = '下载这一话'; btn.style.background ='#e83d3d'; //使用和原网页一样的配色,没有突兀感 btn.style.color = '#fff'; btn.style.padding = '3px'; btn.style.borderRadius = '3px'; btn.style.textDecoration='none';// 不显示下划线 btn.style.cursor = 'pointer'; // 鼠标悬浮时,指针变成手形,让用户知道可以点击 btn.addEventListener('click', e=>{ // 获得这一话的ID,并根据ID下载 downloadEposide(item.firstElementChild.href.split('/')[5]); }); btnParent.appendChild(btn); //添加到网页中 }); },1000); } // 根据这一话的ID,下载一整话 function downloadEposide(eposideId){ // 一话的链接,请求API: http://webtoon.daum.net/data/pc/webtoon/viewer_images/eposideId let url = `http://webtoon.daum.net/data/pc/webtoon/viewer_images/${eposideId}`; GM_xmlhttpRequest({ method: "GET", url: url, onload: function(res){ if(res.status === 200){ // 分析所得数据,并下载图片 let response = JSON.parse(res.responseText); // 这里返加的是文本,需要转换成JSON对象才能用 if(parseInt(response.result.status) == 200){ let data = response.data; // data虽然是Array,但是不能用foreach?? picCount = data.length; for(let i=0;i{ // 每下载成功一张图片减一,减到0时,表示这一话已经全部下载完成 if(--picCount<1){ // 但是alert弹窗的样式非常得不银杏化,因此我们来美化亿下 toast(1, '这一话的所有图片全部处理完成') } }, onerror: ()=>{ toast(-1, `下载第${picNum}幅图片时出错,请按Ctrl+shift+I打开开发者工具自行下载`) console.log(`下载第${picNum}幅图片时出错,它的下载链接是:\n${picUrl}`); }, ontimeout: ()=>{ toast(-1,`下载第${picNum}幅图片时超时,请按Ctrl+shift+I打开开发者工具自行下载`) console.log(`下载第${picNum}幅图片时超时,它的下载链接是:\n${picUrl}`); } }); } // 显示自定义的弹窗 // type 消息类型:1成功,0提示,-1错误 // msg消息体 const colors = ['rgb(160 42 42)','rgb(8 99 3 / 73%)','rgba(70 117 158 / 73%)']; function toast(type, msg){ let toastBox = document.createElement('div'); toastBox.style.position = 'fixed'; toastBox.style.background='white'; toastBox.style.borderRadius='10px'; toastBox.style.background= colors[type+1]; toastBox.style.boxShadow='rgb(25 25 25) 1px 1px 10px 1px'; toastBox.innerText=msg; toastBox.style.color = '#fff'; toastBox.style.bottom='12vh'; // 显示的位置不能离底部太低了,会被其他元素遮挡 toastBox.style.left='2vh'; toastBox.style.transition='1.5s'; toastBox.style.padding = '10px'; document.body.appendChild(toastBox); // 6秒后自动隐藏 setTimeout(()=>{ toastBox.style.opacity = '0'; delete(toastBox); }, 6000); } // 在阅读页上添加下载的功能 function downloadFromRead(){ // 先添加一个悬浮按钮??? // 创建一个“下载”锚点(a),并美化亿下 let btn = document.createElement('a'); btn.innerText = '下载这一话'; btn.style.position = 'fixed'; btn.style.left = '20px'; btn.style.top = '45vh'; btn.style.background ='#e83d3d'; //使用和原网页一样的配色,没有突兀感 btn.style.color = '#fff'; btn.style.padding = '13px'; btn.style.fontSize= '20px'; btn.style.boxShadow='gray 1px 1px 35px 0'; btn.style.borderRadius = '13px'; btn.style.textDecoration='none';// 不显示下划线 btn.style.cursor = 'pointer'; // 鼠标悬浮时,指针变成手形,让用户知道可以点击 btn.addEventListener('click', e=>{ // 获得这一话的ID,并根据ID下载,跟上面的逻辑是一模一样的 let eposideId = location.href.split('/')[5]; downloadEposide(eposideId); }); document.body.appendChild(btn); //添加到网页中 }