2025最新可用-百度网盘不限制下载-神速Down(支持整个文件夹下载)
// ==UserScript==
// @name 2025最新可用-百度网盘不限制下载-神速Down(支持整个文件夹下载)
// @namespace https://github.com/AFANOOO/sspan
// @version 9.9.3
// @icon https://s2.loli.net/2025/04/19/dwVq7tx9NBgvfaz.png
// @author GreasyFork
// @description 2025持续更新可用的.不限制速度文件夹下载的百度网盘解析脚本,无视黑号,拥有IDM/Aria2/Motrix三种方式任意体验极速下载!支持Microsoft Edge、Google Chrome、Firefox等浏览器 面向所有网友免费交流学习使用,更多功能正在完善中...
// @antifeature ads
// @antifeature membership
// @antifeature tracking
// @license MIT
// @match *://pan.baidu.com/*
// @match *://yun.baidu.com/*
// @match *://pan.baidu.com/disk/main*
// @match *://yun.baidu.com/disk/main*
// @match *://pan.baidu.com/s/*
// @match *://yun.baidu.com/s/*
// @match *://pan.baidu.com/share/*
// @match *://yun.baidu.com/share/*
// @homepage https://ass.sshezu.cn
// @supportURL https://ass.sshezu.cn
// @connect localhost
// @connect 127.0.0.1
// @connect baidu.com
// @connect *
// @require https://lib.baomitu.com/jquery/3.7.1/jquery.js
// @require https://lib.baomitu.com/layui/2.9.3/layui.min.js
// @require https://lib.baomitu.com/limonte-sweetalert2/11.10.2/sweetalert2.all.min.js
// @require https://lib.baomitu.com/layui/2.9.3/layui.js
// @resource customCSS https://lib.baomitu.com/layui/2.9.3/css/layui.css
// @grant GM_xmlhttpRequest
// @grant GM_addStyle
// @grant GM_getResourceText
// @run-at document-idle
// @grant unsafeWindow
// @connect oa.assco.cn
// @connect jiexi.ssoos.cc
// @connect pro.ssoos.cc
// ==/UserScript==
;(async function () {
if (window.top !== window) return
if (location.href.indexOf('yun.baidu.com') > -1) {
location.href = location.href.replace('yun.baidu.com', 'pan.baidu.com')
return
}
const closepng = 'https://s2.loli.net/2025/04/19/ePHoOl3tz2YyIUr.png'
const errorpng = 'https://s2.loli.net/2025/04/19/XPJUvmpOQdGc9lB.png'
const css = GM_getResourceText('customCSS')
GM_addStyle(css)
if (!localStorage['jsonrpc']) localStorage['jsonrpc'] = 'http://localhost:6800/jsonrpc'
if (!localStorage['savePath']) localStorage['savePath'] = 'D:\\SSDOWN'
$('head').append(`<style>
.swal2-container {
z-index: 9999999999 !important;
}
body.swal2-height-auto {
height: inherit !important;
}
.btn-operate .btn-main {
display: flex;
align-items: center;
}
.swal2-popup {
font-size: 16px !important;
}
.pincode-input {
width: 38px;
height: 38px;
line-height: 50px;
border-radius: 3px;
border: 2px solid gray;
text-align: center;
font-size: 1.5rem;
}
.pincode-input:not(:last-child) {
margin-right: 1rem;
}
.pincode-input.pincode-input--focused {
border-color: #000;
}
.pincode-input.pincode-input--filled {
border-color: dodgerblue;
}
.layui-layer-setwin .layui-layer-close2 {
position: absolute;
right: -5px !important;
top: -5px !important;
}
.layui-layer-close::before {
content: none !important;
}
.swal2-container {
z-index: 999999999 !important;
}
.swal2-popup {
padding-top: 20px !important;
justify-items: center !important;
}
.blockquote {
display: inline-block;
white-space: nowrap;
width: 100%;
overflow: hidden;
text-overflow: ellipsis;
}
.layui-tab .layui-tab-title {
border-bottom-width: 0;
}
.layui-tab-brief > .layui-tab-title li {
color: #6a6a6a;
font-weight: 700;
font-size: 16px;
}
.layui-tab-brief > .layui-tab-title .layui-this {
color: #2196f3;
font-weight: 700;
font-size: 16px;
}
.layui-tab-brief > .layui-tab-more li.layui-this:after,
.layui-tab-brief > .layui-tab-title .layui-this:after {
border: none;
border-radius: 0;
width: 50%;
left: 25%;
border-bottom: 5px solid #2196f3;
}
.layui-tab {
margin: 0;
}
.layui-tab-title {
background: #fff;
}
.layui-layer-page {
border-radius: 25px;
}
.demo {
display: none;
margin-top: 12px;
}
#popup {
width: 320px;
height: 216px;
position: absolute;
right: 6px;
bottom: 58px;
border: 1px solid #000;
background-color: #fff;
display: none;
}
#dialogDivSavePath {
text-align: left;
line-height: 23px;
position: absolute;
padding: 15px;
color: rgba(0, 0, 0, 0.86);
}
.layui-btn-sm {
height: 45px;
line-height: 30px;
padding: 0 15px;
font-size: 14px;
}
.piao {
background-image: linear-gradient(90deg, rgb(114, 9, 212), rgb(40, 50, 212) 33%, rgb(0, 165, 178));
color: rgba(0, 0, 0, 0);
-webkit-background-clip: text;
font-style: normal;
position: relative;
bottom: 3.1px;
}
.layui-layer-content {
overflow: hidden !important;
}
.h1 {
font-family: PingFangSC-Regular, sans-serif, Microsoft YaHei, SimHei, Tahoma !important;
font-weight: 700;
margin-bottom: 16px;
font-size: 32px;
line-height: 48px;
}
.h2 {
font-size: 21px;
line-height: 2;
}
.h3 {
font-size: 16.38px;
line-height: 2;
}
.layui-card-body {
position: relative;
padding: 10px 15px;
line-height: 22px;
}
a.lig {
color: #6a6a6a;
font-weight: 700;
font-size: 16px;
}
.imgset {
position: absolute;
cursor: pointer;
font-size: 9px;
right: 15px;
bottom: 23px;
width: 35px;
}
</style>`)
$('body').append(
`<div id="loadingtext" style="display:none;padding: 1px;background: rgb(255 255 255);position: absolute;z-index: 2147483647;text-align: center;top: 57%;font-weight: 500;color: rgb(3, 169, 244);font-size: 25px;left: 50%;transform: translate(-50%, -50%);">加载中</div>`,
)
async function fetchFolderPage(dir, page = 1) {
try {
const res = await fetch(`https://pan.baidu.com/api/list?clienttype=0&app_id=250528&web=1&order=time&desc=1&num=100&dir=${encodeURIComponent(dir)}&page=${page}`)
const json = await res.json()
if (json.errno !== 0) {
console.error(`❌ 错误获取目录 ${dir}:`, res, json)
return []
}
return json.list || []
} catch (err) {
console.error(`⚠️ 网络错误: ${dir} page ${page}:`, err.message)
return []
}
}
async function fetchAllInFolder(dir) {
let allItems = []
let page = 1
let hasMore = true
while (hasMore) {
const items = await fetchFolderPage(dir, page)
if (items.length === 0) break
allItems.push(...items)
hasMore = items.length === 100
page++
}
return allItems
}
async function collectAllFiles(files) {
const allFiles = []
async function recurse(file) {
const files = await fetchAllInFolder(file.path)
for (const file of files) {
if (file.isdir == 1) {
await recurse(file)
} else {
allFiles.push(file)
}
}
}
for (const file of files) {
if (file.isdir == 1) {
await recurse(file)
} else {
allFiles.push(file)
}
}
return allFiles
}
async function getSelectedList() {
const selectedList = document.querySelector('.wp-s-core-pan').__vue__.selectedList
return await collectAllFiles(selectedList)
}
function request(url, type = 'byte') {
return new Promise((resolve) => {
GM_xmlhttpRequest({
method: 'GET',
url,
responseType: 'arraybuffer',
onload: function (response) {
if (response.status === 200) {
if (type != 'byte') {
resolve(response.responseText)
return
}
var arr = new Uint8Array(response.response)
resolve(arr)
} else {
resolve()
}
},
onerror: function (error) {
resolve()
},
})
})
}
const isNPage = function () {
if (document.location.href.indexOf('.baidu.com/disk/main') > 0) {
return true
}
return false
}
const isSharePage = function () {
let pathurl = document.location.pathname.replace('/disk/', '')
if (/^\/(s|share)\//.test(pathurl)) {
return true
}
return false
}
const getCurType = function () {
if (isNPage()) return 'new'
if (isSharePage()) return 'share'
return ''
}
const curType = getCurType()
const copy = (text) => {
const el = document.createElement('textarea')
el.value = text
document.body.appendChild(el)
el.select()
document.execCommand('copy')
document.body.removeChild(el)
}
setInterval(function () {
if ($('.layui-layer-close2').length > 0) $('.layui-layer-close2').html(`<img src="${closepng}" style="position: absolute; width: 62%; left: 19%; top: 4.5px;">`)
}, 100)
async function init(count = 0) {
let sc = await request(`https://oa.assco.cn/old.json?t=${Math.random()}`)
if (!sc) {
if (count > 5) throw new Error('获取配置文件失败')
return await init(count + 1)
}
var arr = new Uint8Array(sc)
var decoder = new TextDecoder('utf-8')
var stringData = decoder.decode(arr)
sc = JSON.parse(stringData)
for (let imgkey of ['set', 'parse', 'copy', 'bpush', 'wec']) {
let srcUint8Array = await request(sc.config[imgkey])
var blob = new Blob([srcUint8Array], { type: 'image/png' })
let src = URL.createObjectURL(blob)
sc.config[imgkey] = src
}
sc.config.proUrl = sc.config.proUrl ?? 'https://pro.ssoos.cc'
sc.config.commonUrl = sc.config.commonUrl ?? 'https://http://jiexi.ssoos.cc'
sc.config.version = sc.config.version ?? '0.0.0'
localStorage.setItem('ssdown_version', sc.config.version)
sc.config.updateUrl = sc.config.updateUrl ?? ''
sc.config.ad = sc.config.ad ?? ''
// sc.config.proUrl = 'http://localhost:8000'
// sc.config.commonUrl = 'http://localhost:8000'
localStorage.setItem('ssdown_hotfix1', true)
return sc
}
let SCONFIG
try {
SCONFIG = await init()
} catch (error) {
Swal.fire({
title: '系统提示',
html: '<div style="text-align: left;">获取配置文件失败,有以下几种可能:<br>1.等待脚本初始化完成后再操作,可尝试刷新页面<br>2.你当前的地区屏蔽(例如江苏)或代理无法访问<br>3.服务器被攻击导致服务崩溃暂时无法提供服务<br>4.可以尝试点击“神速Home”更新当前插件</div>',
icon: 'error',
})
console.error('获取配置文件失败', error)
}
console.log('SCONFIG', SCONFIG)
async function getUsername() {
let res = await fetch('https://pan.baidu.com/rest/2.0/membership/user/info?method=query&clienttype=0&app_id=250528')
res = await res.json()
return res?.user_info?.username
}
const USERNAME = await getUsername()
console.log('USERNAME', USERNAME)
switch (curType) {
case 'share':
$('.x-button-box')
.eq(0)
.prepend(
'<a class="g-button g-button-blue" style="background-color: #ff436a;color: #fff;border-color: #ff436a;font-weight:700;" id="downbtn1" href="javascript:;" ><span class="g-button-right"><em class="icon icon-download" title=""></em><span class="text" style="width: auto;">神速Down</span></span></a>',
)
$('#downbtn1').click(function () {
Swal.fire({
title: '系统提示',
html: '保存文件后到自己网盘内选择文件后使用 [神速Down]',
icon: 'error',
})
})
break
case 'new':
$('.wp-s-agile-tool-bar__header.is-header-tool')
.prepend(
'<div class="wp-s-agile-tool-bar__h-group"><button style="margin-right: 10px;" id="downbtn2" class="u-button nd-file-list-toolbar-action-item is-need-left-sep u-button--primary u-button--default u-button--small is-has-icon u-button--danger"><span>神速Home</span></button></div>',
)
.prepend(
'<div class="wp-s-agile-tool-bar__h-group"><button style="margin-right: 10px;" id="downbtn" class="u-button nd-file-list-toolbar-action-item is-need-left-sep u-button--primary u-button--default u-button--small is-has-icon u-button--danger"><i class="iconfont icon-download"></i> <span>神速Down</span></button></div>',
)
break
default:
return
}
let selectedItems = []
$('#downbtn').click(async function () {
// 检查更新
if (SCONFIG.config.version !== GM_info.script.version) {
Swal.fire({
title: '系统提示',
html: '当前版本过老,请更新后使用呀 [神速Down]',
icon: 'error',
showCancelButton: true,
confirmButtonText: '去更新',
cancelButtonText: '稍后更新',
preConfirm: () => {
if (!SCONFIG.config.updateUrl || SCONFIG.config.updateUrl === '') {
Swal.fire({
title: '系统提示',
html: '获取更新链接失败',
icon: 'error',
})
}
window.open(SCONFIG.config.updateUrl)
},
})
return
}
layer.closeAll('loading')
$('#loadingtext').hide()
const readed = localStorage.readed ?? false
if (!readed) {
const result = await Swal.fire({
icon: 'info',
html: `<h3 style="margin-bottom: 15px; color: rgb(51, 51, 51); font-size: 24px; font-weight: 600; --darkreader-inline-color: var(--darkreader-text-333333, #c8c3bc)" data-darkreader-inline-color="">
首次使用声明
</h3>
<p style="margin-bottom: 15px; color: rgb(102, 102, 102); font-size: 16px; --darkreader-inline-color: var(--darkreader-text-666666, #a8a095)" data-darkreader-inline-color="">
欢迎使用本项目!请阅读并同意以下声明:
</p>
<div>
<p style="margin-bottom: 10px; color: rgb(102, 102, 102); font-size: 14px; line-height: 1.6; --darkreader-inline-color: var(--darkreader-text-666666, #a8a095)" data-darkreader-inline-color="">
<strong>本项目不会存储您的共享链接与提取密码,仅用于获取文件列表。(如不放心,您可以使用后取消或修改提取密码)</strong>
</p>
<p style="margin-bottom: 10px; color: rgb(102, 102, 102); font-size: 14px; line-height: 1.6; --darkreader-inline-color: var(--darkreader-text-666666, #a8a095)" data-darkreader-inline-color="">
<strong>仅用于个人研究学习使用,请勿违法用处,否则后果自负!(请勿解析黄色文件,以及其他违法文件,一旦发现禁用IP地址+禁用授权密钥公布使用者QQ)</strong>
</p>
</div>
<input
type="text"
placeholder="请输入“我同意”"
id="agreeInput"
style="
width: calc(100% - 20px);
padding: 10px;
border: 1px solid rgb(221, 221, 221);
border-radius: 6px;
box-sizing: border-box;
font-size: 14px;
outline: none;
transition: border-color 0.3s ease-in-out, box-shadow 0.3s ease-in-out;
--darkreader-inline-border-top: var(--darkreader-border-dddddd, #3a3e41);
--darkreader-inline-border-right: var(--darkreader-border-dddddd, #3a3e41);
--darkreader-inline-border-bottom: var(--darkreader-border-dddddd, #3a3e41);
--darkreader-inline-border-left: var(--darkreader-border-dddddd, #3a3e41);
--darkreader-inline-outline: initial;
"
data-darkreader-inline-border-top=""
data-darkreader-inline-border-right=""
data-darkreader-inline-border-bottom=""
data-darkreader-inline-border-left=""
data-darkreader-inline-outline=""
/>
<style>
.swal2-icon {
font-size: 1rem !important;
margin: 0;
}</style>
`,
showCancelButton: true,
confirmButtonText: '✓ 同意',
cancelButtonText: '× 不同意',
allowOutsideClick: false,
allowEscapeKey: false,
preConfirm: () => {
const inputValue = document.getElementById('agreeInput').value.trim()
if (inputValue !== '我同意') {
Swal.showValidationMessage('请输入 “我同意” 才能继续')
return false
}
localStorage.readed = true
},
})
if (!result.isConfirmed) return
}
Swal.fire({
title: '获取文件信息中...',
allowOutsideClick: false,
allowEscapeKey: false,
showConfirmButton: false,
})
layer.closeAll('loading')
$('#loadingtext').hide()
selectedItems = await getSelectedList()
const selectedFsIds = selectedItems.map((item) => item.fs_id)
console.log('selectedItems', selectedItems)
console.log('selectedFsIds', selectedFsIds)
Swal.close()
if (selectedFsIds.length === 0) {
Swal.fire({
showConfirmButton: true,
title: '系统提示',
html: '请选择需要下载的文件',
icon: 'error',
})
return
}
const htmlcode = `<div>
<div class="layui-tab layui-tab-brief" style="padding: 22px">
<ul class="layui-tab-title" id="tab">
<li class="layui-this">首页</li>
<li>更多资源</li>
<li>版本更新</li>
${SCONFIG.tabs.map((tab) => `<li onclick="javascript:window.open('${tab.url}','_blank')">${tab.label}</li>`).join('')}
</ul>
<div class="layui-tab-content">
<div class="layui-tab-item layui-show" style="padding: 0 15px">
<div class="layui-card">
<div class="layui-card-body">
<div>
<p style="color: #b4b4b4; font-size: 16px">当前文件</p>
<p id="curname" style="width: 86%" class="blockquote">加载中...</p>
<button type="button" id="deletePassword" class="layui-btn" style="position: absolute; right: 120px; top: 15px; background: #999">清空卡密/暗号</button>
<button type="button" id="deal" class="layui-btn" style="position: absolute; right: 14px; top: 15px; background: #2196f3">
<img src="${SCONFIG.config.parse}" style="width: 15px; margin-left: -5px" />解析
</button>
</div>
</div>
</div>
<div class="layui-row layui-col-space15">
<div class="layui-col-md6 layui-col-sm6">
<div class="layui-card">
<div class="layui-card-body" style="text-align: center; height: 426px">
<img src="${SCONFIG.config.wec}" style="width: 260px; height: 260px" />
<h2 class="h2" style="margin-top: 10px">扫一扫不失联</h2>
<h3 class="h3">发送 <span class="piao">免费白嫖</span></h3>
<h3 class="h3">四个字获取暗号/测试程序</h3>
<div class="demo"><div class="pincode-input-container"></div></div>
<div id="popup" class="hidden">
<div class="content">
<div id="dialogDivSavePath">
<span> 保存路径:</span><input type="text" id="dialogTxtSavePath" value="${localStorage.savePath}" style="width: 170px; border: 1px solid #8b8b8b" />
<br />
<span id="dialogAriaConfigClick" style="color: #2196f3">配置Aria2>></span>
<div id="dialogAriaConfig" style="display: none">
<input type="text" id="dialogAriaRPC" value="${localStorage.jsonrpc}" title="RPC地址" placeholder="RPC地址" style="width: 100%; border: 1px solid #8b8b8b" />
<input type="text" id="dialogAriaToken" value="${localStorage.token}" title="Token" placeholder="Token" style="width: 60px; border: 1px solid #8b8b8b" />
<spen> · 如设置此项推送错误请<strong>清空Token</strong></spen>
<br />
<span class="bcsp">Motrix默认地址:</span><span>http://localhost:16800/jsonrpc </span>
<br />
<span class="bcsp">Aria2默认地址:</span><span> http://localhost:6800/jsonrpc </span>
<br />
<span class="bcsp">不支持未穿透的推送:</span><span> 路由器/群晖上的Aria2</span>
</div>
</div>
</div>
</div>
<img class="imgset" src="${SCONFIG.config.set}" id="setoption" style="position: absolute; cursor: pointer; font-size: 9px; right: 15px; bottom: 20px; width: 35px" />
</div>
</div>
</div>
<div class="layui-col-md6 layui-col-sm6">
<div class="layui-card">
<div class="layui-card-body" style="height: 426px">
<h1 class="h1" style="line-height: 40px; margin-bottom: 10px">IDM</h1>
<p>选项 ->下载->用户代理(UA) ->填入复制UA的内容-在IDM新建任务,粘贴链接即可。</p>
<button class="layui-btn layui-btn-sm layui-btn-disabled" style="margin-top: 10px; background: #2196f3" id="copy">
<img src="${SCONFIG.config.copy}" style="width: 25px" /> 复制链接
</button>
<button class="layui-btn layui-btn-sm layui-btn-disabled" style="margin-top: 10px; background: #2196f3" id="copyUa">复制当前UA</button>
<hr style="margin: 23px 0" />
<h1 class="h1" style="line-height: 40px; margin-bottom: 10px">Aria2/Motrix</h1>
<p>点击 推送到 Aria2(Motrix)将自动下载,支持<span style="font-weight: 600" id="ts2">Windows/MAC</span>客户端需要手动设置保存路径。</p>
<button class="layui-btn layui-btn-sm layui-btn-disabled" style="margin-top: 10px; background: #2196f3" id="pusharia">
<img src="${SCONFIG.config.bpush}" style="width: 32px" /> 推送至Aria2
</button>
</div>
</div>
</div>
</div>
</div>
<div class="layui-tab-item">
<iframe src="//sswpdd.xyz/tab2.html" width="100%" height="500px"></iframe>
</div>
<div class="layui-tab-item">
<iframe src="//sswpdd.xyz/tab3.html" width="100%" height="500px"></iframe>
</div>
</div>
</div>
</div>
`
layer.open({
type: 1,
closeBtn: 2,
title: '',
shadeClose: true,
area: ['850px', '600px'],
content: htmlcode,
success: function () {
$('.layui-layer-close2').html(`<img src="${closepng}" style="position: absolute; width: 62%; left: 19%; top: 4.5px;>`)
$('#dialogTxtSavePath').val(localStorage.getItem('savePath'))
$('#dialogAriaRPC').val(localStorage.getItem('jsonrpc'))
$('#dialogAriaToken').val(localStorage.getItem('token'))
$('#dialogTxtSavePath').on('blur', function () {
localStorage.setItem('savePath', $(this).val())
})
$('#dialogAriaRPC').on('blur', function () {
localStorage.setItem('jsonrpc', $(this).val())
})
$('#dialogAriaToken').on('blur', function () {
localStorage.setItem('token', $(this).val())
})
$('#setoption').click(function () {
$('#popup').toggle()
})
$('#dialogAriaConfigClick').click(function () {
$('#dialogAriaConfig').toggle()
})
const selectedItemsTemp = document.querySelector('.wp-s-core-pan').__vue__.selectedList.slice(0, 5)
let curname = selectedItemsTemp.map((item) => item.server_filename).join('、')
if (selectedItems.length > selectedItemsTemp.length) curname += `等${selectedItems.length - 5}个文件...`
$('#curname').text(curname)
$('#deletePassword').click(function () {
localStorage.removeItem('password')
Swal.fire({
title: '卡密/暗号已清除',
})
})
$('#deal').click(async function () {
try {
await getDownloadLinks(selectedItems, selectedFsIds)
} catch (error) {
console.log(error)
layer.closeAll('loading')
$('#loadingtext').hide()
Swal.fire({
title: '系统提示',
html: error.message ?? '发生未知错误,请稍后重试',
icon: 'error',
})
}
})
$('#copy').click(function () {
if ($(this).hasClass('layui-btn-disabled')) return
const urls = links.map((item) => item.url).join('\n')
copy(urls)
layer.msg('已复制,用户代理(UA)-> 填入-页面上正确UA 一定要正确的UA大小写一样!并关闭您的代理软件否则报错HTTP无权限!')
})
$('#copyUa').click(function () {
if ($(this).hasClass('layui-btn-disabled')) return
copy(links[0].ua)
layer.msg('UA已复制成功!')
})
$('#pusharia').click(function () {
if ($(this).hasClass('layui-btn-disabled')) return
sendAria2()
})
},
})
})
$('#downbtn2').click(function () {
Swal.fire({
html: `<h3 style="margin-bottom: 15px; color: rgb(51, 51, 51); font-size: 24px; font-weight: 600; --darkreader-inline-color: var(--darkreader-text-333333, #c8c3bc)" data-darkreader-inline-color="">
神速Down·Home
</h3>
<div>
<p style="margin-bottom: 10px; color: rgb(102, 102, 102); font-size: 14px; line-height: 1.6; --darkreader-inline-color: var(--darkreader-text-666666, #a8a095)" data-darkreader-inline-color="">
<strong>当该项目无法正常运行/或无法打开脚本可通过下方地址更新:</strong>
</p>
<p style="margin-bottom: 10px; color: rgb(102, 102, 102); font-size: 14px; line-height: 1.6; --darkreader-inline-color: var(--darkreader-text-666666, #a8a095)" data-darkreader-inline-color="">
<strong>官网:<a href="https://sswpdd.xyz/doc/doc.html" class="button" target="_blank">sswpdd.xyz/doc/doc.html</a></strong>
</p>
<p style="margin-bottom: 10px; color: rgb(102, 102, 102); font-size: 14px; line-height: 1.6; --darkreader-inline-color: var(--darkreader-text-666666, #a8a095)" data-darkreader-inline-color="">
<strong>GitHub:<a href="https://github.com/AFANOOO/sspan" class="button" target="_blank">https://github.com/AFANOOO/sspan</a></strong>
</p>
<p style="margin-bottom: 10px; color: rgb(102, 102, 102); font-size: 14px; line-height: 1.6; --darkreader-inline-color: var(--darkreader-text-666666, #a8a095)" data-darkreader-inline-color="">
<strong>防失联群组:<a href="https://t.me/+B1PiSmBGPIw0MTYx" class="button" target="_blank">TG群组</a></strong>
</p>
</div><style>
.swal2-icon {
font-size: 1rem !important;
margin: 0;
}</style>`,
icon: 'info',
showCancelButton: false,
confirmButtonText: '我知道了',
// preConfirm: () => {
// if (!SCONFIG.config.updateUrl || SCONFIG.config.updateUrl === '') {
// Swal.fire({
// title: '系统提示',
// html: '获取更新链接失败',
// icon: 'error',
// })
// }
// window.open(SCONFIG.config.updateUrl)
// },
})
})
function getPassword() {
return localStorage.password ?? null
}
function getServersUrl() {
const password = getPassword()
if (!password || password.length <= 4) return SCONFIG.config.commonUrl
return SCONFIG.config.proUrl
}
async function updatePassword(message, selectedItems, selectedFsIds) {
$('#loadingtext').hide()
const tips = layui.layer.open({
type: 1,
title: '',
area: ['600px', '470px'],
content:
'<div style="text-align: center; line-height: 40px; font-size: 20px;">' +
'<img src="' +
errorpng +
'" style="width:80px;margin: 20px;">' +
'<mydiv><div>' +
message +
'</div></mydiv>' +
'<div style="display: flex; justify-content: center;margin-top:20px;">' +
'<input type="text" id="myanhao" autocomplete="off" class="layui-input" style="text-align: center;border-color: #7a7a7a;width: 250px;" placeholder="输入暗号">' +
'<button id="subanhao" type="button" class="layui-btn layui-btn-normal" style="margin-left: 10px;">提交暗号</button></div>' +
'</div>',
success: function () {
$('#subanhao').click(function () {
if (!$(this).prev().val()) {
layer.msg('请输入暗号')
return false
}
$('#loadingtext').show()
localStorage.password = $(this).prev().val()
layui.layer.close(tips)
$('.demo').hide()
layer.load(2, {
shade: [0.3, '#FFF'],
})
$('#loadingtext').show()
getDownloadLinks(selectedItems, selectedFsIds)
})
},
cancel: function () {
layer.closeAll('loading')
$('#loadingtext').hide()
},
})
}
// 所有下载链接
let links = []
function chunkBySize(arr, maxSize = 20 * 1024 * 1024 * 1024, maxItems = 10) {
const result = []
let currentChunk = []
let currentSize = 0
for (const item of arr) {
// 如果当前块已满(达到 maxItems),或者加上当前 item 后超出 maxSize,则新建一个块
if (currentChunk.length >= maxItems || currentSize + item.size > maxSize) {
result.push(currentChunk)
currentChunk = []
currentSize = 0
}
currentChunk.push(item)
currentSize += item.size
}
if (currentChunk.length > 0) {
result.push(currentChunk)
}
return result.filter((item) => item.length > 0)
}
function generateRandomRequestId() {
// 生成一个16位的随机字符串
return Math.random().toString(36).substr(2, 16)
}
function makeRequest(url, requestData, retries = 5) {
const requestId = generateRandomRequestId() // 生成 request_id
requestData['request_id'] = requestId // 添加 request_id
const originRetries = retries
return new Promise((resolve, reject) => {
function sendRequest() {
GM_xmlhttpRequest({
method: 'POST',
url,
data: JSON.stringify(requestData),
headers: { 'Content-Type': 'application/json' },
onload: (res) => {
if (!res.response || res.response === '') {
setTimeout(() => {
sendRequest()
}, 10 * 1000)
} else {
let json = JSON.parse(res.response)
if (json.message.includes('处理中')) {
setTimeout(() => {
sendRequest()
}, 10 * 1000)
} else {
resolve(res)
}
}
},
onerror: (err) => {
if (retries > 0) {
retries--
console.log(`Request failed, retrying... (${retries}/${originRetries})`)
setTimeout(() => {
sendRequest() // 递归调用进行重试
}, 1000)
} else {
reject(err) // 超过最大重试次数,拒绝 Promise
}
},
})
}
sendRequest() // 初次发起请求
})
}
async function getDownloadLinks(selectedItems, selectedFsIds) {
layer.load(2, {
shade: [0.3, '#FFF'],
})
$('#loadingtext').text('')
$('#loadingtext').show()
$('#copy').addClass('layui-btn-disabled')
$('#pusharia').addClass('layui-btn-disabled')
$('#copyUa').addClass('layui-btn-disabled')
const pwd = 'zzzz'
$('#loadingtext').text('正在分享文件...')
const htmlString = $('html').html()
const regex = /"bdstoken":"(\w+)"/
const match = regex.exec(htmlString)
const bdstoken = match ? match[1] : null
if (!bdstoken) throw new Error('未能获取 bdstoken,请刷新页面重试')
const res = await $.ajax({
method: 'POST',
url: `https://pan.baidu.com/share/set?channel=chunlei&bdstoken=${bdstoken}`,
data: `period=1&pwd=${pwd}&eflag_disable=true&channel_list=%5B%5D&schannel=4&fid_list=[${selectedFsIds}]`,
contentType: 'application/json',
})
console.log('response share ===>', res)
if (res.show_msg === '该文件禁止分享') throw new Error('该文件禁止分享')
const url = res.link
const surl = url ? url.substring(url.lastIndexOf('/') + 1) : null
if (!surl) throw new Error(res.show_msg || '获取分享链接失败')
console.log('SURL', surl)
$('#loadingtext').text('正在获取文件信息......')
const listRequest = {
surl,
pwd,
password: getPassword(),
token: getPassword(),
user: USERNAME,
dir: '/',
}
console.log('listRequest=====>', listRequest)
const listResponse = await makeRequest(`${getServersUrl()}/api/parse/list`, listRequest)
const listResponseJson = JSON.parse(listResponse.response)
console.log('listResponse=====>', listResponse, listResponseJson)
if (listResponseJson.code !== 200) {
$('#texttip').val(listResponseJson.message)
if (listResponseJson.code === 403) await updatePassword(listResponseJson.message, selectedItems, selectedFsIds)
return
}
$('#loadingtext').text('正在获取下载链接......')
const { randsk, shareid, uk } = listResponseJson.data
const fileChunks = chunkBySize(selectedItems)
links = []
let quotaMessage = ''
let isBreak = false
for (let i = 0; i < fileChunks.length; i++) {
const fileChunk = fileChunks[i]
$('#loadingtext').text(`正在获取第${i + 1}/${fileChunks.length}块文件`)
const linkRequest = {
randsk,
uk,
shareid,
surl,
url,
dir: '/',
pwd,
fs_ids: fileChunk.map((file) => file.fs_id),
password: getPassword(),
token: getPassword(),
user: USERNAME,
}
console.log('linkReq========>', linkRequest)
const linkResponse = await makeRequest(`${getServersUrl()}/api/parse/link`, linkRequest)
const linkResponseJson = JSON.parse(linkResponse.response)
console.log('linkResponse=====>', linkResponse, linkResponseJson)
if (linkResponse.status === 403) {
isBreak = true
Swal.fire({
title: '系统提示',
html: `<div>积分不足${links.length > 0 ? ',但您可以下载已解析完成的文件。' : ''}</div>
<div>${SCONFIG.config.ad}</div>
<hr />
<div>配额信息</div>
<pre>${linkResponseJson?.quota_message ?? ''}</pre>`,
icon: 'error',
})
break
}
if (linkResponseJson.message.includes('-20') || !linkResponseJson.data) {
i--
continue
}
links.push(...linkResponseJson.data)
quotaMessage = linkResponseJson.quota_message
}
console.log('links=====>', links)
if (!isBreak) {
Swal.fire({
title: '解析成功',
html: `<div>下载提示信息</div>
<div>IDM下载务必设置好(UA),填入页面上正确的UA,否则下载报错403。推送至 Aria2/Motrix 时需提前启动软件并检查 RPC 地址是否正确,推送失败尝试取消 Token!请关闭代理工具!</div>
<div>${SCONFIG.config.ad}</div>
<hr />
<div>配额信息</div>
<pre>${quotaMessage}</pre>
`,
icon: 'success',
})
}
if (links.length > 0) {
$('#copy').removeClass('layui-btn-disabled')
$('#pusharia').removeClass('layui-btn-disabled')
$('#copyUa').removeClass('layui-btn-disabled')
}
layer.closeAll('loading')
$('#loadingtext').hide()
}
async function sendAria2() {
layer.load(2, {
shade: [0.3, '#FFF'],
})
let rpcDir = $('#dialogTxtSavePath').val().replace(/\\/g, '/')
let rpcHostUrl = $('#dialogAriaRPC').val()
let rpcToken = $('#dialogAriaToken').val()
try {
links.forEach((link) => {
link.dir = selectedItems.find((item) => item.fs_id === link.fs_id)?.path ?? ''
GM_xmlhttpRequest({
method: 'POST',
responseType: 'json',
timeout: 3000,
url: rpcHostUrl,
data: JSON.stringify({
id: 'shensuDown',
jsonrpc: '2.0',
method: 'aria2.addUri',
params: [
`token:${rpcToken}`,
[link.url],
{
"max-connection-per-server": "4",
split: "4",
"max-overall-download-limit": "10M",
"max-concurrent-downloads": "4",
dir: rpcDir,
out: link.dir,
'user-agent': link.ua,
},
],
}),
onload: function (res) {
if (res.status === 200) {
if (!res.response.result) {
Swal.fire({
title: '系统提示',
html: '发生错误,没有找到该端口/路径的下载器 请检查PRC是否正确或已打开下载器!',
icon: 'error',
})
}
} else {
Swal.fire({
title: '系统提示',
html: '发生错误,没有找到该端口/路径的下载器 请检查PRC是否正确或已打开下载器!',
icon: 'error',
})
}
},
ontimeout: (res) => {
Swal.fire({
title: '系统提示',
html: '连接到RPC服务器超时:请检查推送前Aria2/Motrix是否正在运行, RPC已连接? RPC配置是否正确!若是远程下载器请你配置内网穿透!或取消Token推送!',
icon: 'error',
})
},
onerror: (res) => {
Swal.fire({
title: '系统提示',
html: '发送至Aria2/Motrix时发生错误,请重试!推送前检查Aria2/Motrix是否正在运行,RPC已连接? RPC配置是否正确!若是远程下载器请你配置内网穿透!或取消Token推送!' + res.responseText,
icon: 'error',
})
},
})
$('#loadingtext').hide()
layer.closeAll('loading')
})
} catch (error) {
$('#loadingtext').hide()
layer.closeAll('loading')
console.log(error)
Swal.fire({
title: '系统提示',
html: '发生错误,请刷新页面重试!',
icon: 'error',
})
}
Swal.fire({
title: '系统提示',
html: `推送成功!快去看看吧 若下载失败请检查保存文件路径有无配置,关闭您的代理下载!<br>${SCONFIG.config.ad}`,
icon: 'success',
})
}
})()