// ==UserScript== // @name 豆瓣资源下载大师 // @description 聚合数百家资源网站,通过右侧边栏1秒告诉你哪些网站能下载豆瓣页面上的电影|电视剧|纪录片|综艺|动画|音乐|图书等。 // @author Noir夜祀 // @contributor Rhilip // @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 // @grant GM_getResourceText // @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 // @include https://movie.douban.com/* // @include https://music.douban.com/* // @include https://book.douban.com/* // @include https://bangumi.moe/search/title* // @include https://desitorrents.tv/* // @include https://v.dsb.ink/* // @include http://www.x5v.net/* // @resource top250_css https://img1.doubanio.com/f/movie/5986ab7176af54744e71209c6afb5e1d8d1191cf/dist/movie/charts/top250.css // @license Zlib/Libpng License // @version 1.0.1 // @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAACXBIWXMAAAsTAAALEwEAmpwYAAAGWmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNy4xLWMwMDAgNzkuYTg3MzFiOSwgMjAyMS8wOS8wOS0wMDozNzozOCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0RXZ0PSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VFdmVudCMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIDIzLjAgKFdpbmRvd3MpIiB4bXA6Q3JlYXRlRGF0ZT0iMjAyMi0wOS0yM1QxMzo0NjoxOSswODowMCIgeG1wOk1vZGlmeURhdGU9IjIwMjItMDktMjNUMTM6NDY6NDIrMDg6MDAiIHhtcDpNZXRhZGF0YURhdGU9IjIwMjItMDktMjNUMTM6NDY6NDIrMDg6MDAiIGRjOmZvcm1hdD0iaW1hZ2UvcG5nIiBwaG90b3Nob3A6Q29sb3JNb2RlPSIzIiBwaG90b3Nob3A6SUNDUHJvZmlsZT0ic1JHQiBJRUM2MTk2Ni0yLjEiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6NjZiMzcwOTAtM2M4Mi01ODRlLWI4ODItZjhkYTY0YTkyMDVlIiB4bXBNTTpEb2N1bWVudElEPSJhZG9iZTpkb2NpZDpwaG90b3Nob3A6YWI2MmY2NmUtZWViYi03YTQxLTkxODYtZmQwYzYxMWQ1YzU1IiB4bXBNTTpPcmlnaW5hbERvY3VtZW50SUQ9InhtcC5kaWQ6Y2FkZDFhNzItNTY0ZC04YjQ0LTkyMGMtNjAyMzNmMTdmYjlmIj4gPHhtcE1NOkhpc3Rvcnk+IDxyZGY6U2VxPiA8cmRmOmxpIHN0RXZ0OmFjdGlvbj0iY3JlYXRlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDpjYWRkMWE3Mi01NjRkLThiNDQtOTIwYy02MDIzM2YxN2ZiOWYiIHN0RXZ0OndoZW49IjIwMjItMDktMjNUMTM6NDY6MTkrMDg6MDAiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCAyMy4wIChXaW5kb3dzKSIvPiA8cmRmOmxpIHN0RXZ0OmFjdGlvbj0iY29udmVydGVkIiBzdEV2dDpwYXJhbWV0ZXJzPSJmcm9tIGFwcGxpY2F0aW9uL3ZuZC5hZG9iZS5waG90b3Nob3AgdG8gaW1hZ2UvcG5nIi8+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJzYXZlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDo2NmIzNzA5MC0zYzgyLTU4NGUtYjg4Mi1mOGRhNjRhOTIwNWUiIHN0RXZ0OndoZW49IjIwMjItMDktMjNUMTM6NDY6NDIrMDg6MDAiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCAyMy4wIChXaW5kb3dzKSIgc3RFdnQ6Y2hhbmdlZD0iLyIvPiA8L3JkZjpTZXE+IDwveG1wTU06SGlzdG9yeT4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz7XAPwHAAABgklEQVRYhe2Wv0tCURTHP0+00EUM3mAiJYjPodDZQSGMksYamrOhrfG15ZhjW0vNDTY0RAbW4F+QGJLiUPjrgVD4F9ik+St9mnUd/E7v3nvO+X645zzek1BtEeASsPO/qgFRCdVWFWDehjAINAewGwSaA2Ds3XhTM6zYnH9i9v5ZYjXu69oTfgPCASRUW1MkgPAb6BvClp6P0/jsa1MxydRe8J8HB57N7g10Kl8vcp25Gavwvm8XRXZPB+C1XiCWio8F4F9e1wUgvAVzAF0z4JU9xMLqWIW9smd6AIrs5nRMAL0S3oKR34LkQYItzwYApUaF4MXO0ILpozucVgcAD4Untq/2hsaPbEEie9sGcFodLBoXyNeLA2MV2d02b+WO0sgWJAuPXeuIsvljbO9Zb+5EAOVGlayW6zAJDwH4PstqOcqN6u8BAO7zqfZzyBXAYjL3xVhMZkKuwMCcYeobwubZh67ESSWdLHWthb+GwgHm/4QzAVAT6K8ZgENAE2BeAaJfBA5av5nlEvoAAAAASUVORK5CYII= // @run-at document-end // @namespace doveboy_js // @downloadURL none // ==/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; } // 不属于豆瓣的页面 if (!/douban.com/.test(location.host)) { function AutoSearch(host, zInputSelector, btnSelector, dict) { if (location.host === host) { let match = location.href.match(/#search_(.+)/); // 从url的hash部分获取搜索关键词 if (match) { history.pushState("", document.title, window.location.pathname + window.location.search); // 清空hash window.addEventListener("load", function () { // 等待页面完全加载完成 let zInput = $(zInputSelector); zInput.val(decodeURIComponent(match[1])); // 填入搜索值 dict = $.extend({}, dict); if (dict.ang) { // 补加Event,防止Angular绑定失效。 From: https://stackoverflow.com/questions/34360739/automate-form-submission-on-an-angularjs-website-using-tampermonkey let changeEvent = document.createEvent("HTMLEvents"); changeEvent.initEvent("change", true, true); zInput[0].dispatchEvent(changeEvent); } if (dict.notarget) { // 取消form的提交跳转 $(btnSelector).parents("form").attr("target", "_self"); } $(btnSelector).click(); // 模拟点击 }); } } } AutoSearch("bangumi.moe", '#filter-tag-list div.torrent-title-search > md-input-group > input', '#filter-tag-list > div.torrent-search > div:nth-child(3) > button:nth-child(4)', { ang: true }); AutoSearch("v.dsb.ink", '.search__input', '.icon-search', {}); AutoSearch("www.x5v.net", '#searchText', '#btnSearch', {}); AutoSearch("desitorrents.tv", 'input.search_string_input', 'input.search_string_input+img', {}); return; // 终止脚本运行,防止豆瓣的css以及js片段等污染页面 } // 阻止其他豆瓣同类脚本加载 var seBwhA = document.createElement("a"); seBwhA.id = "seBwhA"; document.getElementsByTagName("html")[0].appendChild(seBwhA); // 注入脚本相关的CSS,包括:隐藏、调整豆瓣原先的元素,脚本页面样式 GM_addStyle(` .c-aside{margin-bottom:30px}.c-aside-body a{border-radius:6px;color:#37a;display:inline-block;letter-spacing:normal;margin:0 8px 8px 0;padding:0 8px;text-align:center;width:65px}.c-aside-body a:link,.c-aside-body a:visited{background-color:#f5f5f5;color:#37a}.c-aside-body a:active,.c-aside-body a:hover{background-color:#e8e8e8;color:#37a}.c-aside-body a.available{background-color:#5ccccc;color:#006363}.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)} .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}'); } // -- 定义基础方法 -- // 对使用GM_xmlhttpRequest返回的html文本进行处理并返回DOM树 function page_parser(responseText) { // 替换一些信息防止图片和页面脚本的加载,同时可能加快页面解析速度 // responseText = responseText.replace(/s+src=/ig, ' data-src='); // 图片,部分外源脚本 // responseText = responseText.replace(/]*?>[\S\s]*?<\/script>/ig, ''); //页面脚本 return (new DOMParser()).parseFromString(responseText, 'text/html'); } function getDoc(url, meta, callback) { GM_xmlhttpRequest({ method: 'GET', url: url, onload: function (responseDetail) { if (responseDetail.status === 200) { let doc = page_parser(responseDetail.responseText); callback(doc, responseDetail, meta); } } }); } function getJSON(url, callback) { GM_xmlhttpRequest({ method: 'GET', url: url, headers: { 'Accept': 'application/json' }, onload: function (response) { if (response.status >= 200 && response.status < 400) { callback(JSON.parse(response.responseText), url); } else { callback(false, url); } } }); } // 缓存相关方法 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 _version = GM_getValue("version", "轻量版"); let delete_site_prefix = "delete_site_"; if (typeof GM_registerMenuCommand !== "undefined") { function changeVersionConfirm() { if (confirm(`你当前版本是 ${_version},是否进行切换?`)) { GM_setValue("version", _version === "完整版" ? "轻量版" : "完整版"); } } GM_registerMenuCommand("脚本功能切换", changeVersionConfirm); function changeTagBColor() { let now_bcolor_list = [GM_getValue("tag_bcolor_exist", "#e3f1ed"), GM_getValue("tag_bcolor_not_exist", "#f4eac2"), GM_getValue("tag_bcolor_need_login", ""), GM_getValue("tag_bcolor_error", "")]; let name = prompt("请依次输入代表'资源存在','资源不存在','站点需要登陆','站点解析错误'颜色的Hex值,并用英文逗号分割。当前值为:", `${now_bcolor_list.join(',')}`); if (name != null && name !== "") { try { let bcolor_list = name.split(","); GM_setValue("tag_bcolor_exist", bcolor_list[0] || "#e3f1ed"); GM_setValue("tag_bcolor_not_exist", bcolor_list[1] || "#f4eac2"); GM_setValue("tag_bcolor_need_login", bcolor_list[2] || ""); GM_setValue("tag_bcolor_error", bcolor_list[3] || ""); } catch (e) { alert("解析输入出错"); } } } GM_registerMenuCommand("更改标签背景色", changeTagBColor); function changeTagFColor() { let now_fcolor_list = [GM_getValue("tag_fcolor_exist", "#3377aa"), GM_getValue("tag_fcolor_not_exist", "#3377aa"), GM_getValue("tag_fcolor_need_login", "#3377aa"), GM_getValue("tag_fcolor_error", "#3377aa")]; let name = prompt("请依次输入代表'资源存在','资源不存在','站点需要登陆','站点解析错误'颜色的Hex值,并用英文逗号分割。当前值为:", `${now_fcolor_list.join(',')}`); if (name != null && name !== "") { try { let fcolor_list = name.split(","); GM_setValue("tag_fcolor_exist", fcolor_list[0] || "#3377aa"); GM_setValue("tag_fcolor_not_exist", fcolor_list[1] || "#3377aa"); GM_setValue("tag_fcolor_need_login", fcolor_list[2] || "#3377aa"); GM_setValue("tag_fcolor_error", fcolor_list[3] || "#3377aa"); } catch (e) { alert("解析输入出错"); } } } GM_registerMenuCommand("更改标签文字色", changeTagFColor); function forceCacheClear() { if (confirm("清空所有缓存信息(包括资源存在情况、登陆情况等)")) { clearExpiredCacheValue(true); } } GM_registerMenuCommand("清空脚本缓存", forceCacheClear); } let fetch_anchor = function (anchor) { return anchor[0].nextSibling.nodeValue.trim(); }; function starBlock(source, source_link, _rating, _vote, max = 10) { let starValue = parseFloat(_rating) / 2; starValue = starValue % 1 > 0.5 ? Math.floor(starValue) + 0.5 : Math.floor(starValue); starValue *= (100 / max); return `
${parseFloat(_rating).toFixed(1)}
` } function parseLdJson(raw) { return JSON.parse(raw.replace(/\n/ig, '')); } $(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 = []; /** label对象键值说明: * name: String 站点名称,请注意该站点名称在不同的site_map中也应该唯一,以免脚本后续判断出错 * method: String 搜索请求方式,默认值为 "GET" * link: String 构造好的请求页面链接,作为label的href属性填入,用户应该能直接点开这个页面。 * ajax: String 如果站点使用ajax的形式加载页面,则需要传入该值作为实际请求的链接,即优先级比link更高。 * type: String 返回结果类型,脚本默认按html页面解析;只有当传入值为"json"时,脚本按JSON格式解析 * selector: String 搜索成功判断结果,默认值为 "table.torrents:last > tbody > tr:gt(0)" (适用于国内多数NexusPHP构架的站点) * 如果type为"page"(默认)时,为一个(jQuery)CSS选择器, * 如果type为"json"或"jsonp"时,为一个具体的判断式。 * selector_need_login 搜索需要登录的选择器,仅在type为默认时有用,其余用法同Selector一致。 * data: String 作为请求的主体发送的内容,默认为空即可 * headers: Object 修改默认请求头,(防止某些站点有referrer等请求头检查 * rewrite_href: Boolean 如果站点最终搜索显示的页面与实际使用搜索页面(特别是使用post进行交互的站点)不一致, * 设置为true能让脚本存储最终url,并改写label使用的href属性 * csrf: Object 一个类似这样的字典 { name: "_csrf", update: "link"} * 其中key为站点csrf防护的名称,update为需要更新的字段(一般为link或data) * * 注意: 1. 如果某键有默认值,则传入值均会覆盖默认值 * 2. 关于请求方法请参考:https://github.com/scriptish/scriptish/wiki/GM_xmlhttpRequest * */ 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(`
`); $("#drdm_req_status_hide").click(function () { $("#drdm_req_status").hide(); }); $("#drdm_hide_fail").click(function () { $("#drdm_sites a[title!=\"资源存在\"]").hide(); $('#drdm_sites > div.c-aside.name-offline').each(function () { let that = $(this); if (that.find('> div > ul > a:visible').length == 0) { that.hide() } }); }); $("#drdm_show_all").click(function () { $("#drdm_sites a:hidden").show(); $('#drdm_sites > div.c-aside.name-offline').show(); }); let update_status_interval; function update_req_status() { let asking_length = $("#drdm_sites a[title=\"正在请求信息中\"]").length; $("#drdm_req_success").text($("#drdm_sites a[title=\"资源存在\"]").length); $("#drdm_req_asking").text(asking_length); $("#drdm_req_noexist").text($("#drdm_sites a[title=\"资源不存在\"]").length); $("#drdm_req_fail").text($("#drdm_sites a[title=\"站点需要登陆\"]").length + $("#drdm_sites a[title=\"遇到问题\"]").length); if (asking_length === 0) { clearInterval(update_status_interval); // 当所有请求完成后清除定时器 if (GM_getValue('enalbe_adv_auto_tip_hide', false)) { $("#drdm_req_status_hide").click(); } } } function _encodeToGb2312(str, opt) { let ret = ""; try { ret = encodeToGb2312(str, opt); } catch (e) { ret = Math.random() * 1e6; $("#drdm_dep_notice").show(); } return ret; } if (location.host === "movie.douban.com") { // 查看原图 if ($('#mainpic p.gact').length === 0) { // 在未登录的情况下添加空白元素以方便查看原图交互按钮的定位 $("#mainpic").append("

"); } let posterAnchor = document.querySelector('#mainpic > a > img'); let postersUrl, rawUrl; if (posterAnchor) { postersUrl = posterAnchor.getAttribute("src"); rawUrl = postersUrl.replace(/photo\/[sl](_ratio_poster|pic)\/public\/(p\d+).+$/, "photo/raw/public/$2.jpg"); $('#mainpic p.gact').after(`查看原图`); } // 调整底下剧情简介的位置 let interest_sectl_selector = $('#interest_sectl'); interest_sectl_selector.after($('div.grid-16-8 div.related-info')); interest_sectl_selector.attr('style', 'float:right'); $('div.related-info').attr('style', 'width:480px;float:left'); // Movieinfo信息生成相关 let this_title, trans_title, aka; let year, region, genre, language, playdate; let imdb_link, imdb_id, imdb_average_rating, imdb_votes, imdb_rating; let douban_average_rating, douban_votes, douban_rating; let episodes, duration; let director, writer, cast; let tags, introduction, awards; // 获得