-
Notifications
You must be signed in to change notification settings - Fork 283
Expand file tree
/
Copy pathbaidu2.js
More file actions
338 lines (322 loc) · 13 KB
/
baidu2.js
File metadata and controls
338 lines (322 loc) · 13 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
/**
* 百度网盘解析模块 - 秋秋版本
* 提供百度网盘分享链接的解析和文件获取功能
* 支持获取分享文件列表、生成播放链接等操作
*/
import '../../libs_drpy/jsencrypt.js'
import {ENV} from "../env.js";
import axios from "axios";
import qs from "qs"; // 添加缺失的qs模块导入
/**
* 百度网盘驱动类
* 用于解析百度网盘分享链接,获取文件信息和播放地址
*/
class BaiduDrive {
/**
* 构造函数 - 初始化百度网盘相关配置
*/
constructor() {
// 百度网盘分享链接正则表达式
this.regex = /https:\/\/pan\.baidu\.com\/s\/(.*)\?.*?pwd=([^&]+)/;//https://pan.baidu.com/s/1kbM0KWLDpeS8I49tmwS6lQ?pwd=74j5
// 支持的视频质量类型
this.type = ["M3U8_AUTO_4K", "M3U8_AUTO_2K", "M3U8_AUTO_1080", "M3U8_AUTO_720", "M3U8_AUTO_480"];
// 请求头配置
this.headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36",
"Connection": "keep-alive",
"Accept": "application/json, text/plain, */*",
"Accept-Encoding": "gzip, deflate, br",
"Accept-Language": "zh,en-GB;q=0.9,en-US;q=0.8,en;q=0.7,zh-CN;q=0.6"
};
// 百度网盘API基础地址
this.api = 'https://pan.baidu.com';
// 分享链接
this.link = ''
// 提取码
this.pwd = '';
// 短链接标识
this.surl = '';
// 短链接(去掉首字符)
this.shorturl = ''
// 分享ID
this.shareid = '';
// 应用ID
this.app_id = 250528;
// 视图模式
this.view_mode = 1;
// 渠道标识
this.channel = 'chunlei';
}
/**
* 初始化方法,加载本地配置
* @returns {Promise<void>}
*/
async init() {
if (this.cookie) {
console.log('百度网盘cookie获取成功' + this.cookie)
}
}
/**
* 获取百度网盘Cookie
* @returns {string} 百度网盘Cookie
*/
get cookie() {
return ENV.get('baidu_cookie')
}
/**
* 解析分享链接,提取surl和密码
* @param {string} url - 百度网盘分享链接
* @returns {Promise<void>}
*/
async getSurl(url) {
this.link = url
const matches = this.regex.exec(url);
if (matches && matches[1]) {
this.surl = matches[1];
// 去掉首字符的短链接
this.shorturl = this.surl.split('').slice(1).join('')
this.pwd = matches[2] || '';
}
}
/**
* 获取签名信息
* @returns {Promise<string>} 签名字符串
*/
async getSign() {
let data = (await axios.get(`${this.api}/share/tplconfig?surl=${this.surl}&fields=Espace_info,card_info,sign,timestamp&view_mode=${this.view_mode}&channel=${this.channel}&web=1&app_id=${this.app_id}`, {
headers: this.headers
})).data
return data.data.sign
}
/**
* 获取分享数据的主入口方法
* @param {string} link - 百度网盘分享链接
* @returns {Promise<Object>} 分享文件数据
*/
async getShareData(link) {
await this.getSurl(link)
return await this.getShareList()
}
/**
* 获取随机密钥(randsk)并更新Cookie
* @returns {Promise<string>} 随机密钥
*/
async getRandsk() {
// 构造验证请求数据
let data = qs.stringify({
'pwd': this.pwd,
'vcode': '',
'vcode_str': ''
});
// 发送验证请求获取randsk
let randsk = (await axios.post(`${this.api}/share/verify?surl=${this.shorturl}&pwd=${this.pwd}`, data, {
headers: {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36',
'Referer': this.link,
}
})).data.randsk
let BDCLND = "BDCLND=" + randsk
// 更新Cookie中的BDCLND值
if (!this.cookie.includes('BDCLND')) {
let cookie = this.cookie + BDCLND
ENV.set('baidu_cookie', cookie)
return randsk
} else {
let cookie = this.cookie.split(';').map(it => {
if (/BDCLND/.test(it)) {
it = BDCLND
}
return it
}).join(';')
if (cookie !== this.cookie) {
ENV.set('baidu_cookie', cookie);
}
return randsk
}
}
/**
* 获取分享文件列表
* @returns {Promise<Object>} 文件列表对象
*/
async getShareList() {
await this.getRandsk()
this.headers['cookie'] = this.cookie
// 获取分享根目录文件列表
let data = (await axios.get(`${this.api}/share/list?web=5&app_id=${this.app_id}&desc=1&showempty=0&page=1&num=20&order=time&shorturl=${this.shorturl}&root=1&view_mode=${this.view_mode}&channel=${this.channel}&web=1&clienttype=0`, {
headers: this.headers
})).data
if (data.errno === 0 && data.list.length > 0) {
let file = {}
let dirs = [] // 目录列表
let videos = [] // 视频文件列表
this.uk = data.uk
this.shareid = data.share_id
// 遍历文件列表,分类处理
data.list.map(item => {
// 目录类型 (category: 6)
if (item.category === '6' || item.category === 6) {
dirs.push(item.path)
}
// 视频类型 (category: 1)
if (item.category === '1' || item.category === 1) {
// 确保所有情况下都提取文件名
const fileName = item.server_filename || item.path.split('/').pop();
videos.push({
name: fileName, // 只使用文件名
path: item.path.replaceAll('#', '\0'), // 如果路径里含有#,替换为非法文本\0,所有系统的路径都不可能存在这个文本
uk: this.uk,
shareid: this.shareid,
fsid: item.fs_id || item.fsid
})
}
});
// 初始化文件对象
if (!(data.title in file) && data.title !== undefined) {
file[data.title] = [];
}
if (videos.length >= 0 && data.title !== undefined) {
file[data.title] = [...videos]
}
// 递归获取子目录中的文件
let result = await Promise.all(dirs.map(async (path) => this.getSharepath(path)))
result = result.filter(item => item !== undefined && item !== null).flat()
if (result.length >= 0) {
// 确保递归获取的文件也正确处理文件名
const processedResult = result.map(item => {
if (item.name && item.name.includes('/')) {
item.name = item.name.split('/').pop();
}
return item;
});
file[data.title].push(...processedResult);
}
return file;
}
}
/**
* 获取指定路径下的文件列表(递归)
* @param {string} path - 目录路径
* @returns {Promise<Array>} 文件列表数组
*/
async getSharepath(path) {
await this.getRandsk()
this.headers['cookie'] = this.cookie
// 获取指定目录下的文件列表
let data = (await axios.get(`${this.api}/share/list?is_from_web=true&uk=${this.uk}&shareid=${this.shareid}&order=name&desc=0&showempty=0&view_mode=${this.view_mode}&web=1&page=1&num=100&dir=${encodeURIComponent(path)}&channel=${this.channel}&web=1&app_id=${this.app_id}`, {
headers: this.headers
})).data
if (data.errno === 0 && data.list.length > 0) {
let dirs = [] // 子目录列表
let videos = [] // 视频文件列表
// 遍历当前目录文件
data.list.map(item => {
// 目录类型
if (item.category === '6' || item.category === 6) {
dirs.push(item.path)
}
// 视频类型
if (item.category === '1' || item.category === 1) {
// 确保所有情况下都提取文件名
const fileName = item.server_filename || item.path.split('/').pop();
videos.push({
name: fileName, // 只使用文件名
path: item.path.replaceAll('#', '\0'), // 如果路径里含有#,替换为非法文本\0,所有系统的路径都不可能存在这个文本
uk: this.uk,
shareid: this.shareid,
fsid: item.fs_id || item.fsid
})
}
});
// 递归处理子目录
let result = await Promise.all(dirs.map(async (path) => this.getSharepath(path)))
result = result.filter(item => item !== undefined && item !== null);
// 确保递归获取的文件也正确处理文件名
const processedResult = result.map(item => {
if (item.name && item.name.includes('/')) {
item.name = item.name.split('/').pop();
}
return item;
});
return [...videos, ...processedResult.flat()];
}
}
/**
* 获取文件的播放链接(Web版)
* @param {string} path - 文件路径
* @param {string} uk - 用户标识
* @param {string} shareid - 分享ID
* @param {string} fsid - 文件ID
* @returns {Promise<Array>} 不同清晰度的播放链接数组
*/
async getShareUrl(path, uk, shareid, fsid) {
path = path.replaceAll('\0', '#'); // 把真实路径还原
log('[baidu2][getShareUrl] path:', path);
let sign = await this.getSign()
let urls = []
let t = Math.floor(new Date() / 1000); // 当前时间戳
// 生成不同清晰度的播放链接
this.type.map(it => {
urls.push({
name: it.replace('M3U8_AUTO_', ''),
url: `${this.api}/share/streaming?channel=${this.channel}&uk=${uk}&fid=${fsid}&sign=${sign}×tamp=${t}&shareid=${shareid}&type=${it}&vip=0&jsToken&isplayer=1&check_blue=1&adToken`
})
})
return urls
}
/**
* 获取用户UID
* @returns {Promise<string>} 用户UID
*/
async getUid() {
let data = (await axios.get('https://mbd.baidu.com/userx/v1/info/get?appname=baiduboxapp&fields=%20%20%20%20%20%20%20%20%5B%22bg_image%22,%22member%22,%22uid%22,%22avatar%22,%20%22avatar_member%22%5D&client&clientfrom&lang=zh-cn&tpl&ttt', {
headers: this.headers
})).data
return data.data.fields.uid
}
/**
* SHA1哈希计算
* @param {string} message - 待哈希的消息
* @returns {string} SHA1哈希值
*/
sha1(message) {
return CryptoJS.SHA1(message).toString(CryptoJS.enc.Hex);
}
/**
* 获取文件的直链地址(App版)
* @param {string} path - 文件路径
* @param {string} uk - 用户标识
* @param {string} shareid - 分享ID
* @param {string} fsid - 文件ID
* @returns {Promise<string>} 直链地址
*/
async getAppShareUrl(path, uk, shareid, fsid) {
path = path.replaceAll('\0', '#'); // 把真实路径还原
log('[baidu2][getAppShareUrl] path:', path);
let BDCLND = await this.getRandsk()
let uid = await this.getUid()
// 设置移动端请求头
let header = Object.assign({}, this.headers, {
"User-Agent": 'netdisk;P2SP;2.2.91.136;android-android;',
});
let devuid = "73CED981D0F186D12BC18CAE1684FFD5|VSRCQTF6W"; // 设备标识
let time = String(new Date().getTime()); // 时间戳
// 生成签名
let rand = this.sha1(this.sha1(this.cookie.match(/BDUSS=(.+?);/)[1]) + uid + "ebrcUYiuxaZv2XGu7KIYKxUrqfnOfpDF" + time + devuid + "11.30.2ae5821440fab5e1a61a025f014bd8972");
// 构造请求URL
let url = this.api + "/share/list?shareid=" + shareid + "&uk=" + uk + "&fid=" + fsid + "&sekey=" + BDCLND + "&origin=dlna&devuid=" + devuid + "&clienttype=1&channel=android_12_zhao_bd-netdisk_1024266h&version=11.30.2&time=" + time + "&rand=" + rand;
let data = (await axios.get(url, {
headers: header
})).data
if (data.errno === 0 && data.list.length > 0) {
// let relink = await axios.get(link,{
// headers:header,
// redirect: false,
// onlyHeaders: true
// })
// console.log(relink)
return data.list[0].dlink // 返回直链地址
}
}
}
// 导出百度网盘实例
export const Baidu2 = new BaiduDrive();