baiduwangpan-cli 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +263 -0
- package/bin/bdp.js +387 -0
- package/lib/config.js +59 -0
- package/lib/group.js +123 -0
- package/lib/http.js +90 -0
- package/lib/index.js +5 -0
- package/lib/pan.js +204 -0
- package/package.json +44 -0
- package/scripts/postinstall.js +295 -0
package/lib/config.js
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
const fs = require("fs");
|
|
2
|
+
const path = require("path");
|
|
3
|
+
const os = require("os");
|
|
4
|
+
|
|
5
|
+
const CONFIG_DIR = path.join(os.homedir(), ".bdp");
|
|
6
|
+
const CONFIG_FILE = path.join(CONFIG_DIR, "config.json");
|
|
7
|
+
|
|
8
|
+
function load() {
|
|
9
|
+
try {
|
|
10
|
+
const raw = fs.readFileSync(CONFIG_FILE, "utf-8");
|
|
11
|
+
return JSON.parse(raw);
|
|
12
|
+
} catch {
|
|
13
|
+
return {};
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function save(cfg) {
|
|
18
|
+
fs.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
19
|
+
fs.writeFileSync(CONFIG_FILE, JSON.stringify(cfg, null, 2), "utf-8");
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function get() {
|
|
23
|
+
const fileCfg = load();
|
|
24
|
+
return {
|
|
25
|
+
bduss: fileCfg.bduss || process.env.BDP_BDUSS || "",
|
|
26
|
+
stoken: fileCfg.stoken || process.env.BDP_STOKEN || "",
|
|
27
|
+
pcsPath: fileCfg.pcsPath || process.env.BAIDUPCS_CMD || findPCS(),
|
|
28
|
+
ua: fileCfg.ua || process.env.NETDISK_UA ||
|
|
29
|
+
"netdisk;P2SP;3.0.0.8;netdisk;11.12.3;ANG-AN00;android-android;10.0;JSbridge4.4.0;jointBridge;1.1.0;",
|
|
30
|
+
maxBytes: parseInt(fileCfg.maxBytes || process.env.BDP_MAX_CAT_BYTES || "1048576", 10),
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function set(key, value) {
|
|
35
|
+
const cfg = load();
|
|
36
|
+
cfg[key] = value;
|
|
37
|
+
save(cfg);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function findPCS() {
|
|
41
|
+
// 1. Look in package root (postinstall target)
|
|
42
|
+
const pkgRoot = path.join(__dirname, "..");
|
|
43
|
+
for (const name of ["BaiduPCS-Go.exe", "BaiduPCS-Go"]) {
|
|
44
|
+
const p = path.join(pkgRoot, name);
|
|
45
|
+
if (fs.existsSync(p)) return p;
|
|
46
|
+
}
|
|
47
|
+
// 2. Look in global node_modules
|
|
48
|
+
const nmGlobal = path.join(__dirname, "..", "..", "BaiduPCS-Go.exe");
|
|
49
|
+
if (fs.existsSync(nmGlobal)) return nmGlobal;
|
|
50
|
+
// 3. Fallback to PATH
|
|
51
|
+
return "BaiduPCS-Go";
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function isLoggedIn() {
|
|
55
|
+
const cfg = get();
|
|
56
|
+
return !!(cfg.bduss && cfg.stoken);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
module.exports = { get, set, load, save, isLoggedIn, CONFIG_DIR, CONFIG_FILE };
|
package/lib/group.js
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
const http = require("./http");
|
|
2
|
+
const { getBdstoken } = http;
|
|
3
|
+
|
|
4
|
+
async function listGroups() {
|
|
5
|
+
const bdstoken = await getBdstoken();
|
|
6
|
+
if (!bdstoken) throw new Error("Cannot get bdstoken — check BDUSS/STOKEN config");
|
|
7
|
+
|
|
8
|
+
const data = http.curlJson(
|
|
9
|
+
`${http.PAN_BASE}/mbox/group/list?clienttype=0&app_id=${http.APP_ID}&web=1&bdstoken=${bdstoken}`
|
|
10
|
+
);
|
|
11
|
+
|
|
12
|
+
if (data.errno !== 0) throw new Error(`API error: errno=${data.errno}`);
|
|
13
|
+
|
|
14
|
+
return (data.records || []).map((g) => ({
|
|
15
|
+
gid: g.gid,
|
|
16
|
+
name: g.name,
|
|
17
|
+
gnum: g.gnum,
|
|
18
|
+
description: g.gdesc || "",
|
|
19
|
+
type: g.type,
|
|
20
|
+
created: g.ctime,
|
|
21
|
+
}));
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async function listShares(gid) {
|
|
25
|
+
const bdstoken = await getBdstoken();
|
|
26
|
+
const data = http.curlJson(
|
|
27
|
+
`${http.PAN_BASE}/mbox/group/listshare?clienttype=0&app_id=${http.APP_ID}&web=1&type=2&gid=${gid}&limit=50&desc=1&bdstoken=${bdstoken}`,
|
|
28
|
+
{ method: "POST", body: "" }
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
if (data.errno !== 0) throw new Error(`API error: errno=${data.errno}`);
|
|
32
|
+
|
|
33
|
+
const shares = [];
|
|
34
|
+
for (const msg of data.records?.msg_list || []) {
|
|
35
|
+
for (const f of msg.file_list || []) {
|
|
36
|
+
shares.push({
|
|
37
|
+
msgId: msg.msg_id,
|
|
38
|
+
fromUk: msg.uk,
|
|
39
|
+
fromUser: msg.uname,
|
|
40
|
+
name: decodeURIComponent(f.path || f.server_filename || ""),
|
|
41
|
+
fsId: f.fs_id,
|
|
42
|
+
isDir: f.isdir === "1",
|
|
43
|
+
size: parseInt(f.size || "0"),
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return shares;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async function listFiles(gid, fsId, options = {}) {
|
|
51
|
+
const page = options.page || 1;
|
|
52
|
+
const num = options.num || 100;
|
|
53
|
+
|
|
54
|
+
// First get the fromUk and msgId for this fsId
|
|
55
|
+
let { fromUk, msgId } = options;
|
|
56
|
+
if (!fromUk || !msgId) {
|
|
57
|
+
const shares = await listShares(gid);
|
|
58
|
+
const match = shares.find((s) => s.fsId === fsId);
|
|
59
|
+
if (match) {
|
|
60
|
+
fromUk = match.fromUk;
|
|
61
|
+
msgId = match.msgId;
|
|
62
|
+
} else if (shares.length > 0) {
|
|
63
|
+
fromUk = shares[0].fromUk;
|
|
64
|
+
msgId = shares[0].msgId;
|
|
65
|
+
} else {
|
|
66
|
+
throw new Error("Cannot find share info for this fsId");
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const bdstoken = await getBdstoken();
|
|
71
|
+
const data = http.curlJson(
|
|
72
|
+
`${http.PAN_BASE}/mbox/msg/shareinfo?type=2&from_uk=${fromUk}&msg_id=${msgId}&to_uk=0&num=${num}&page=${page}&fs_id=${fsId}&gid=${gid}&clienttype=0&app_id=${http.APP_ID}&web=1&bdstoken=${bdstoken}`,
|
|
73
|
+
{ method: "POST", body: "" }
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
if (data.errno !== 0) throw new Error(`API error: errno=${data.errno}`);
|
|
77
|
+
|
|
78
|
+
const files = (Array.isArray(data.records) ? data.records : []).map((f) => ({
|
|
79
|
+
name: f.server_filename || "",
|
|
80
|
+
path: decodeURIComponent(f.path || ""),
|
|
81
|
+
isDir: f.isdir === 1 || f.isdir === "1",
|
|
82
|
+
size: parseInt(f.size || "0"),
|
|
83
|
+
fsId: f.fs_id,
|
|
84
|
+
md5: f.md5 || "",
|
|
85
|
+
category: parseInt(f.category || "0"),
|
|
86
|
+
ctime: f.server_ctime || "",
|
|
87
|
+
mtime: f.server_mtime || "",
|
|
88
|
+
}));
|
|
89
|
+
|
|
90
|
+
return { files, hasMore: data.has_more === 1, page };
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async function searchFiles(gid, keyword, options = {}) {
|
|
94
|
+
const maxDepth = options.maxDepth || 1;
|
|
95
|
+
const results = [];
|
|
96
|
+
const kw = keyword.toLowerCase();
|
|
97
|
+
|
|
98
|
+
const shares = await listShares(gid);
|
|
99
|
+
|
|
100
|
+
for (const share of shares) {
|
|
101
|
+
if (share.name.toLowerCase().includes(kw)) {
|
|
102
|
+
results.push({ ...share, group: gid });
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (share.isDir && maxDepth > 0) {
|
|
106
|
+
try {
|
|
107
|
+
const { files } = await listFiles(gid, share.fsId, {
|
|
108
|
+
fromUk: share.fromUk,
|
|
109
|
+
msgId: share.msgId,
|
|
110
|
+
});
|
|
111
|
+
for (const f of files) {
|
|
112
|
+
if (f.name.toLowerCase().includes(kw)) {
|
|
113
|
+
results.push({ ...f, group: gid });
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
} catch {}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return results;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
module.exports = { listGroups, listShares, listFiles, searchFiles };
|
package/lib/http.js
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
const { spawnSync } = require("child_process");
|
|
2
|
+
const config = require("./config");
|
|
3
|
+
|
|
4
|
+
function curlGet(url, extraHeaders = {}) {
|
|
5
|
+
const cfg = config.get();
|
|
6
|
+
const headers = {
|
|
7
|
+
Cookie: `BDUSS=${cfg.bduss}; STOKEN=${cfg.stoken}`,
|
|
8
|
+
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
|
|
9
|
+
Referer: "https://pan.baidu.com/disk/main",
|
|
10
|
+
...extraHeaders,
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const args = ["-s", "-L", "--max-time", "30", "--compressed"];
|
|
14
|
+
for (const [k, v] of Object.entries(headers)) {
|
|
15
|
+
args.push("-H", `${k}: ${v}`);
|
|
16
|
+
}
|
|
17
|
+
args.push(url);
|
|
18
|
+
|
|
19
|
+
const result = spawnSync("curl", args, { encoding: "utf-8", maxBuffer: 50 * 1024 * 1024 });
|
|
20
|
+
return result.stdout || "";
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function curlPost(url, body = "", extraHeaders = {}) {
|
|
24
|
+
const cfg = config.get();
|
|
25
|
+
const headers = {
|
|
26
|
+
Cookie: `BDUSS=${cfg.bduss}; STOKEN=${cfg.stoken}`,
|
|
27
|
+
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
|
|
28
|
+
Referer: "https://pan.baidu.com/disk/main",
|
|
29
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
30
|
+
...extraHeaders,
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const args = ["-s", "-L", "-X", "POST", "--max-time", "30", "--compressed"];
|
|
34
|
+
for (const [k, v] of Object.entries(headers)) {
|
|
35
|
+
args.push("-H", `${k}: ${v}`);
|
|
36
|
+
}
|
|
37
|
+
if (body) args.push("-d", body);
|
|
38
|
+
args.push(url);
|
|
39
|
+
|
|
40
|
+
const result = spawnSync("curl", args, { encoding: "utf-8", maxBuffer: 50 * 1024 * 1024 });
|
|
41
|
+
return result.stdout || "";
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function curlRange(url, start, end, ua) {
|
|
45
|
+
const rangeArg = end != null ? `${start}-${end}` : `${start}-`;
|
|
46
|
+
const args = [
|
|
47
|
+
"-s", "-L",
|
|
48
|
+
"-r", rangeArg,
|
|
49
|
+
"-H", `User-Agent: ${ua}`,
|
|
50
|
+
"-H", `Range: bytes=${rangeArg}`,
|
|
51
|
+
"--max-time", "30",
|
|
52
|
+
url,
|
|
53
|
+
];
|
|
54
|
+
const result = spawnSync("curl", args, { maxBuffer: 60 * 1024 * 1024 });
|
|
55
|
+
return result.stdout || Buffer.alloc(0);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function curlJson(url, options = {}) {
|
|
59
|
+
const text = options.method === "POST"
|
|
60
|
+
? curlPost(url, options.body || "", options.headers || {})
|
|
61
|
+
: curlGet(url, options.headers || {});
|
|
62
|
+
const cleaned = text.replace(/^\s+/, "");
|
|
63
|
+
try {
|
|
64
|
+
return JSON.parse(cleaned);
|
|
65
|
+
} catch {
|
|
66
|
+
return { _raw: cleaned };
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async function getBdstoken() {
|
|
71
|
+
const fields = encodeURIComponent('["bdstoken","token","uk","isdocuser","servertime"]');
|
|
72
|
+
const data = curlJson(
|
|
73
|
+
`https://pan.baidu.com/api/gettemplatevariable?clienttype=0&app_id=250528&web=1&fields=${fields}`
|
|
74
|
+
);
|
|
75
|
+
const token = data?.result?.bdstoken || null;
|
|
76
|
+
if (!token) {
|
|
77
|
+
const cfg = require("./config").get();
|
|
78
|
+
throw new Error(
|
|
79
|
+
`Cannot get bdstoken. Check BDUSS/STOKEN.\n` +
|
|
80
|
+
` BDUSS: ${cfg.bduss ? "set" : "MISSING"} STOKEN: ${cfg.stoken ? "set" : "MISSING"}\n` +
|
|
81
|
+
` Run: bdp login --bduss <value> --stoken <value>`
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
return token;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const APP_ID = "250528";
|
|
88
|
+
const PAN_BASE = "https://pan.baidu.com";
|
|
89
|
+
|
|
90
|
+
module.exports = { curlGet, curlPost, curlRange, curlJson, getBdstoken, APP_ID, PAN_BASE };
|
package/lib/index.js
ADDED
package/lib/pan.js
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
const { spawnSync } = require("child_process");
|
|
2
|
+
const config = require("./config");
|
|
3
|
+
const http = require("./http");
|
|
4
|
+
|
|
5
|
+
function runPCS(args) {
|
|
6
|
+
const cfg = config.get();
|
|
7
|
+
const result = spawnSync(cfg.pcsPath, args, {
|
|
8
|
+
encoding: "utf-8",
|
|
9
|
+
maxBuffer: 20 * 1024 * 1024,
|
|
10
|
+
});
|
|
11
|
+
if (result.error) {
|
|
12
|
+
throw new Error(`BaiduPCS-Go not found at: ${cfg.pcsPath}. Set pcsPath in config or BAIDUPCS_CMD env.`);
|
|
13
|
+
}
|
|
14
|
+
return result.stdout || "";
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function getDlink(remotePath) {
|
|
18
|
+
const output = runPCS(["locate", remotePath]);
|
|
19
|
+
const urls = output.match(/https?:\/\/[^\s]+/g);
|
|
20
|
+
if (urls && urls.length > 0) {
|
|
21
|
+
return urls[0].replace(/[,\s]+$/, "");
|
|
22
|
+
}
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function getFileMeta(remotePath) {
|
|
27
|
+
const output = runPCS(["meta", remotePath]);
|
|
28
|
+
let size = null;
|
|
29
|
+
|
|
30
|
+
let m = output.match(/大小[::]\s*([\d,]+)/);
|
|
31
|
+
if (m) size = parseInt(m[1].replace(/,/g, ""), 10);
|
|
32
|
+
if (!size) {
|
|
33
|
+
m = output.match(/size[":\s]+(\d+)/i);
|
|
34
|
+
if (m) size = parseInt(m[1], 10);
|
|
35
|
+
}
|
|
36
|
+
if (!size) {
|
|
37
|
+
m = output.match(/(\d[\d,]*)\s*B\b/);
|
|
38
|
+
if (m) size = parseInt(m[1].replace(/,/g, ""), 10);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
let isDir = false;
|
|
42
|
+
if (/目录|isdir.*1|is_dir.*true/i.test(output)) isDir = true;
|
|
43
|
+
|
|
44
|
+
return { size, isDir, raw: output };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function fetchRangeText(url, start, end) {
|
|
48
|
+
const cfg = config.get();
|
|
49
|
+
const buf = http.curlRange(url, start, end, cfg.ua);
|
|
50
|
+
return decodeText(buf);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function fetchRangeBuf(url, start, end) {
|
|
54
|
+
const cfg = config.get();
|
|
55
|
+
return http.curlRange(url, start, end, cfg.ua);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function decodeText(buf) {
|
|
59
|
+
if (typeof buf === "string") return buf;
|
|
60
|
+
for (const enc of ["utf-8", "gbk", "gb2312", "latin-1"]) {
|
|
61
|
+
try {
|
|
62
|
+
return buf.toString(enc);
|
|
63
|
+
} catch {
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return buf.toString("utf-8");
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// ── Pan Operations ─────────────────────────────────────
|
|
71
|
+
|
|
72
|
+
function ls(dirPath = "/") {
|
|
73
|
+
return runPCS(["ls", dirPath]);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function search(keyword, searchPath = "/") {
|
|
77
|
+
return runPCS(["search", `-path=${searchPath}`, "-r", keyword]);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function cat(remotePath, maxBytes) {
|
|
81
|
+
const cfg = config.get();
|
|
82
|
+
const limit = maxBytes || cfg.maxBytes;
|
|
83
|
+
const meta = getFileMeta(remotePath);
|
|
84
|
+
|
|
85
|
+
if (meta.size && meta.size > 50 * 1024 * 1024) {
|
|
86
|
+
process.stderr.write(`[WARN] File is ${(meta.size / 1024 / 1024).toFixed(1)}MB, truncating to ${(limit / 1024).toFixed(0)}KB\n`);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const dlink = getDlink(remotePath);
|
|
90
|
+
if (!dlink) throw new Error(`Cannot get dlink for: ${remotePath}`);
|
|
91
|
+
|
|
92
|
+
const readBytes = Math.min(meta.size || limit, limit) - 1;
|
|
93
|
+
return fetchRangeText(dlink, 0, readBytes);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function head(remotePath, n = 20, maxBytes) {
|
|
97
|
+
const cfg = config.get();
|
|
98
|
+
const limit = maxBytes || cfg.maxBytes;
|
|
99
|
+
const dlink = getDlink(remotePath);
|
|
100
|
+
if (!dlink) throw new Error(`Cannot get dlink for: ${remotePath}`);
|
|
101
|
+
|
|
102
|
+
const chunkSize = Math.min(n * 4096 + 1024, limit);
|
|
103
|
+
const text = fetchRangeText(dlink, 0, chunkSize - 1);
|
|
104
|
+
const lines = text.split("\n");
|
|
105
|
+
return lines.slice(0, n);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function tail(remotePath, n = 20, maxBytes) {
|
|
109
|
+
const cfg = config.get();
|
|
110
|
+
const limit = maxBytes || cfg.maxBytes;
|
|
111
|
+
const meta = getFileMeta(remotePath);
|
|
112
|
+
if (!meta.size) throw new Error(`Cannot get file size for: ${remotePath}`);
|
|
113
|
+
|
|
114
|
+
const dlink = getDlink(remotePath);
|
|
115
|
+
if (!dlink) throw new Error(`Cannot get dlink for: ${remotePath}`);
|
|
116
|
+
|
|
117
|
+
const chunkSize = Math.min(n * 4096 + 1024, limit, meta.size);
|
|
118
|
+
const start = Math.max(0, meta.size - chunkSize);
|
|
119
|
+
const text = fetchRangeText(dlink, start, meta.size - 1);
|
|
120
|
+
const lines = text.split("\n");
|
|
121
|
+
return lines.slice(-n);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function grep(pattern, remotePath, options = {}) {
|
|
125
|
+
const cfg = config.get();
|
|
126
|
+
const dlink = getDlink(remotePath);
|
|
127
|
+
if (!dlink) throw new Error(`Cannot get dlink for: ${remotePath}`);
|
|
128
|
+
|
|
129
|
+
const text = fetchRangeText(dlink, 0, cfg.maxBytes - 1);
|
|
130
|
+
|
|
131
|
+
let regex;
|
|
132
|
+
try {
|
|
133
|
+
regex = new RegExp(pattern, options.ignoreCase ? "gi" : "g");
|
|
134
|
+
} catch {
|
|
135
|
+
regex = new RegExp(pattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), options.ignoreCase ? "gi" : "g");
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const matches = [];
|
|
139
|
+
const lines = text.split("\n");
|
|
140
|
+
lines.forEach((line, i) => {
|
|
141
|
+
if (regex.test(line)) {
|
|
142
|
+
matches.push({ line: i + 1, content: line });
|
|
143
|
+
}
|
|
144
|
+
regex.lastIndex = 0;
|
|
145
|
+
});
|
|
146
|
+
return matches;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function peek(remotePath) {
|
|
150
|
+
const meta = getFileMeta(remotePath);
|
|
151
|
+
const dlink = getDlink(remotePath);
|
|
152
|
+
|
|
153
|
+
const result = {
|
|
154
|
+
path: remotePath,
|
|
155
|
+
size: meta.size,
|
|
156
|
+
isDir: meta.isDir,
|
|
157
|
+
dlink: !!dlink,
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
if (dlink && !meta.isDir) {
|
|
161
|
+
const cfg = config.get();
|
|
162
|
+
const previewBytes = Math.min(512, meta.size || 512) - 1;
|
|
163
|
+
const buf = fetchRangeBuf(dlink, 0, previewBytes);
|
|
164
|
+
|
|
165
|
+
let isBinary = false;
|
|
166
|
+
for (const b of buf.slice(0, 200)) {
|
|
167
|
+
if (b < 9 || (b > 13 && b < 32)) {
|
|
168
|
+
isBinary = true;
|
|
169
|
+
break;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
result.type = isBinary ? "binary" : "text";
|
|
174
|
+
if (!isBinary) {
|
|
175
|
+
result.preview = decodeText(buf).split("\n").slice(0, 10);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return result;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function download(remotePath, saveTo) {
|
|
183
|
+
const args = ["d", remotePath];
|
|
184
|
+
if (saveTo) args.push("--saveto", saveTo);
|
|
185
|
+
return runPCS(args);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function upload(localPath, remotePath) {
|
|
189
|
+
return runPCS(["u", localPath, remotePath]);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function mkdir(remotePath) {
|
|
193
|
+
return runPCS(["mkdir", remotePath]);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function remove(remotePath) {
|
|
197
|
+
return runPCS(["rm", remotePath]);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
module.exports = {
|
|
201
|
+
ls, search, cat, head, tail, grep, peek,
|
|
202
|
+
download, upload, mkdir, remove,
|
|
203
|
+
getDlink, getFileMeta, fetchRangeText,
|
|
204
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "baiduwangpan-cli",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "百度网盘 CLI — 全盘读写 · 免下载读取文件内容 · 群聊文件浏览 (Agent 友好)",
|
|
5
|
+
"main": "lib/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"bdp": "bin/bdp.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"start": "node bin/bdp.js",
|
|
11
|
+
"postinstall": "node scripts/postinstall.js"
|
|
12
|
+
},
|
|
13
|
+
"engines": {
|
|
14
|
+
"node": ">=14"
|
|
15
|
+
},
|
|
16
|
+
"license": "MIT",
|
|
17
|
+
"keywords": [
|
|
18
|
+
"baidu",
|
|
19
|
+
"netdisk",
|
|
20
|
+
"pan",
|
|
21
|
+
"cli",
|
|
22
|
+
"agent",
|
|
23
|
+
"bduss",
|
|
24
|
+
"baidupcs",
|
|
25
|
+
"baidu-pan",
|
|
26
|
+
"baiduyunpan",
|
|
27
|
+
"ai-agent",
|
|
28
|
+
"mcp"
|
|
29
|
+
],
|
|
30
|
+
"files": [
|
|
31
|
+
"bin/",
|
|
32
|
+
"lib/",
|
|
33
|
+
"scripts/postinstall.js"
|
|
34
|
+
],
|
|
35
|
+
"repository": {
|
|
36
|
+
"type": "git",
|
|
37
|
+
"url": "https://github.com/NkAntony777/baiduwangpan-cli.git"
|
|
38
|
+
},
|
|
39
|
+
"homepage": "https://github.com/NkAntony777/baiduwangpan-cli",
|
|
40
|
+
"bugs": {
|
|
41
|
+
"url": "https://github.com/NkAntony777/baiduwangpan-cli/issues"
|
|
42
|
+
},
|
|
43
|
+
"author": "NkAntony777"
|
|
44
|
+
}
|