Greasy Fork is available in English.

豆瓣图书资源全网免费搜索下载助手:一键搞定搞定豆瓣图书 PDF 下载【失效联系作者24小时更新】

安装脚本以后,一键下载豆瓣书籍 PDF

このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください。
// ==UserScript==
// @name           豆瓣图书资源全网免费搜索下载助手:一键搞定搞定豆瓣图书 PDF 下载【失效联系作者24小时更新】
// @description    安装脚本以后,一键下载豆瓣书籍 PDF
// @author         蜡小新
// @contributor    蜡小新
// @connect        *
// @grant          GM_xmlhttpRequest
// @grant          GM_setClipboard
// @grant          GM_addStyle
// @grant          GM_setValue
// @grant          GM_getValue
// @grant          GM_listValues
// @grant          GM_deleteValue
// @grant          GM_registerMenuCommand
// @require        https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js
// @require        https://greasyfork.org/scripts/368137-encodeToGb2312/code/encodeToGb2312.js?version=601683
// @match        https://book.douban.com/*
// @version        1.0.1
// @icon           https://img3.doubanio.com/favicon.ico
// @run-at         document-end
// @namespace      doveboy_js
// @license        MIT
// ==/UserScript==

/* global $, jQuery, encodeToGb2312 */

// This Userscirpt can't run under Greasemonkey 4.x platform
if (typeof GM_xmlhttpRequest === "undefined") {
    alert("不支持Greasemonkey 4.x,请换用暴力猴或Tampermonkey");
    return;
}


var seBwhA = document.createElement("a");
seBwhA.id = "seBwhA";
document.getElementsByTagName("html")[0].appendChild(seBwhA);

GM_addStyle(`
.c-aside{margin-bottom:30px}.c-aside-body a{color: #3377aa;margin-right: 5px;}.c-aside-body a:link,.c-aside-body a:visited{color: #3377aa;}.c-aside-body a:active,.c-aside-body a:hover{background-color: #3377aa;color: white;}.c-aside-body a.available{color:#3377aa}.c-aside-body a.available:active,.c-aside-body a.available:hover{background-color:#3cc}.c-aside-body a.sites_r0{text-decoration:line-through}
#c_dialog li{margin:10px}#c_dialog{text-align:center}
#interest_sectl .rating_imdb{border-top:1px solid #eaeaea;border-bottom:1px solid #eaeaea;padding-bottom:0}#interest_sectl .rating_wrap{padding-top:15px}#interest_sectl .rating_more{border-bottom:1px solid #eaeaea;color:#9b9b9b;margin:0;padding:15px 0;position:relative}#interest_sectl .rating_more a{left:80px;position:absolute}#interest_sectl .rating_more .titleOverviewSprite{background:url(https://coding.net/u/Changhw/p/MyDoubanMovieHelper/git/raw/master/title_overview_sprite.png) no-repeat;display:inline-block;vertical-align:middle}#interest_sectl .rating_more .popularityImageUp{background-position:-14px -478px;height:8px;width:8px}#interest_sectl .rating_more .popularityImageDown{background-position:-34px -478px;height:8px;width:8px}#interest_sectl .rating_more .popularityUpOrFlat{color:#83c40b}#interest_sectl .rating_more .popularityDown{color:#930e02}
.more{display:block;height:34px;line-height:34px;text-align:center;font-size:14px;background:#f7f7f7}
div#drdm_setting input[type=checkbox]{display:none}div#drdm_setting input[type=checkbox]+label{display:inline-block;width:40px;height:20px;position:relative;transition:.3s;margin:0 20px;box-sizing:border-box;background:#ddd;border-radius:20px;box-shadow:1px 1px 3px #aaa}div#drdm_setting input[type=checkbox]+label:after,div#drdm_setting input[type=checkbox]+label:before{content:'';display:block;position:absolute;left:0;top:0;width:20px;height:20px;transition:.3s;cursor:pointer}div#drdm_setting input[type=checkbox]+label:after{background:#fff;border-radius:50%;box-shadow:1px 1px 3px #aaa}div#drdm_setting input[type=checkbox]:checked+label{background:#aedcae}div#drdm_setting input[type=checkbox]:checked+label:after{background:#5cb85c;left:calc(100% - 20px)}
.top250{background:url(https://s.doubanio.com/f/movie/f8a7b5e23d00edee6b42c6424989ce6683aa2fff/pics/movie/top250_bg.png) no-repeat;width:150px;margin-right:5px;font:12px Helvetica,Arial,sans-serif;margin:5px 0;color:#744900}.top250 span{display:inline-block;text-align:center;height:18px;line-height:18px}.top250 a,.top250 a:active,.top250 a:hover,.top250 a:link,.top250 a:visited{color:#744900;text-decoration:none;background:0}.top250-no{width:34%}.top250-link{width:66%}
.drdm-dl-horizontal dt{float:left;width:160px;overflow:hidden;clear:left;text-align:right;text-overflow:ellipsis;white-space:nowrap}.drdm-dl-horizontal dd{margin-left:180px}
`);

if (GM_getValue('enable_extra_stylesheet', true)) {
    GM_addStyle('#dale_movie_chart_top_right,#dale_movie_home_bottom_right,#dale_movie_home_bottom_right_down,#dale_movie_home_download_bottom,#dale_movie_home_inner_bottom,#dale_movie_home_side_top,#dale_movie_home_top_right,#dale_movie_subject_bottom_super_banner,#dale_movie_subject_download_middle,#dale_movie_subject_inner_middle,#dale_movie_subject_middle_right,#dale_movie_subject_top_midle,#dale_movie_subject_top_right,#dale_movie_tags_top_right,#dale_movie_towhome_explore_right,#dale_review_best_top_right,#footer,#movie_home_left_bottom,.extra,.mobile-app-entrance.block5.app-movie,.qrcode-app,.top-nav-doubanapp,div.gray_ad,div.ticket{display:none}');
}

function page_parser(responseText) {
    return (new DOMParser()).parseFromString(responseText, 'text/html');
}

// 缓存相关方法
function CacheStorage(name, expire = null) {
    let now = Date.now();
    let cache_name = "drdm_cache_" + (name ? name : 'default');

    if (localStorage[cache_name + "_exp"]) {
        if (now > localStorage[cache_name + "_exp"]) {
            localStorage.removeItem(cache_name);
        }
    }

    let cache = localStorage[cache_name] ? JSON.parse(localStorage[cache_name]) : {};
    localStorage.setItem(cache_name + "_exp", now + expire);

    return {
        flush: function () {
            localStorage.setItem(cache_name, JSON.stringify(cache));
        },

        add: function (name, value) {
            cache[name] = value;
            this.flush();
        },

        del: function (name) {
            if (name) {
                delete cache[name];
                this.flush;
            } else {
                localStorage.removeItem(cache_name);
            }
        },

        get: function (name, def = null) {
            if (name) {
                return cache[name] ? cache[name] : def;
            } else {
                return cache;
            }
        }
    }
}

function clearExpiredCacheValue(force) {
    let StorageKey = [];
    for (let i = 0, len = localStorage.length; i < len; ++i) { // 先从里面取出所有的key
        StorageKey.push(localStorage.key(i));
    }

    let CacheKey = StorageKey.filter(function (x) {
        return x && x.match(/(drdm_cache_.+)_exp/);
    }); // 再从中提取出本脚本缓存的键值

    for (let i = 0,len = CacheKey.length ; i < len ; ++i) {
        let key_name = CacheKey[i];
        let exp_at = localStorage.getItem(key_name);
        if (force || exp_at < Date.now()) {
            localStorage.removeItem(key_name);
            localStorage.removeItem(key_name.slice(0,-4)); // 移除 _exp 后缀
        }
    }
}

let delete_site_prefix = "delete_site_";

let fetch_anchor = function (anchor) {
    return anchor[0].nextSibling.nodeValue.trim();
};

$(document).ready(function () {
    let douban_link, douban_id;

    douban_link = 'https://' + location.href.match(/douban.com\/subject\/\d+\//); //豆瓣链接
    douban_id = location.href.match(/(\d{7,8})/g);

    let site_map = [];

    if (douban_id) {
        clearExpiredCacheValue(false); // 清理缓存
        let cache = CacheStorage(douban_id, 86400 * 7 * 1e3);
        let need_login_cache = CacheStorage("need_login", 86400 * 1e3);
        $("#content div.aside").prepend(`<div id='db_new_sites'></div>`);
        function _encodeToGb2312(str, opt) {
            let ret = "";
            try {
                ret = encodeToGb2312(str, opt);
            } catch (e) {
                ret = Math.random() * 1e6;
            }
            return ret;
        }

        if (location.host === "book.douban.com") {
            let title = $('#wrapper > h1 > span')[0].textContent.replace(/[:\(].*$/, "");
            let original_anchor = $('#info span.pl:contains("原作名")');
            let original = original_anchor[0] ? fetch_anchor(original_anchor) : '';
            let title_eng = title.match(/([a-zA-Z :,\(\)])+/);
            let original_eng = original.match(/([a-zA-Z :,\(\)])+/);
            let ywm = "";
            if (title_eng) {
                ywm = title;
            } else if (original_eng) {
                ywm = original.replace(/[:\(].*$/, "");
            }
            let has_ywm = title_eng + original_eng;
            let stitle = ywm.toLowerCase().replace(/ /g, "-");
            let is_writer = $('#info span.pl:contains("作者")').length > 0;
            let writer = is_writer ? ' ' + $('#info span.pl:contains("作者")')[0].nextSibling.nextSibling.textContent.replace(/\[[^\]]+\]/g, '').replace(/([^)]+)/g, '').replace(/^\s{1,}/g, '') : '';
            let gtitle = _encodeToGb2312(title, true);
            let ptitle = encodeURI(title).replace(/%/g, "%25");
            let isbn_anchor = $('#info span.pl:contains("ISBN")');
            let isbn = isbn_anchor[0] ? fetch_anchor(isbn_anchor) : '';

            // 比较时无视英文名大小写
            jQuery.expr[':'].Contains = function(a, i, m) {
                return jQuery(a).text().toUpperCase().indexOf(m[3].toUpperCase()) >= 0;
            };

            function isbn13to10(str) {
                //ISBN 10位和13位码互相转换
                var s;
                var c;
                var checkDigit = 0;
                var result = "";
                s = str.substring(3,str.length);
                for (let i = 10; i > 1; i-- ) {
                    c = s.charAt(10 - i);
                    checkDigit += (c - 0) * i;
                    result += c;
                }
                checkDigit = (11 - (checkDigit % 11)) % 11;
                result += checkDigit == 10 ? 'X' : (checkDigit + "");
                return ( result );
            }

            let isbn10 = isbn13to10 (isbn);

            site_map.push({
                name: "免费网盘 PDF 下载",
                label: [
                    { name: '56网盘书', link: 'http://www.56wangpan.com/search/o2kw' + title, selector: `div.title > a[title*='${title}']` },
                    { name: '爱问资料', link: `http://ishare.iask.sina.com.cn/search/home.html?ft=all&cond=${ptitle}`, selector: 'ul.landing-txt-list h4.data-name' },
                    { name: '小白盘图书', link: 'http://www.xiaobaipan.com/list-' + title + '.html?order=size', selector: 'h4.job-title a' },
                ]
            });
            site_map.push({
                name: "免费获得纸质书籍",
                label: [
                    { name: '华章出版社', link: `http://www.hzbook.com/index.php/Book/search.html?k=${title}`, selector: `div.comControlBook > a:contains(${title})` },
                ]
            });
            site_map.push({
                name: "二手书购买",
                label: [
                    { name: '淘宝', link: `https://s.taobao.com/search?tab=old&q=${isbn}`, selector: `div` },
                    { name: '京东', link:  `https://search.jd.com/search?keyword=${title}二手书`, selector: `div` },
                ]
            });
            site_map.push({
                name: "正版书购买",
                label: [
                    { name: '淘宝', link: `https://s.taobao.com/search?q=${isbn}`, selector: `div` },
                    { name: '京东', link:  `https://search.jd.com/search?keyword=${title}`, selector: `div` },
                ]
            });
            site_map.push({
                name: "电子书购买",
                label: [
                    { name: '多看阅读', link: `http://www.duokan.com/search/${title}`, selector: `div.wrap > a:contains(${title})` },
                    { name: '京东数字', link: `https://s-e.jd.com/Search?enc=utf-8&key=${title}${writer}`, selector: 'div.p-name a' },
                    { name: '亚马逊商店', link: 'https://www.amazon.cn/s?i=digital-text&k=' + isbn, selector: 'div.sg-col-inner h2' },
                ]
            });
            site_map.push({
                name: "免费在线听书",
                label: [
                    { name: '懒人听书', link: 'http://www.lrts.me/search/book/' + title, selector: 'ul li.book-item' },
                    { name: '评书吧', link: 'http://www.pingshu8.com/search/1.asp?keyword=' + gtitle, selector: "table.TableLine div[align='left']" },
                    { name: '天方听书网', link: 'http://www.tingbook.com/Book/SearchResult.aspx?keyword=' + title, selector: 'ul.search_result_list h4.clearfix' },
                    { name: '听中国', link: 'http://www.tingchina.com/search1.asp?mainlei=0&lei=0&keyword=' + gtitle, selector: 'dl.singerlist1 li' },
                    { name: '喜马有声', link: 'https://www.ximalaya.com/search/' + title + '/', selector: `div.xm-album-cover__wrapper + a[title*='${title}']` },
                ]
            });
        }

        function login_check(res) {
            let need_login = false;
            if (/login|verify|checkpoint|returnto/ig.test(res.finalUrl)) {
                need_login = true; 
            } else if (/refresh: \d+; url=.+login.+/ig.test(res.responseHeaders)) {
                need_login = true; 
            } else {
                let responseText = res.responseText;
                if (typeof responseText === "undefined") {
                    need_login = true; 
                } else if (responseText.length < 800 && /login|not authorized/.test(responseText)) {
                    need_login = true; 
                }
            }
            return need_login;
        }

        function Exist_check(label) {
            let site = label.name;
            let psite = $(`a[data-name="${site}"]`);

            function HideTag() {
                if (GM_getValue('enable_adv_auto_hide',false)) {
                    $(psite).hide();
                }
            }

            function TagExist(link) {
                let storage_data = true;
                if (label.rewrite_href && label.rewrite_href === true) { 
                    storage_data = cache.get(site, link || $(psite).attr("href"));
                    $(psite).attr("href", storage_data);
                }
                cache.add(site, storage_data);
            }

            function TagNotExist() {
                $(psite).attr("title", "资源不存在");
                HideTag();
            }

            function TagNeedLogin() {
                need_login_cache.add(site, true);
                $(psite).click(function () {
                    need_login_cache.del(site);
                });
                $(psite).attr("title", "站点需要登陆");
                HideTag();
            }

            function TagError(errtype) {
                $(psite).attr("title", "遇到问题" + (errtype ? (" - " + errtype) : ""));
                HideTag();
            }

            if (cache.get(site)) { TagExist(); return; }

            if (need_login_cache.get(site)) { TagNeedLogin(); return; }

            function check_core(label) {
                GM_xmlhttpRequest({
                    method: label.method || "GET",
                    url: label.ajax || label.link,
                    data: label.data,
                    headers: label.headers,
                    timeout: 45e3, 
                    onload: function (res) {
                        if (login_check(res)) {
                            TagNeedLogin();
                        } else {
                            try { 
                                let responseText = res.responseText;
                                if (label.type && ["json", "jsonp"].includes(label.type)) { 
                                    if (label.type === "jsonp") {
                                        responseText = responseText.match(/[^(]+\((.+)\)/)[1];
                                    }
                                    let par = JSON.parse(responseText);
                                    if (eval("par." + label.selector)) {
                                        TagExist();
                                    } else {
                                        TagNotExist(); 
                                    }
                                } else { // 否则默认label.type的默认值为 html
                                    let doc = page_parser(res.responseText);
                                    let body = doc.querySelector("body");
                                    // 因为jQuery的选择器丰富,故这里不用 dom.querySelector() 而用 jQuery.find()
                                    if (label.selector_need_login && $(body).find(label.selector_need_login).length) {
                                        TagNeedLogin(); // 如果存在selector_need_login选择器,则先判断是否存在以确定是否需要登录
                                    } else if ($(body).find(label.selector || "table.torrents:last > tbody > tr:gt(0)").length) {
                                        TagExist(res.finalUrl); // 最后使用selector选择器判断资源是否存在
                                    } else {
                                        TagNotExist(); // 所有情况都失败则未存在
                                    }
                                }
                            } catch (e) {
                                TagError("解析错误");
                            }
                        }
                    },
                    onerror: function () { TagError("请求故障"); },
                    ontimeout: function () { TagError("请求超时"); },
                });
            }

            // 请求动作方法
            function check_func() {
                $(psite).attr("title", "正在请求信息中");
                if (label.csrf) { // 对某些启用了csrf的站点,先使用正常方式请求一次获取csrf值
                    GM_xmlhttpRequest({
                        method: "GET",
                        url: label.link,
                        timeout: 45e3, // 默认45s服务器无响应算超时
                        onload: function (res) {
                            if (!login_check(res)) {
                                try {
                                    let doc = page_parser(res.responseText);
                                    let csrf_ = $(`input[name='${label.csrf.name}'`, doc);
                                    let csrf_key = csrf_.attr("value"); // 获取csrf值
                                    label[label.csrf.update] += `&${label.csrf.name}=${csrf_key}`; // 更新对应选项
                                    check_core(label);
                                } catch (e) {
                                    TagError("解析故障");
                                }
                            }
                        },
                        onerror: function () { TagError("请求错误"); },
                        ontimeout: function () { TagError("请求超时"); },
                    });
                } else {
                    check_core(label);
                }
            }

            // 根据设置,是自动请求还是鼠标移动时在做请求
            if (GM_getValue("enable_adv_auto_search", true)) {
                check_func();
            } else {
                $(psite).mouseenter(function(e){ // 鼠标进入时才自动搜索并反馈
                    if ($(psite).attr('title') == 'empty') { // 防止重复请求
                        check_func();
                        $(psite).unbind('mouseenter');
                    }
                });
            }
        }

        function site_exist_status() {
            for (let i = 0; i < site_map.length; i++) {
                let map_dic = site_map[i];
                if (GM_getValue(delete_site_prefix + map_dic.name, false)) {
                    continue;
                }
                $('#db_new_sites').append(`<div class="c-aside name-offline" style="padding: 18px 16px;background: #F6F6F2;" data-id="${i}"><h2><i>${map_dic.name}</i>· · · · · ·</h2><div class=c-aside-body style="padding: 0 12px;"> <ul class=bs > </ul> </div> </div>`);

                let in_site_html = $(`div[data-id='${i}'] ul.bs`);
                for (let j = 0; j < map_dic.label.length; j++) {
                    let label = map_dic.label[j];
                    if (GM_getValue(delete_site_prefix + label.name, false)) {
                        continue;
                    }
                    in_site_html.append(`<a href="${label.link}" data-name="${label.name}" target="_blank" rel="nofollow" title="empty">${label.name}</a>`);
                    Exist_check(label);

                }
            }
        }
        site_exist_status();
    }
});