daum漫画图片下载

一键下载Daum的整话漫画

目前為 2020-11-28 提交的版本,檢視 最新版本

// ==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
// ==/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<data.length;i++){
                        // 下载单幅图片
                        let item = data[i];
                        downloadSinglePic(item.url, item.imageOrder);
                    }
                }else{
                    console.log(`ID为${eposideId}的这一话下载失败`);
                }
            }else{ console.log(`ID为${eposideId}的这一话下载失败`); }
        },
        onerror : function(err){ console.log(`ID为${eposideId}的这一话下载失败,原因为${err}`);  }
    });
}

// 下载单幅图片
function downloadSinglePic(picUrl, picNum){
    /*
    来自tamperMonkey的官方文档,参数说明
    url - the URL from where the data should be downloaded (required)
    name - the filename - for security reasons the file extension needs to be whitelisted at Tampermonkey's options page (required)
    headers - see GM_xmlhttpRequest for more details
    saveAs - boolean value, show a saveAs dialog
    onerror callback to be executed if this download ended up with an error
    onload callback to be executed if this download finished
    onprogress callback to be executed if this download made some progress
    ontimeout callback to be executed if this download failed due to a timeout
    */

    // 下载失败、超时的每一张图片单独提醒用户
    // 下载成功不打开“另存为”窗口
    GM_download({
        url:picUrl,
        name: `${picNum<10? '0':''}${picNum}.jpg`,
        saveAs:false,
        onload:()=>{
            // 每下载成功一张图片减一,减到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);    //添加到网页中
}

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址