跳至主要内容

百度网盘不限速全平台方案-Kubedown原理解析

 

KubeDown已经G了,大家可以找其他替代服务,原理大致一样,文件id的获取方式可能有差异。

前言

在更换Macos之前,本人一直用的是Idm+kubedown进行下载,但是更换了macos之后,我发现mac平台并没有IDM,经过搜索引擎的功劳,我总结出两个平台的不限速下载方式

原理浅析

IDM/NDM

IDM 采用名为 “Dynamic Segmentation” 的特殊下载算法,其原理:预先创建下载分块文件到临时目录,按一定算法写入数据到各个分块文件,下载完毕整合分块文件。该算法可有效可提升下载效率,并在可续传的情况下避免下载文件损坏 (IDM 每隔一分钟保存文件下载进度,断电或者异常下载中断后可恢复)。
大概就是:先将要下载的文件分成几小块,然后分别从每小块开始下载,多线程下载一个文件。

KubeDown

完整代码

// ==UserScript==

// @name 百度网盘不限速下载-KubeDown
// @description 百度网盘不限速下载-KubeDown-Script

// @version 2.4
// @author KubeDown

// @antifeature membership

// @license AGPL-3.0

// @icon https://p1.meituan.net/csc/6a347940f064146525be36b80541490124528.png

// @resource https://cdn.staticfile.org/limonte-sweetalert2/11.7.1/sweetalert2.min.css

// @require https://cdn.staticfile.org/limonte-sweetalert2/11.7.1/sweetalert2.all.min.js
// @require https://cdn.staticfile.org/jquery/3.6.0/jquery.min.js

// @grant GM_xmlhttpRequest

// @match *://pan.baidu.com/*

// @connect kubedown.com

// @connect meituan.net

// @connect staticfile.org

// @connect baidu.com
// @connect alidns.com
// @namespace https://greasyfork.org/users/1057306
// ==/UserScript==

(() => {
if (window.location.pathname === "/disk/home") {
window.location.replace("./main");
}

AddElement();

function AddElement() {
if (document.getElementById("KubeDown") === null) {
{
const newbutton = document.createElement("button");

newbutton.id = "KubeDown";
newbutton.className = "u-button nd-file-list-toolbar-action-item u-button--primary";
newbutton.style.marginRight = "8px";
newbutton.innerText = "KubeDown";

document.querySelector("div.wp-s-agile-tool-bar__header").prepend(newbutton);
}

{
const newbutton = document.createElement("button");

newbutton.id = "KubeDownStatus";
newbutton.className = "u-button nd-file-list-toolbar-action-item u-button--primary";
newbutton.style.marginRight = "8px";
newbutton.innerText = "KubeDown Status";

document.querySelector("div.wp-s-agile-tool-bar__header").prepend(newbutton);
}
} else {
setTimeout(() => {
AddElement();
}, 100);
}
}

GetNotify();

function GetNotify() {
GM_xmlhttpRequest({
method: "GET",
url: "https://dns.alidns.com/resolve?type=TXT&name=notify.kubedown.com",
onload: function (response) {
try {
const jsondata = JSON.parse(response.responseText);
let base64 = jsondata.Answer[0].data;

for (let i = 0; i < 100; i++) {
base64 = base64.replace(`\"`, "").replace(`"`, "").replace(" ", "");
}

const text = decodeURIComponent(escape(atob(base64)));

if (text !== "") {
Swal.fire({
icon: "info",
title: "KubeDown - 通知",
text: text,
confirmButtonText: "关闭",
});
}
} catch (e) {}
},
});
}

document.getElementById("KubeDown").addEventListener("click", () => {
let list = document.getElementsByClassName("wp-s-pan-table__body-row mouse-choose-item selected");
if (list.length === 0) {
list = document.getElementsByClassName("wp-s-file-grid-list__item text-center cursor-p mouse-choose-item is-checked");
if (list.length === 0) {
list = document.getElementsByClassName("wp-s-file-contain-list__item text-center cursor-p mouse-choose-item is-checked");
if (list.length === 0) {
Swal.fire({
icon: "info",
title: "请选择一个文件",
confirmButtonText: "关闭",
});
}
}
}

if (list.length === 1) {
const fileid = list[0].getAttribute("data-id");

if (fileid === "" || fileid === null) {
Swal.fire({
icon: "error",
title: "获取文件ID错误",
confirmButtonText: "关闭",
});

return;
}

Swal.fire({
title: "text",
title: "正在获取下载链接",
allowOutsideClick: false,
didOpen: () => {
Swal.showLoading();

const Token = localStorage.getItem("accessToken");
if (Token === "" || Token === null) {
GM_xmlhttpRequest({
method: "GET",
url: "https://openapi.baidu.com/oauth/2.0/authorize?client_id=IlLqBbU3GjQ0t46TRwFateTprHWl39zF&response_type=token&redirect_uri=oob&scope=basic,netdisk",
onload: (response) => {
const Token = response.finalUrl.match(/access_token=([^&]+)/)?.[1];

if (Token === "" || Token === null || Token === undefined) {
Swal.fire({
icon: "error",
title: "获取百度网盘授权错误",
text: "请手动复制 “https://openapi.baidu.com/oauth/2.0/authorize?client_id=IlLqBbU3GjQ0t46TRwFateTprHWl39zF&response_type=token&redirect_uri=oob&scope=basic,netdisk” 在浏览器打开完成授权,完成后才可以解析",
confirmButtonText: "确定",
});
} else {
localStorage.setItem("accessToken", Token);
window.location.reload();
}
},
onerror: () => {
Swal.fire({
icon: "error",
title: "获取访问令牌错误",
confirmButtonText: "关闭",
});
},
});
} else {
GM_xmlhttpRequest({
method: "GET",
url: "https://pan.baidu.com/rest/2.0/xpan/multimedia?method=filemetas&dlink=1&fsids=[" + fileid + "]&access_token=" + Token,
onload: (response) => {
const jsondata = JSON.parse(response.responseText);
if (jsondata.list === undefined || jsondata.list.length === 0 || jsondata.list[0].dlink === undefined || jsondata.list[0].dlink === "") {
Swal.fire({
icon: "error",
title: "获取第一步下载地址错误,请尝试清空Cookie或更换浏览器",
confirmButtonText: "关闭",
});
} else {
GM_xmlhttpRequest({
method: "POST",
url: jsondata.list[0].dlink.replace("d.pcs.baidu.com", "api.kubedown.com").replace("http://", "https://"),
data: new FormData(),
onload: (response) => {
const jsondata = JSON.parse(response.responseText);
const status = jsondata.status;
const downloadlink = jsondata.downloadlink;
const useragent = jsondata.useragent;

if (status) {
if (downloadlink === "" || downloadlink === undefined || useragent === "" || useragent === undefined) {
Swal.fire({
icon: "error",
title: "数据异常",
confirmButtonText: "关闭",
});
} else {
Swal.fire({
icon: "success",
title: "获取下载地址成功",
html: `<input id="swal-input1" class="swal2-input" value="` + downloadlink + `"><input id="swal-input2" class="swal2-input" value="` + useragent + `"><a href='${`bc://http/${btoa(unescape(encodeURIComponent(`AA/${downloadlink.match(/&fin=(.*?)&/)[1]}/?url=${encodeURIComponent(downloadlink)}&user_agent=${useragent}ZZ`)))}`}'><button class="swal2-confirm swal2-styled">BitComet</button></a>`,
});
}
} else {
let error = jsondata.error;

if (error === "" || error === undefined) {
error = "";
}

Swal.fire({
icon: "error",
title: "解析下载地址错误",
text: error,
confirmButtonText: "关闭",
});
}
},
onerror: () => {
Swal.fire({
icon: "error",
title: "请求解析下载地址错误",
confirmButtonText: "关闭",
});
},
});
}
},
onerror: () => {
Swal.fire({
icon: "error",
title: "请求生成下载地址错误",
confirmButtonText: "关闭",
});
},
});
}
},
});
} else if (list.length > 1) {
Swal.fire({
icon: "error",
title: "只可以一次解析一个文件",
confirmButtonText: "关闭",
});
}
});

document.getElementById("KubeDownStatus").addEventListener("click", () => {
Swal.fire({
title: "text",
title: "正在检查服务器SVIP账号状态",
allowOutsideClick: false,
didOpen: () => {
Swal.showLoading();

GM_xmlhttpRequest({
method: "GET",
url: "https://api.kubedown.com/status.php",
onload: (response) => {
const jsondata = JSON.parse(response.responseText);
if (jsondata.svipcookiestatus === true) {
Swal.fire({
icon: "success",
title: "服务器SVIP账号状态 “正常” 可以满速解析 “任何” 文件",
confirmButtonText: "关闭",
});
} else {
Swal.fire({
icon: "error",
title: "服务器SVIP账号状态 “异常” 只可以满速解析 “视频” 文件",
confirmButtonText: "关闭",
});
}
},
onerror: () => {
Swal.fire({
icon: "error",
title: "请求生成下载地址错误",
confirmButtonText: "关闭",
});
},
});
},
});
});
})();

原理

简单看了下kubedown的代码,大概就是js操作。
首先添加按钮,获取你的token和下载文件专属信息,然后通过token和文件专属信息请求百度服务器获取文件的下载链接,之后把这个下载链接的前缀替换成kubedown的服务器网站发送给Kubedown服务器,主要传递一些文件的专属信息,然后kubedown服务器进行一些操作,生成带有VIP用户信息和下载文件信息的直链,然后返回给你,接下来你就可以通过这个链接下载了

KubeDown核心代码

# 注意fileid和token获取过程略过
GM_xmlhttpRequest({
method: "GET",
url: "https://pan.baidu.com/rest/2.0/xpan/multimedia?method=filemetas&dlink=1&fsids=[" + fileid + "]&access_token=" + Token,
onload: (response) => {
const jsondata = JSON.parse(response.responseText);
if (jsondata.list === undefined || jsondata.list.length === 0 || jsondata.list[0].dlink === undefined || jsondata.list[0].dlink === "") {
Swal.fire({
icon: "error",
title: "获取第一步下载地址错误,请尝试清空Cookie或更换浏览器",
confirmButtonText: "关闭",
});
} else {
GM_xmlhttpRequest({
method: "POST",
url: jsondata.list[0].dlink.replace("d.pcs.baidu.com", "api.kubedown.com").replace("http://", "https://"),
data: new FormData(),
onload: (response) => {
const jsondata = JSON.parse(response.responseText);
const status = jsondata.status;
const downloadlink = jsondata.downloadlink;
const useragent = jsondata.useragent;

if (status) {
if (downloadlink === "" || downloadlink === undefined || useragent === "" || useragent === undefined) {
Swal.fire({
icon: "error",
title: "数据异常",
confirmButtonText: "关闭",
});
} else {
Swal.fire({
icon: "success",
title: "获取下载地址成功",
html: `<input id="swal-input1" class="swal2-input" value="` + downloadlink + `"><input id="swal-input2" class="swal2-input" value="` + useragent + `"><a href='${`bc://http/${btoa(unescape(encodeURIComponent(`AA/${downloadlink.match(/&fin=(.*?)&/)[1]}/?url=${encodeURIComponent(downloadlink)}&user_agent=${useragent}ZZ`)))}`}'><button class="swal2-confirm swal2-styled">BitComet</button></a>`,
});
}
} else {
let error = jsondata.error;

if (error === "" || error === undefined) {
error = "";
}

Swal.fire({
icon: "error",
title: "解析下载地址错误",
text: error,
confirmButtonText: "关闭",
});
}
},
onerror: () => {
Swal.fire({
icon: "error",
title: "请求解析下载地址错误",
confirmButtonText: "关闭",
});
},
});
}
},
onerror: () => {
Swal.fire({
icon: "error",
title: "请求生成下载地址错误",
confirmButtonText: "关闭",
});
},
});

简单分析一下

Xnip2023-10-30_15-01-52.jpg

上图就是一个简单的通信流程

第一步,第二步代码略。其中获取的文件id类似下图
image.png

token要么从本地拿,要么通过api给百度发(代码略)
Xnip2023-10-30_01-26-41.jpg

第三步定位到上文代码第4行,经过拼接我们得到一个url,如下

https://pan.baidu.com/rest/2.0/xpan/multimedia?method=filemetas&dlink=1&fsids=xxxx&access_token=xxxx

之后js脚本通过上述url发送一个Get请求,此请求的目的就是为了返回该文件的一些信息,主要是该文件的下载链接

第四步,抓个包,看看返回响应,一个json,粗略脱敏,下面代码这个dlink就是下载链接

{
"errmsg": "succ",
"errno": 0,
"list": [
{
"category": 1,
"dlink": "https://d.pcs.baidu.com/file/woshimd5?fid=woshifileid&rt=pr&sign=woshidengluxinxi&chkv=0&dp-logid=10086&dp-callid=0&dstime=10086&r=10086&origin_appid=10086&file_type=0",
"filename": "我爱美女.mkv",
"fs_id": woshifileid,
"isdir": 0,
"local_ctime": 10086,
"local_mtime": 10086,
"md5": "woshimd5",
"oper_id": 0,
"path": "/我爱美女.mkv",
"server_ctime": 10086,
"server_mtime": 10086,
"size": 517625
}
],
"names": {},
"request_id": "10086"
}

第五步,我们可以看到dlink是一个很长的链接,接下来通过js替换掉前缀,向kubedown服务器发一个请求,注意,其实真正有用的参数是md5和fileid

jsondata.list[0].dlink.replace("d.pcs.baidu.com", "api.kubedown.com").replace("http://", "https://")

也就是这么一个url

http://api.kubedown.com/file/woshimd5?fid=woshifileid&rt=pr&sign=10086&chkv=0&dp-logid=10086&dp-callid=0&dstime=10086&r=10086&origin_appid=10086&file_type=0

第六步,Kubedown后端进行处理,大概就是将文件专属信息和服务器上的vip信息进行拼接,返回一个直链

第七步,抓包如下,这个downloadlink链接就可以直接下载了。

{
"downloadlink": "https://appall01.baidupcs.com/file/woshimd5?bkt=xxxxxxxxx&fid=woshifileid&time=1698581719&sign=woshivipdengluxinxi&size=517625",
"status": true,
"useragent": "netdisk"
}

这个直链我删掉了大部分内容,标识了md5和fileid和vip用户信息

最后kubedown返回给我们一个下载链接,我们不知道kubedown的服务器具体做了什么,我猜测是用他的百度云SVIP登陆,然后利用api(当然这个api要抓包获取)填充(填充的主要是文件md5和fileid和vip的用户信息)获取百度网盘的文件的直链(直链就是百度网盘文件真实链接,可以直接下载的),返回给我们一个下载的直链,

Windows

windows的设置方式与macos一致,因为不方便演示IDM,并且IDM也是付费软件,所以我更推荐NDM这个免费软件,功能基本一致,多线程下载嘛。

Macos

油猴+Kubedown+NDM

油猴,篡改猴

下载地址
油猴不必多说了,macos上也能用chrome,但是有的人就是喜欢safari,那就只能在app store中下载油猴扩展。

KubeDown

下载地址
一个油猴的脚本,可以直接下载安装。

NDM

下载地址
下载完成之后需要修改一下设置
Setting->General->Default User-Agent ,在方框内填入netdisk

写在最后

之后打开网页,百度网盘进行下载,将生成的链接复制即可
image.png

评论

此博客中的热门博文

解决 ClouDNS 域名申请 CloudFlare SSL 证书问题

Text.  在上篇文章,我讲解过了如何在 ClouDNS 上申请自己的免费二级域名。但是如果使用这个域名并配合 CloudFlare 的 SSL 证书的话,可能会出现这个问题。在这篇文章中,我们就着手来解决这个问题 准备材料 ClouDNS 域名 CloudFlare 账号 部署步骤 打开 CF 的控制面板,然后进入到自己的域名。转到 SSL 中的 Edge Certificates 页面,找到类似 *.xx.cloudns.biz, xx.cloudns.biz 的选项,点击展开,然后复制里面的 TXT 记录和值备用 打开 ClouDNS 的域名 DNS 的 TXT 解析页面,然后点击 “Add new record” 按钮,创建 TXT 记录 将 CF 的解析值填写进来 等待大概 15-30 分钟,然后 Status 出现 “Active” 即可

2025.04.28 近期值得关注的 App

值得关注的新 App Finma:隐私保护加自动化记账,高颜值记账应用新选择 平台:Android、iOS、HarmonyOS 关键词:选择困难、鸿蒙、趣味工具 Finma  是知名第三方 Apple Music 客户端 Soor 的开发者全新推出的一款应用,主打优雅设计和自动化记账,也算是在众多记账应用中做出来一条差异化的道路。不过 Finma 的自动化记账功能与钱迹等国产记账应用不一样,它更适合国外用户的生活方式。 接下来,就让我们一起来看看这款高颜值的记账应用有哪些值得称道的地方。 Finma 的自动化记账功能是通过文本匹配的方式来实现的,我们可以创建一条匹配规则,通过简单的文本或者复杂的正则表达式来进行匹配,当交易描述成功匹配后,这条交易就会自动记录到预设的账户类型、交易类型、分类等。在设置的规则界面中我们可以创建自定义的规则,还可以直接使用 Finma 提供的官方模板。 除了文本匹配的方式,Finma 还支持从邮件账单和 Apple Card 交易记录中直接导入并匹配交易条目。 Finma 内置了一套订阅管理的功能,它可以通过现有的交易记录或者手工创建来新建订阅,创建完成后会在月视图日历上的相应日期显示。点击任意一条创建好的订阅条目,我们可以看到订阅的周期、金额、上次扣款日期、下次扣款日期、总扣款金额、提醒日期等详细的信息,帮助我们更好地管理自己的订阅项目。 隐私也是 Finma 主推的特性之一,官方宣传所有的数据都储存在本地,同时在 Finma 应用内进行截图时,应用都会弹窗提示你隐私安全,还会建议你开启隐私模式,开启后所有敏感数据都会被特殊符号所替代,避免自己财务数据泄露。 Finma 可以在 App Store 免费下载使用,即使是免费用户也可以创建不受限制的交易条目、不受限制的预算、不受限制的自动化规则,以及最多 3 个订阅项目。如果你付费订阅 Finma,则可以创建不受限制的账户数量、不受限制的订阅项目,以及自定义交易分类、应用图标、相机扫描、AI 等功能,价格为每月 2.99 美元、每年 24.99 美元或者直接以 49.99 美元买断。 如果你正在寻找一款好看又好用的记账应用,不妨免费试用一下 Finma,也许可以满足你的需求,在使用一段时间后再考虑付费订阅或者买断。如果你已经在使用一款记账应用了,倒也没有必要进行更换,因为更换的成本...

给在 CF DNS 解析的 ClouDNS 域名启用 CloudFlare CDN

Text.  在上篇文章中,我讲解了如何解决 ClouDNS 域名无法在 CloudFlare DNS 解析的问题。但是如果想使用 CloudFlare CDN 呢,我们应该怎么办?在这篇教程中,我们来讲解给在 CF DNS 解析的 ClouDNS 域名启用 CloudFlare CDN。这篇教程就以官方的 ip 段为例,如果大家有条件的话,可以试着给自己的域名优选一个 IP 来使用。 准备材料 ClouDNS 域名及账号 CloudFlare 账号 解决步骤 打开 CF 的 DNS 控制面板,解析自己的域名,并打开小云朵 可以到 https://www.cloudflare.com/zh-cn/ips/ ,查看 CloudFlare 的官方 IP 段。这里我就取 104.16.0.0 和 2606:4700:: 来为例 打开 ClouDNS 的域名解析页面,添加一条 A 记录。名称和 CF DNS 解析的名称一样,但是解析的 IP 为刚刚记录的 CF CDN IP 添加一条 AAAA 记录。名称和 CF DNS 解析的名称一样,但是解析的 IP 为刚刚记录的 CF CDN IPv6 IP   5.等待 ClouDNS 解析好之后,尝试 ping,如果 ping 出来的 IP 是解析好的 CF CDN IP,即可代表成功。