├── .eslintrc ├── .github └── ISSUE_TEMPLATE.md ├── EX-baiduyunpan.mock.js ├── EX-baiduyunpan.user.js └── README.md /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | }, 5 | "parserOptions": { 6 | "ecmaVersion": 5 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | # 提交 Issue 前,请[查看][1]是否有相同的 Issue,重复问题直接关闭。 2 | 3 | # Before you submit an Issue, please [check][1] the Issues posted. Duplicate Issues will be closed directly. 4 | 5 | [1]: https://github.com/gxvv/ex-baiduyunpan/issues?q=is%3Aissue+is%3Aclosed 6 | -------------------------------------------------------------------------------- /EX-baiduyunpan.mock.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // @name EX-百度云盘-mock 3 | // @namespace https://github.com/gxvv/ex-baiduyunpan/ 4 | // @version 0.2.0 5 | // @description ex-baiduyunpan-mock 6 | // @author gxvv 7 | // @license MIT 8 | // @match *://pan.baidu.com/disk/home* 9 | // @match *://yun.baidu.com/disk/home* 10 | // @match *://pan.baidu.com/s/* 11 | // @match *://yun.baidu.com/s/* 12 | // @match *://pan.baidu.com/share/link?* 13 | // @match *://yun.baidu.com/share/link?* 14 | // @match *://eyun.baidu.com/s/* 15 | // @match *://eyun.baidu.com/enterprise/* 16 | // @run-at document-end 17 | // @grant unsafeWindow 18 | // ==/UserScript== 19 | 20 | (function(require) { 21 | 'use strict'; 22 | 23 | var script = unsafeWindow.document.createElement('script'); 24 | script.setAttribute('src', 'https://cdn.bootcss.com/Mock.js/1.0.0/mock-min.js'); 25 | unsafeWindow.document.body.appendChild(script); 26 | 27 | script.onload = function() { 28 | init(unsafeWindow.Mock); 29 | }; 30 | 31 | function init(Mock) { 32 | Mock.mock(/\/api\/download/, function(opts) { 33 | console.log(opts); 34 | if (/type=batch/.test(opts.body)) { 35 | return { 36 | "errno": 0, 37 | "request_id": 1638728891788015501, 38 | "dlink": "batch-https:\/\/www.baidupcs.com\/rest\/2.0\/pcs\/file?xxxx" 39 | }; 40 | } else if (/type=nolimit/.test(opts.body)) { 41 | return { 42 | "errno": 0, 43 | "request_id": 1638807446155143968, 44 | "dlink": [{ 45 | "fs_id": "585382316377758", 46 | "dlink": "nolimit-https:\/\/d.pcs.baidu.com\/file\/xxxx" 47 | }, { 48 | "fs_id": "719329425382101", 49 | "dlink": "nolimit-https:\/\/d.pcs.baidu.com\/file\/xxxx" 50 | }] 51 | }; 52 | } 53 | return {errno: -1}; 54 | }); 55 | 56 | Mock.mock(/\/api\/sharedownload/, function(opts) { 57 | console.log(opts); 58 | if (/type=batch/.test(opts.body)) { 59 | return { 60 | "errno": 0, 61 | "request_id": 1638867848412003560, 62 | "server_time": 1520841258, 63 | "dlink": "batch-https:\/\/www.baidupcs.com\/rest\/2.0\/pcs\/file?xxxx" 64 | }; 65 | } 66 | 67 | return { 68 | "errno": 0, 69 | "request_id": 1638837700730131957, 70 | "server_time": 1520841146, 71 | "list": [{ 72 | "fs_id": 168192872164822, 73 | "server_filename": "\u6559\u7236BD\u53cc\u8bed\u53cc\u5b57.mkv", 74 | "size": 3311639424, 75 | "isdir": 0, 76 | "category": 1, 77 | "share": "0", 78 | "dlink": "nolimit-https:\/\/d.pcs.baidu.com\/file\/xxxxx", 79 | }] 80 | }; 81 | }); 82 | } 83 | })(unsafeWindow.require); 84 | -------------------------------------------------------------------------------- /EX-baiduyunpan.user.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // @name EX-百度云盘 3 | // @namespace https://github.com/gxvv/ex-baiduyunpan/ 4 | // @version 0.3.3 5 | // @description [下载大文件] [批量下载] [文件夹下载] [百度网盘] [百度云盘] [企业版] 6 | // @description [baidu] [baiduyun] [yunpan] [baiduyunpan] [eyun] 7 | // @author gxvv 8 | // @license MIT 9 | // @supportURL https://github.com/gxvv/ex-baiduyunpan/issues 10 | // @updateURL https://gxvv.github.io/ex-baiduyunpan/EX-baiduyunpan.user.js 11 | // @date 01/01/2017 12 | // @modified 20/08/2018 13 | // @match *://pan.baidu.com/disk/home* 14 | // @match *://yun.baidu.com/disk/home* 15 | // @match *://pan.baidu.com/s/* 16 | // @match *://yun.baidu.com/s/* 17 | // @match *://pan.baidu.com/share/link?* 18 | // @match *://yun.baidu.com/share/link?* 19 | // @match *://eyun.baidu.com/s/* 20 | // @match *://eyun.baidu.com/enterprise/share/link?* 21 | // @run-at document-end 22 | // @grant unsafeWindow 23 | // @grant GM_addStyle 24 | // @grant GM_info 25 | // @require https://cdn.bootcss.com/babel-standalone/6.26.0/babel.min.js 26 | // @require https://cdn.bootcss.com/clipboard.js/1.5.16/clipboard.min.js 27 | // ==/UserScript== 28 | 29 | /* jshint ignore:start */ 30 | var inline_src = (<> { 36 | let showError = msg => { 37 | GM_addStyle(`#errorDialog {position: fixed;top: 76.5px; bottom: auto; left: 423px; right: auto;background: #fff;border: 1px solid #ced1d9;border-radius: 4px;box-shadow: 0 0 3px #ced1d9;color: black;word-break: break-all;display: block;width: 520px;padding: 10px 20px;z-index: 9999;} 38 | #errorDialog h3 {border-bottom: 1px solid #ced1d9;font-size: 1.5em;font-weight: bold;} 39 | #errorDialog .very-very-important {font-size: 20px;font-weight: bold;} 40 | `); 41 | let div = document.createElement('div'); 42 | 43 | div.innerHTML = `
`; 61 | div.addEventListener('click', div.remove); 62 | document.body.appendChild(div); 63 | }; 64 | 65 | define('ex-yunpan:pageInfo', () => { 66 | const URL_HREF = location.href; 67 | const MATCHS = { 68 | 'https?://(pan|yun).baidu.com/disk/home.*': 'pan', 69 | 'https?://(pan|yun).baidu.com/s/.*': 'share', 70 | 'https?://(pan|yun).baidu.com/share/link\?.*': 'share', 71 | 'https?://eyun.baidu.com/(s|enterprise)/.*': 'enterprise' 72 | }; 73 | const PAGE_CONFIG = { 74 | pan: { 75 | prefix: 'function-widget-1:', 76 | product: 'pan', 77 | containers: ['.g-button[title="\u79bb\u7ebf\u4e0b\u8f7d"]'], 78 | style: () => {} 79 | }, 80 | share: { 81 | prefix: 'function-widget-1:', 82 | product: 'share', 83 | containers: [ 84 | 'div:not(.file-name)>div>.x-button-box>.g-button[title^="\u4e0b\u8f7d"]', 85 | '.module-share-top-bar .x-button-box>.g-button[title="\u4e0b\u8f7d"]' 86 | ], 87 | style: () => { 88 | let style = `.KPDwCE .QxJxtg {z-index: 2;} 89 | .module-share-header .slide-show-right {width: auto;} 90 | .ex-yunpan-dropdown-button.g-dropdown-button.button-open .menu {z-index:41;} 91 | .module-share-header .slide-show-header h2 {width:210px;} 92 | .g-dropdown-button.ex-yunpan-dropdown-button {margin: 0 5px!important;}`; 93 | 94 | GM_addStyle(style); 95 | } 96 | }, 97 | enterprise: { 98 | prefix: 'business-function:', 99 | product: 'enterprise', 100 | containers: ['div:not(.operate)>.button-box-container>.g-button[title="\u4e0b\u8f7d"]'], 101 | style: () => { 102 | let style= `.ex-yunpan-dropdown-button .icon-download{background-image: url(/box-static/business-function/infos/icons_z.png);} 103 | .ex-yunpan-dropdown-button .g-button:hover .icon-download{background-position: 0px -34px;}`; 104 | 105 | GM_addStyle(style); 106 | } 107 | } 108 | }; 109 | let currentPage = 'pan'; 110 | 111 | for (let match in MATCHS) { 112 | if (new RegExp(match).test(URL_HREF)) { 113 | currentPage = MATCHS[match]; 114 | break; 115 | } 116 | } 117 | return PAGE_CONFIG[currentPage]; 118 | }); 119 | 120 | define('ex-yunpan:ctx', require => { 121 | let {product} = require('ex-yunpan:pageInfo'); 122 | let prefix = product === 'enterprise' ? 'business-core:' : 'system-core:'; 123 | let {instanceForSystem: ctx} = require(`${prefix}context/context.js`); 124 | 125 | return ctx; 126 | }); 127 | 128 | define('ex-yunpan:pluginInit', async require => { 129 | let ctx = require('ex-yunpan:ctx'); 130 | let {prefix} = require('ex-yunpan:pageInfo'); 131 | let {pageInfo: {currentProduct = 'pan'} = {currentProduct: 'pan'}} = ctx; 132 | let currPrdIsEyun = currentProduct === 'enterprise'; 133 | 134 | require.async(`${prefix}download/service/guanjiaConnector.js`, gjc => { 135 | gjc.init = () => ctx.ui.tip({mode: 'caution', 136 | msg: 'EX-baiduyunpan: 检测到正在调用云管家,若脚本失效,请检查更新', 137 | autoClose: false, 138 | hasClose: true}); 139 | }); 140 | let ddsScript = currPrdIsEyun ? 'downloadDirectService.js' : 'downloadDirect.js'; 141 | 142 | require.async(`${prefix}download/service/${ddsScript}`, dds => { 143 | let funName = currPrdIsEyun ? 'straightforwardDownload' : 'start'; 144 | let _ = dds[funName]; 145 | 146 | if (typeof _ !== 'function') return; 147 | dds[funName] = (...args) => { 148 | ctx.ui.tip({mode: 'loading', 149 | msg: 'EX-baiduyunpan: 正在开始下载...'}); 150 | _.apply(null, args); 151 | }; 152 | }); 153 | require.async(`${prefix}download/util/context.js`, context => { 154 | context.getContext = () => ctx; 155 | }); 156 | let limitedIsRemoved = await new Promise((resolve, reject) => { 157 | unsafeWindow.addEventListener('load', () => reject(false)); 158 | if (currPrdIsEyun) { 159 | require.async(`${prefix}download/service/downloadManager.js`, dm => { 160 | dm.MODE_PRE_INSTALL = dm.MODE_PRE_DOWNLOAD; 161 | resolve(true); 162 | }); 163 | } else { 164 | require.async(`${prefix}download/config.js`, config => { 165 | [].push.apply(config.directDownloadkeysConfig, config.guanjiaDownloadkeysConig); 166 | config.guanjiaDownloadkeysConig = []; 167 | resolve(true); 168 | }); 169 | } 170 | }).catch(result => false); 171 | 172 | if (limitedIsRemoved) { 173 | let tipConfig = { 174 | mode: 'success', 175 | msg: 'EX-baiduyunpan: 插件加载成功' 176 | }; 177 | 178 | try { 179 | await require('ex-yunpan:downloadBtnInit'); 180 | } catch (e) { 181 | tipConfig = { 182 | mode: 'caution', 183 | msg: 'EX-baiduyunpan: 插件加载成功,按钮初始化失败', 184 | autoClose: false, 185 | hasClose: true 186 | }; 187 | } 188 | ctx.ui.tip(tipConfig); 189 | } else { 190 | if(document.querySelector('#share_nofound_des,#error-info') !== null) return; 191 | throw new Error('插件加载失败 core crack failed'); 192 | } 193 | }); 194 | 195 | define('ex-yunpan:downloadBtnInit', async require => { 196 | let ctx = require('ex-yunpan:ctx'); 197 | let {pageInfo: {currentProduct = ''} = {currentProduct: ''}} = ctx; 198 | let pageInfo = require('ex-yunpan:pageInfo'); 199 | let {prefix} = pageInfo; 200 | let fetchDownLinks = require('ex-yunpan:fetchDownLinks'); 201 | let menus = [ 202 | { 203 | title: '普通下载', 204 | click: () => { 205 | let {start} = require(`${prefix}download/start.js`); 206 | 207 | start(ctx); 208 | }, 209 | enablePrd: ['pan', 'share', 'enterprise'] 210 | }, { 211 | title: '复制链接', 212 | click: async () => { 213 | let result = await fetchDownLinks(); 214 | 215 | if (result.length === 0) return; 216 | let {show} = require('ex-yunpan:clipboardDialog'); 217 | 218 | show(result); 219 | }, 220 | enablePrd: ['pan', 'share', 'enterprise'] 221 | }, { 222 | title: '复制压缩链接', 223 | click: async () => { 224 | let result = await fetchDownLinks('batch'); 225 | 226 | if (result.length === 0) return; 227 | let {show} = require('ex-yunpan:clipboardDialog'); 228 | 229 | show(result); 230 | }, 231 | enablePrd: ['pan', 'share', 'enterprise'] 232 | }, { 233 | title: ``, 235 | enablePrd: ['pan', 'share', 'enterprise'] 236 | } 237 | ]; 238 | let exDlBtnConfig = { 239 | type: 'dropdown', 240 | title: 'EX-下载', 241 | resize: true, 242 | menu: menus.filter(menu => ~menu.enablePrd.indexOf(currentProduct)), 243 | icon: 'icon-download' 244 | }; 245 | let selectors = pageInfo.containers.join(); 246 | let elements = [].slice.call(document.querySelectorAll(selectors) || [], 0); 247 | 248 | elements.forEach(element => { 249 | let exDlBtn = ctx.ui.button(exDlBtnConfig); 250 | 251 | exDlBtn.dom.addClass('ex-yunpan-dropdown-button').insertAfter(element); 252 | exDlBtn.resizeButtonWidth(); 253 | }); 254 | pageInfo.style(); 255 | }); 256 | 257 | define('ex-yunpan:fetchDownLinks', () => { 258 | let ctx = require('ex-yunpan:ctx'); 259 | let {prefix} = require('ex-yunpan:pageInfo'); 260 | let dServ = null; 261 | 262 | new Promise((resolve, reject) => { 263 | unsafeWindow.addEventListener('load', () => reject()); 264 | require.async(`${prefix}download/service/dlinkService.js`, dlinkService => resolve(dlinkService)); 265 | }).then(dlinkService => dServ = dlinkService).catch(() => dServ = false); 266 | let fetchDownLinks = async type => { 267 | let selectedList = ctx.list.getSelected(); 268 | 269 | if (selectedList.length === 0) { 270 | ctx.ui.tip({mode: 'caution', 271 | msg: 'EX-baiduyunpan: 您还没有选择下载的文件'}); 272 | return []; 273 | } 274 | let {pageInfo: {currentProduct = 'pan'} = {currentProduct: 'pan'}} = ctx; 275 | let foldersList = selectedList.filter(item => item.isdir === 1); 276 | let filesList = selectedList.filter(item => item.isdir === 0); 277 | 278 | if (filesList.length > 0 && currentProduct !== 'enterprise' && type === 'nolimit') { 279 | foldersList.unshift(filesList); 280 | } else { 281 | [].push.apply(foldersList, filesList); 282 | } 283 | ctx.ui.tip({mode: 'loading', 284 | msg: '开始请求链接...'}); 285 | let requestMethod; 286 | 287 | if (currentProduct === 'pan') { 288 | requestMethod = (list, callback) => { 289 | dServ.getDlinkPan(dServ.getFsidListData(list), 290 | type || (list.isdir === 1 ? 'batch' : 'nolimit'), 291 | callback, 292 | undefined, 293 | undefined, 294 | 'POST'); 295 | }; 296 | } else if (currentProduct === 'share') { 297 | let {shareid, uk, sign, timestamp} = require('disk-share:widget/data/yunData.js').get(); 298 | 299 | requestMethod = (list, callback) => { 300 | dServ.getDlinkShare({ 301 | list, 302 | sign, 303 | timestamp, 304 | share_id: shareid, 305 | share_uk: uk, 306 | type: type || (list.isdir === 1 ? 'batch' : 'nolimit') 307 | }, callback); 308 | }; 309 | } else { 310 | let {shareid, uk, sign, timestamp} = require('page-common:widget/data/yunData.js').get(); 311 | 312 | requestMethod = (list, callback) => { 313 | dServ.getDlinkShare({ 314 | sign, 315 | timestamp, 316 | share_id: shareid, 317 | share_uk: uk, 318 | list: [list], 319 | isForBatch: type === 'batch' 320 | }, callback); 321 | }; 322 | } 323 | let timeout = foldersList.length === 1 ? 3e4 : 3e3; 324 | let promises = foldersList.map(e => new Promise((resolve, reject) => { 325 | setTimeout(() => resolve(Object.assign({}, e)), timeout); 326 | requestMethod(e, result => resolve(Object.assign({}, e, result))); 327 | })); 328 | let result = await Promise.all(promises); 329 | 330 | ctx.ui.hideTip(); 331 | let failedRes = result.filter(res => res.errno !== 0); 332 | 333 | if (failedRes.length > 0) { 334 | try { 335 | dServ.dialog.hide(); 336 | } catch (e) {/* do nothing */} 337 | ctx.ui.tip({mode: 'caution', 338 | msg: `EX-baiduyunpan: ${failedRes.length}个文件请求链接失败`}); 339 | } 340 | let dlinks = []; 341 | 342 | result.filter(res => res.errno === 0).forEach(res => { 343 | if (typeof res.dlink === 'string') { 344 | let fileType = res.isdir ? '【文件夹】' : '【文件】'; 345 | let packNameEncode = encodeURIComponent(`${fileType}${res.server_filename}.zip`); 346 | let dlink = `${res.dlink}&zipname=${packNameEncode}`; 347 | 348 | dlinks.push(res.dlink && dlink); 349 | } else { 350 | let linklist = res.dlink || res.list || []; 351 | 352 | [].push.apply(dlinks, linklist.map(e => e.dlink)); 353 | } 354 | }); 355 | if (dlinks.length === 0) { 356 | ctx.ui.tip({mode: 'caution', 357 | msg: 'EX-baiduyunpan: 未获取到下载链接,请重试'}); 358 | } 359 | return dlinks; 360 | }; 361 | 362 | return fetchDownLinks; 363 | }); 364 | define('ex-yunpan:clipboardDialog', () => { 365 | let ctx = require('ex-yunpan:ctx'); 366 | let show = list => { 367 | let clipboard; 368 | let maxrow = list.length > 10 ? 11 : list.length + 1; 369 | let textareaHtml = ``; 371 | let dialog = ctx.ui.confirm({ 372 | title: '复制链接', 373 | body: textareaHtml, 374 | sureText: '复制', 375 | onClose: () => { 376 | if (clipboard && clipboard.destory) { 377 | clipboard.destroy(); 378 | } 379 | } 380 | }); 381 | 382 | dialog.buttonIns[0].dom.attr({ 383 | 'data-clipboard-action': 'copy', 384 | 'data-clipboard-target': '#ExTextArea' 385 | }).addClass('ex-clip-btn').off(); 386 | clipboard = new Clipboard('.ex-clip-btn'); 387 | clipboard.on('success', e => { 388 | ctx.ui.tip({mode: 'success', 389 | msg: `EX-baiduyunpan: 复制${list.length}个链接`}); 390 | e.clearSelection(); 391 | dialog.hide(); 392 | clipboard.destroy(); 393 | }).on('error', e => { 394 | ctx.ui.tip({mode: 'caution', 395 | msg: 'EX-baiduyunpan: 复制失败,请手动复制'}); 396 | clipboard.destory(); 397 | }); 398 | }; 399 | 400 | return { 401 | show 402 | }; 403 | }); 404 | 405 | require('ex-yunpan:pluginInit').catch(ex => { 406 | showError(ex); 407 | }); 408 | })(unsafeWindow); 409 | /* jshint ignore:start */ 410 | ]]>>).toString(); 411 | var c = Babel.transform(inline_src, { presets: ["es2015", "es2016"] }); 412 | eval(c.code); 413 | /* jshint ignore:end */ 414 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # EX-百度云盘 2 |  3 |  4 |  5 | 6 | 如有问题欢迎提交Issue,欢迎交流讨论。:star2: 7 | ## ⚠️注意 8 | 因时间有限,仅对最新版Chrome和Safari浏览器下TamperMonkey提供支持。不会增加额外非必要的功能。 9 | 10 | 本脚本仅提供了获取下载链接的功能,下载速度过慢/第三方下载工具不能下载/链接超时/压缩包不能解压等问题与脚本无关。 11 | 12 | ## 功能 13 | - 解除大文件下载限制 14 | - 支持文件夹下载 15 | - 支持多文件下载 16 | - 支持批量复制下载链接 17 | - 支持百度网盘和百度企业网盘 18 | 19 | ## License 20 | The MIT License (MIT) 21 | Copyright © 2017 [gxvv](https://github.com/gxvv) 22 | 23 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 24 | 25 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 26 | 27 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 28 | --------------------------------------------------------------------------------