跳至主要内容

百度网盘不限速全平台方案-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” 即可

解决 ClouDNS 域名无法在 CloudFlare DNS 解析的问题

Text. 在我们将 ClouDNS 域名 CloudFlare 之后,估计大家可能发现了一个问题:如果在 CloudFlare 的 DNS 解析这个域名,那这个域名有可能无法使用。这是因为不知道啥的原因,ClouDNS 的域名 DNS 还未完全转接给 CloudFlare 进行托管。这篇教程中,我就来和大家一起来解决 ClouDNS 域名无法在 CloudFlare DNS 解析的问题。 准备材料 ClouDNS 域名及账号 CloudFlare 账号 解决步骤 打开 CF 的 DNS 控制面板,解析自己的域名,并关闭小云朵 我们这时可以尝试 ping,可以发现是很大概率是 ping 不出来的 这时,我们回到 ClouDNS 的域名解析页面,添加一条和 CF DNS 相同样的记录 等待 ClouDNS 解析好之后,再次尝试 ping,如果 ping 出来的 ip 是解析好的 ip,即可代表成功。

节点客户端推荐:Karing,支持 Win / Mac / 安卓 / iOS,并附使用教程

  相信有些小白肯定和我几年前刚入坑一样热衷于使用 XX VPN 这种软件觉得一键上手挺容易,而不去使用机场、自建节点。但是这种 VPN 来说肯定比自建节点来讲是不安全的。那么我们有没有一款软件可以让小白去使用机场、自建节点呢?这篇文章介绍的 Karing,就是这种软件。下面我来和大家一起了解并使用这款软件 准备材料 Win / Mac / 安卓 / iOS 平台的设备 步骤 如为 Win / 安卓设备,打开他们的项目: https://github.com/KaringX/karing/releases ,然后根据自己的平台下载对应的安装包。如为 Mac / iOS 设备,直接在 App Store 搜索 Karing / Karing VPN 安装即可 打开 Karing,这里我们选择简体中文,点击“下一步” 分流规则这里按照默认即可,点击“下一步” 在这里我们可以通过订阅 / 节点链接、导入配置文件和扫码来添加节点 这里就以添加订阅为例,在配置链接处复制粘贴订阅链接,然后填写备注,最后点击右上角按钮添加即可 点击中间的开关按钮,启用代理。这里程序会挑选出延迟最低的节点供你链接 可以点击最下面的框,然后手动选择某一节点进行链接 可以在设置→我的配置处,管理自己的订阅配置 Text. Text.