chore: 初始化 123yunpan skill 仓库 (v1.3.0)
- 完整 skill 本体:SKILL.md / README.md / manifest.json / config.json - scripts/123yunpan.py:零依赖 CLI(含空间双配额、QPS 限流、零依赖 XLSX) - references/ 参考资料 - 排除 .workbuddy/ 开发目录与 __pycache__
This commit is contained in:
+11
@@ -0,0 +1,11 @@
|
||||
# 开发目录(内存、回归脚本、缓存)不参与版本控制
|
||||
.workbuddy/
|
||||
|
||||
# Python 缓存
|
||||
__pycache__/
|
||||
*.pyc
|
||||
*.pyo
|
||||
|
||||
# 系统文件
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
@@ -0,0 +1,368 @@
|
||||
# 123云盘 (123 Cloud Drive) CLI
|
||||
|
||||
> 🚀 123云盘开放平台 API 命令行工具 v1.3.0
|
||||
|
||||
一站式管理 123 云盘:文件管理、上传下载、分享链接、离线下载、直链、图床、视频转码、专业/标准双空间配额报告。纯 Python 标准库实现,零依赖(**Excel 导出也不需要 openpyxl**)。
|
||||
|
||||
---
|
||||
|
||||
## 📋 功能一览
|
||||
|
||||
| 分类 | 功能 |
|
||||
|------|------|
|
||||
| 📁 文件管理 | 浏览、创建目录、重命名、移动、复制、回收站、下载 |
|
||||
| 🔎 递归搜索 | 跨文件夹深度搜索文件名,支持自定义搜索深度 |
|
||||
| ⬆️ 上传 | V2 分片上传(>100MB 自动分片)、单步上传、**秒传检测** |
|
||||
| 🔗 分享 | 普通分享 / 付费分享 / **批量创建**,支持密码、有效期、流量控制 |
|
||||
| 📊 分享统计 | 一键查看分享总数、预览/下载/转存次数、流量 Top N |
|
||||
| 📄 分享导出 | 批量创建分享,超过 10 个链接自动生成**三列 Excel**(文件名称 / 链接 / 路径) |
|
||||
| 🗑️ 分享管理 | 批量删除分享链接 / 断点续跑 |
|
||||
| 📥 离线下载 | HTTP / FTP / 磁力链接离线下载 |
|
||||
| 🌐 直链 | 启用直链、**短直链生成**、IP 白名单、缓存刷新、流量日志 |
|
||||
| 🖼️ 图床 | 图片存储、移动、复制、离线迁移 |
|
||||
| 🎬 视频转码 | 多分辨率转码、MP4/M3U8 下载 |
|
||||
| 🔒 保险箱 | 解锁保险箱 |
|
||||
| ⚙️ 配置管理 | 配置查看、**Token 有效性验证** |
|
||||
| 🌲 目录树 | 文件夹树形结构展示 |
|
||||
| ⬇️ 批量下载 | 多文件并行下载 |
|
||||
| 📊 存储分析 | 按文件夹统计空间占用 |
|
||||
| 📤 分享导出 | 分享链接批量导出 Excel |
|
||||
| 🔄 配置迁移 | 配置导入/导出 |
|
||||
| 💾 空间报告 | 专业空间 / 标准空间**双配额**明细、使用率预警、清理倒计时 |
|
||||
|
||||
---
|
||||
|
||||
## 🧮 空间规则:专业空间 / 标准空间
|
||||
|
||||
官方 2026-09-24 公告后,账号空间拆分为**两套独立配额**:
|
||||
|
||||
| 空间 | 存放内容 | 超额后果 |
|
||||
|------|----------|----------|
|
||||
| **专业空间** | 本人持有的文件、≥10GB 文件、直链文件、转码文件、图床文件 | 阻断开放平台的**上传 / 离线下载 / 转码** |
|
||||
| **标准空间** | 转存的文件(<10GB);重复文件与回收站文件不计入 | 同样阻断上传 |
|
||||
|
||||
```bash
|
||||
python3 scripts/123yunpan.py space
|
||||
```
|
||||
|
||||
- 使用率 ≥ 90% → 红色预警
|
||||
- 专业空间剩余天数 > 0 → 打印清理倒计时(超出后配额不可恢复)
|
||||
- `config check` 与 `upload` 执行前自动预检,专业空间已满会直接给出阻断提示
|
||||
|
||||
---
|
||||
|
||||
## 🚀 快速开始
|
||||
|
||||
### 1. 获取凭证
|
||||
|
||||
1. 访问 [123pan.com/member](https://www.123pan.com/member) 购买**开发者权益包**
|
||||
2. 通过站内信获取 `client_id` 和 `client_secret`
|
||||
|
||||
### 2. 配置
|
||||
|
||||
```bash
|
||||
# 一键配置(推荐)
|
||||
python3 scripts/123yunpan.py setup --client-id YOUR_ID --client-secret YOUR_SECRET
|
||||
|
||||
# 验证配置是否有效
|
||||
python3 scripts/123yunpan.py config check
|
||||
```
|
||||
|
||||
输出示例:
|
||||
```
|
||||
🔍 正在验证配置...
|
||||
✅ Client ID: 已配置
|
||||
✅ Client Secret: 已配置
|
||||
✅ Access Token: 已配置
|
||||
|
||||
✅ Token 有效!
|
||||
{
|
||||
"用户ID": 1812749510,
|
||||
"昵称": "暖心向阳335",
|
||||
"VIP": "是",
|
||||
"总容量": "21.56 TB",
|
||||
"已用": "859.33 GB"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📖 完整命令参考
|
||||
|
||||
### 文件管理
|
||||
|
||||
```bash
|
||||
# 浏览文件夹
|
||||
python3 scripts/123yunpan.py file list --parent-id 0 --limit 100
|
||||
|
||||
# 只看回收站中的文件
|
||||
python3 scripts/123yunpan.py file list --parent-id 0 --trashed
|
||||
|
||||
# 同时显示回收站文件(带 🗑️ 标记)
|
||||
python3 scripts/123yunpan.py file list --parent-id 0 --show-trashed
|
||||
|
||||
# 跨文件夹递归搜索(最大深度 5 层)
|
||||
python3 scripts/123yunpan.py file search "Photoshop" --parent-id 27172609 --max-depth 3
|
||||
|
||||
# 存储报告(按文件夹统计空间占用)
|
||||
python3 scripts/123yunpan.py file storage-report --parent-id 0
|
||||
|
||||
# 获取文件详情
|
||||
python3 scripts/123yunpan.py file detail --file-id 12345
|
||||
|
||||
# 批量获取文件详情
|
||||
python3 scripts/123yunpan.py file details-multi --file-ids "123,456,789"
|
||||
|
||||
# 创建文件夹
|
||||
python3 scripts/123yunpan.py file mkdir --name "新文件夹" --parent-id 0
|
||||
|
||||
# 目录树展示
|
||||
python3 scripts/123yunpan.py file tree --parent-id 0
|
||||
|
||||
# 重命名
|
||||
python3 scripts/123yunpan.py file rename --file-id 12345 --new-name "新名称.txt"
|
||||
|
||||
# 批量重命名
|
||||
python3 scripts/123yunpan.py file rename-batch --file-ids 123 456 --new-names "a.txt" "b.txt"
|
||||
|
||||
# 移动文件
|
||||
python3 scripts/123yunpan.py file move --file-ids "123,456" --target-parent-id 0
|
||||
|
||||
# 复制文件
|
||||
python3 scripts/123yunpan.py file copy --file-id 12345 --target-parent-id 0
|
||||
|
||||
# 移入回收站
|
||||
python3 scripts/123yunpan.py file trash --file-ids "123,456"
|
||||
|
||||
# 从回收站恢复
|
||||
python3 scripts/123yunpan.py file recover --file-ids "123,456"
|
||||
|
||||
# 下载文件
|
||||
python3 scripts/123yunpan.py file download --file-id 12345 --output /path/to/save
|
||||
|
||||
# 批量下载文件
|
||||
python3 scripts/123yunpan.py file download-batch --file-ids "123,456,789" --output /path/to/save
|
||||
```
|
||||
|
||||
### 上传
|
||||
|
||||
```bash
|
||||
# 上传文件(自动选择单次/分片上传,自动检测秒传)
|
||||
python3 scripts/123yunpan.py upload --file /path/to/file.txt --parent-id 0
|
||||
|
||||
# 上传并指定文件名
|
||||
python3 scripts/123yunpan.py upload --file /path/to/file.txt --parent-id 0 --filename "自定义名.zip"
|
||||
|
||||
# 重复文件处理:1=重命名 2=覆盖(默认不传=覆盖)
|
||||
python3 scripts/123yunpan.py upload --file /path/to/file.txt --parent-id 0 --duplicate 2
|
||||
```
|
||||
|
||||
秒传时会显示:`⚡ 秒传成功!文件已存在,跳过上传 (fileID: 12345)`
|
||||
|
||||
### 分享管理
|
||||
|
||||
```bash
|
||||
# 创建单个分享
|
||||
python3 scripts/123yunpan.py share create --name "分享名" --file-ids "123,456" --expire 0
|
||||
|
||||
# 分享导出(批量导出分享链接到 Excel)
|
||||
python3 scripts/123yunpan.py share export --output shares.xlsx
|
||||
|
||||
# ⭐ 查看空间占用(专业空间 / 标准空间)
|
||||
python3 scripts/123yunpan.py space
|
||||
|
||||
# ⭐ 批量创建分享(递归遍历文件夹,导出 Excel)
|
||||
python3 scripts/123yunpan.py share create-batch --parent-id 27172609 --expire 0 -o result.xlsx
|
||||
|
||||
# 批量创建分享(导出 JSON)
|
||||
python3 scripts/123yunpan.py share create-batch --parent-id 27172609 -o result.json
|
||||
|
||||
# 超过 10 个链接自动生成 Excel;可用以下参数调整
|
||||
python3 scripts/123yunpan.py share create-batch --parent-id 27172609 --excel-threshold 50
|
||||
python3 scripts/123yunpan.py share create-batch --parent-id 27172609 --no-excel
|
||||
python3 scripts/123yunpan.py share create-batch --parent-id 27172609 --resume # 断点续跑
|
||||
|
||||
# 列出分享
|
||||
python3 scripts/123yunpan.py share list --limit 10
|
||||
python3 scripts/123yunpan.py share list --all-pages
|
||||
|
||||
# ⭐ 分享统计(查看下载量 Top N)
|
||||
python3 scripts/123yunpan.py share stats --top 10
|
||||
|
||||
# 更新分享设置
|
||||
python3 scripts/123yunpan.py share update --share-ids "1,2" --traffic-switch 2
|
||||
|
||||
# 批量删除分享
|
||||
python3 scripts/123yunpan.py share delete-batch --share-ids "1,2,3"
|
||||
|
||||
# 删除所有分享(跳过确认)
|
||||
python3 scripts/123yunpan.py share delete-batch --all -y
|
||||
|
||||
# 创建付费分享
|
||||
python3 scripts/123yunpan.py share pay-create --name "付费资源" --file-ids "123,456" --amount 10
|
||||
|
||||
# 列出付费分享
|
||||
python3 scripts/123yunpan.py share pay-list --limit 10
|
||||
```
|
||||
|
||||
### 直链管理
|
||||
|
||||
```bash
|
||||
# 启用文件直链
|
||||
python3 scripts/123yunpan.py direct-link enable --file-id 12345
|
||||
|
||||
# 获取完整直链
|
||||
python3 scripts/123yunpan.py direct-link url --file-id 12345
|
||||
|
||||
# ⭐ 生成短直链(更隐私,不暴露文件名)
|
||||
python3 scripts/123yunpan.py direct-link short-url --file-id 12345
|
||||
# 输出: https://1812749510.v.123pan.cn/1812749510/12345
|
||||
|
||||
# 刷新直链缓存
|
||||
python3 scripts/123yunpan.py direct-link cache-refresh --file-id 12345
|
||||
|
||||
# 禁用直链
|
||||
python3 scripts/123yunpan.py direct-link disable --file-id 12345
|
||||
|
||||
# IP 白名单管理
|
||||
python3 scripts/123yunpan.py direct-link ip-list
|
||||
python3 scripts/123yunpan.py direct-link ip-switch --switch 1
|
||||
python3 scripts/123yunpan.py direct-link ip-update --ips "1.1.1.1" "2.2.2.2"
|
||||
|
||||
# 流量日志
|
||||
python3 scripts/123yunpan.py direct-link traffic-logs --file-id 12345
|
||||
```
|
||||
|
||||
### 离线下载
|
||||
|
||||
```bash
|
||||
# 创建离线下载任务
|
||||
python3 scripts/123yunpan.py offline create --url "https://example.com/file.zip" --parent-id 0
|
||||
|
||||
# 查询下载进度
|
||||
python3 scripts/123yunpan.py offline progress --task-id abc123
|
||||
```
|
||||
|
||||
### 图床 (OSS)
|
||||
|
||||
```bash
|
||||
python3 scripts/123yunpan.py oss list --parent-id 0 --limit 100
|
||||
python3 scripts/123yunpan.py oss detail --file-id 12345
|
||||
python3 scripts/123yunpan.py oss delete --file-id 12345
|
||||
python3 scripts/123yunpan.py oss move --file-id 12345 --target-parent-id 0
|
||||
python3 scripts/123yunpan.py oss copy --file-ids "123,456" --target-parent-id 0
|
||||
```
|
||||
|
||||
### 视频转码
|
||||
|
||||
```bash
|
||||
python3 scripts/123yunpan.py transcode list --parent-file-id 0 --limit 100
|
||||
python3 scripts/123yunpan.py transcode resolutions --file-id 12345
|
||||
python3 scripts/123yunpan.py transcode transcode --file-id 12345 --resolution 1080p
|
||||
python3 scripts/123yunpan.py transcode download --file-id 12345 --output /path/
|
||||
python3 scripts/123yunpan.py transcode download-m3u8 --file-id 12345 --output /path/
|
||||
```
|
||||
|
||||
### 其他
|
||||
|
||||
```bash
|
||||
# 查看配置
|
||||
python3 scripts/123yunpan.py config
|
||||
|
||||
# 验证配置有效性
|
||||
python3 scripts/123yunpan.py config check
|
||||
|
||||
# 配置导出
|
||||
python3 scripts/123yunpan.py config export --output config.json
|
||||
|
||||
# 配置导入
|
||||
python3 scripts/123yunpan.py config import --file config.json
|
||||
|
||||
# 获取用户信息
|
||||
python3 scripts/123yunpan.py user-info
|
||||
|
||||
# 解锁保险箱
|
||||
python3 scripts/123yunpan.py safebox unlock --password "your_password"
|
||||
|
||||
# ⭐ 空间占用报告(专业空间 + 标准空间)
|
||||
python3 scripts/123yunpan.py space
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📄 分享 Excel 列结构
|
||||
|
||||
`share create-batch` 在**有效链接数 > 10** 时自动生成 Excel,固定三列:
|
||||
|
||||
| 列名 | 说明 | 示例 |
|
||||
|------|------|------|
|
||||
| **文件名称** | 去分类前缀、去扩展名 | `Cinema 4D R20` |
|
||||
| **链接** | 分享/直链 URL,可点击超链接 | `https://www.123pan.com/s/JyAKVv-o6bI` |
|
||||
| **路径** | 云盘内完整相对路径(含文件名) | `软件/office/离线安装包/2021/ProPlus2021Retail.img` |
|
||||
|
||||
名称解析规则:`Cinema 4D/Cinema 4D R20.zip` → 名称 `Cinema 4D R20`、路径 `Cinema 4D/Cinema 4D R20.zip`。
|
||||
|
||||
> xlsx 由 `zipfile` + 手写 OOXML 生成,**不需要安装 openpyxl**。
|
||||
|
||||
---
|
||||
|
||||
## ⚡ 限流保护
|
||||
|
||||
CLI 内置端点级 QPS 表,自动限速,无需手动 `sleep`:
|
||||
|
||||
| 端点 | QPS |
|
||||
|------|-----|
|
||||
| `/api/v2/file/list` | **1.5**(最严格) |
|
||||
| `/api/v1/user/info` | 10 |
|
||||
| `/api/v1/access_token` | 10 |
|
||||
| `/api/v1/share/create` | 3 |
|
||||
|
||||
- **429 / 5xx** → 指数退避重试(最多 4 次)
|
||||
- **401** → 自动刷新 Token 后重试一次
|
||||
- 错误码会翻译为「原因 + 处置建议」再打印,不会只丢裸码
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ 注意事项
|
||||
|
||||
| 项目 | 说明 |
|
||||
|------|------|
|
||||
| 文件名限制 | 不能包含 `\\\"/:*?|><`,长度 < 256 字符 |
|
||||
| 上传限制 | 单文件最大 10GB(API 限制) |
|
||||
| 分享有效期 | 仅支持 0(永久)/ 1 / 7 / 30 天 |
|
||||
| 分享名称长度 | 付费分享要求 < 35 字符,CLI 自动截断 |
|
||||
| 多 ID 写法 | `--file-ids` / `--share-ids` 只接受一个值,多个 ID 要写成 `--file-ids "1,2,3"` |
|
||||
| `--json` 位置 | 全局选项,必须写在子命令之前(`--json config check`) |
|
||||
| transcode 参数 | 用 `--parent-file-id`,与 file 系列的 `--parent-id` 不同 |
|
||||
| 空间双配额 | **专业空间 / 标准空间独立**,任一超额都阻断上传,先看 `space` |
|
||||
| 审核驳回文件 | `status > 100` 无法分享,批量分享时自动剔除 |
|
||||
| Token 刷新 | 过期时自动刷新,无需手动处理 |
|
||||
| `fileIDList` 格式 | 必须是逗号分隔的**字符串**(`"123,456"`),不是数组 |
|
||||
| 分享链接格式 | `https://www.123pan.com/s/{shareKey}` |
|
||||
| 回收站 | v2 API 默认包含回收站文件,用 `--trashed` / `--show-trashed` 过滤 |
|
||||
| Excel 阈值 | 默认超过 **10** 个链接自动生成,可用 `--excel-threshold` / `--no-excel` 调整 |
|
||||
| `--no-color` | 禁用彩色输出,适用于日志记录和管道操作 |
|
||||
| `--json` | 所有命令支持 `--json` 输出 JSON 格式,便于脚本解析 |
|
||||
|
||||
---
|
||||
|
||||
## 📁 文件结构
|
||||
|
||||
```
|
||||
123yunpan/
|
||||
├── SKILL.md # AI 技能文档(完整 API 参考,含 Changelog)
|
||||
├── README.md # 用户文档(本文件)
|
||||
├── manifest.json # 技能清单(命令 / 参数 / 输出字段契约)
|
||||
├── scripts/
|
||||
│ └── 123yunpan.py # CLI 主程序(纯 Python 标准库)
|
||||
└── references/
|
||||
├── bulk-operations.md # 批量操作指南
|
||||
└── competitor-upload-patterns.md # 上传模式参考
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📚 相关链接
|
||||
|
||||
- [123云盘开放平台文档](https://123yunpan.yuque.com/org-wiki-123yunpan-muaork/cr6ced)
|
||||
- [123云盘官网](https://www.123pan.com)
|
||||
@@ -0,0 +1,746 @@
|
||||
---
|
||||
name: 123yunpan
|
||||
description: "Use when interacting with 123 Cloud Drive (123云盘) via its open API. Supports file management, upload, download, sharing, offline download, direct links, image hosting, and video transcoding."
|
||||
version: 1.3.0
|
||||
author: 暖心向阳335
|
||||
license: MIT
|
||||
metadata:
|
||||
hermes:
|
||||
tags: [123yunpan, cloud-drive, file-management, api, storage]
|
||||
related_skills: [bulk-downloading, resource-sharing-platform]
|
||||
---
|
||||
|
||||
# 123云盘 (123 Cloud Drive) Skill
|
||||
|
||||
## Agent Quick Start
|
||||
|
||||
**脚本路径**: `scripts/123yunpan.py`(相对于本 skill 目录)
|
||||
**首次使用**: 先跑 `config check` 确认凭证有效,再操作文件
|
||||
**输出格式**: 加 `--json` 获取单行 JSON,方便解析
|
||||
**文件 ID**: 所有操作都基于 `file_id`(数字),先 `file list` 或 `file search` 获取 ID
|
||||
|
||||
**⚠️ 操作规则**:
|
||||
1. **删除规则**: 用户要求删除文件时,必须先向用户展示文件列表(如"删除文件如下:A.txt、B.zip"),确认后再执行删除操作,删除后提醒"如需找回,请前往回收站恢复"
|
||||
2. **下载规则**: 下载完成后告知用户保存位置,如"文件已保存到:/path/to/file.pdf"
|
||||
3. **分享规则**: 分享少于 10 个文件时直接给出链接;**超过 10 个时导出 Excel 文件并发送给用户**(阈值可用 `--excel-threshold` 覆盖,或用 `--no-excel` 关闭自动导出)
|
||||
4. **搜索规则**: 用户未给出具体云盘位置只给了文件名,搜索到多个匹配结果时,列出文件让用户选择,不要自行决定
|
||||
5. **批量规则**: 批量操作前先告知用户数量和预计耗时,如"共 150 个文件,预计需要 2 分钟"
|
||||
6. **上传规则**: 用户未指定目标文件夹时,先列出根目录让用户选择上传位置
|
||||
7. **错误规则**: 遇到错误时给出解决方案,不要只报错。如"文件不存在"→提示检查 file_id 是否正确;"分享失败"→提示检查文件是否违规
|
||||
|
||||
```
|
||||
# 1. 验证配置(--json 是全局选项,必须写在子命令之前)
|
||||
python3 scripts/123yunpan.py --json config check
|
||||
|
||||
# 2. 查看根目录
|
||||
python3 scripts/123yunpan.py --json file list --parent-id 0
|
||||
|
||||
# 3. 搜索文件
|
||||
python3 scripts/123yunpan.py --json file search "关键词" --parent-id 0
|
||||
|
||||
# 4. 查看目录树(快速了解结构)
|
||||
python3 scripts/123yunpan.py file tree --parent-id 0 --max-depth 2
|
||||
```
|
||||
|
||||
## Common Workflows
|
||||
|
||||
### 🔍 "帮我找某个文件"
|
||||
```
|
||||
1. file search "关键词" --parent-id 0 --json → 获取 file_id
|
||||
2. file detail --file-id {id} --json → 确认文件信息
|
||||
3. file download --file-id {id} -o ./ → 下载
|
||||
4. 告知用户:文件已保存到:./xxx.pdf
|
||||
```
|
||||
|
||||
### 🔗 "把这个文件夹分享出去"
|
||||
```
|
||||
1. file tree --parent-id {folder_id} --max-depth 2 → 确认内容
|
||||
2. 少于 10 个:share create 逐个创建,直接给出链接
|
||||
3. 超过 10 个:share create-batch --parent-id {folder_id} --expire 0 -o links.xlsx → 导出 Excel
|
||||
```
|
||||
|
||||
### ⬆️ "上传文件到云盘"
|
||||
```
|
||||
1. 用户未指定位置 → file list --parent-id 0 --json → 列出根目录让用户选
|
||||
2. upload --file /path/to/file --parent-id {folder_id}
|
||||
3. 告知用户:文件已上传到:{folder_id}/xxx.pdf
|
||||
```
|
||||
|
||||
### ⬇️ "批量下载这些文件"
|
||||
```
|
||||
1. file search "关键词" --parent-id {root} --json → 获取 file_id 列表
|
||||
2. file download-batch --file-ids "id1,id2,id3" -o ./downloads/ → 批量下载
|
||||
```
|
||||
|
||||
### 📊 "看看空间占用情况"
|
||||
```
|
||||
1. space → 专业空间 / 标准空间明细(双配额)
|
||||
2. file storage-report --parent-id 0 --top 10 → 按文件夹分析+大文件排行
|
||||
```
|
||||
|
||||
### 🗑️ "删除文件"
|
||||
```
|
||||
1. file list --parent-id {folder_id} --search "关键词" --json → 找到 file_id
|
||||
2. 向用户展示将删除的文件列表,格式:删除文件如下:XXX.txt、XXX.zip
|
||||
3. file trash --file-ids "{id1},{id2}" → 移入回收站
|
||||
4. 提醒用户:如需找回,请前往回收站恢复
|
||||
```
|
||||
|
||||
> ⚠️ **删除操作必须先向用户展示文件列表,确认后再执行!**
|
||||
|
||||
### 🔄 "把配置迁移到另一台机器"
|
||||
```
|
||||
# 机器 A:
|
||||
config export -o config.json
|
||||
|
||||
# 机器 B:
|
||||
config import --file config.json
|
||||
config check → 验证导入成功
|
||||
```
|
||||
|
||||
## Overview
|
||||
|
||||
This skill provides a complete CLI wrapper for the [123 Cloud Drive Open Platform API](https://123yunpan.yuque.com/org-wiki-123yunpan-muaork/cr6ced). It covers **70+ API endpoints** across authentication, file management, upload, download, sharing, offline download, direct links, image hosting, and video transcoding.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
### 1. Purchase Developer Access Pack
|
||||
Visit https://www.123pan.com/member and purchase a developer access pack. You will receive `client_id` and `client_secret` via in-app message.
|
||||
|
||||
### 2. Configuration
|
||||
The skill requires three credentials stored in environment variables:
|
||||
|
||||
```bash
|
||||
# Required for API authentication
|
||||
export TWOPAN_CLIENT_ID="your_client_id_here"
|
||||
export TWOPAN_CLIENT_SECRET="your_client_secret_here"
|
||||
export TWOPAN_ACCESS_TOKEN="your_access_token_here"
|
||||
```
|
||||
|
||||
**If these are not set when the skill is first used**, the CLI will output a clear reminder:
|
||||
|
||||
```
|
||||
⚠️ 123云盘技能需要配置以下环境变量才能使用:
|
||||
export TWOPAN_CLIENT_ID="your_client_id"
|
||||
export TWOPAN_CLIENT_SECRET="your_client_secret"
|
||||
export TWOPAN_ACCESS_TOKEN="your_access_token"
|
||||
|
||||
获取方式:
|
||||
1. 访问 https://www.123pan.com/member 购买开发者权益包
|
||||
2. 通过 client_id + client_secret 调用 /api/v1/access_token 获取 access_token
|
||||
3. 将三个值设置为环境变量
|
||||
|
||||
或运行: 123yunpan.py setup
|
||||
```
|
||||
|
||||
### 3. Interactive Setup (Recommended)
|
||||
Run `123yunpan.py setup` without arguments for interactive credential entry:
|
||||
```
|
||||
$ python3 scripts/123yunpan.py setup
|
||||
⚠️ 未检测到 client_id
|
||||
获取方式:访问 https://www.123pan.com/member 购买开发者权益包
|
||||
请输入 client_id: █
|
||||
⚠️ 未检测到 client_secret
|
||||
请输入 client_secret: █
|
||||
正在获取 access_token...
|
||||
✅ 配置已保存!
|
||||
```
|
||||
If config already exists (from a previous setup or env vars), it skips prompting and uses the existing values. Non-interactive mode also works: `setup --client-id ID --client-secret SECRET`.
|
||||
|
||||
## CLI Usage
|
||||
|
||||
```bash
|
||||
# === Global Options ===
|
||||
# Add --no-color to disable colored output, --json to output raw JSON
|
||||
|
||||
# Setup / get token
|
||||
python3 scripts/123yunpan.py setup --client-id YOUR_ID --client-secret YOUR_SECRET
|
||||
|
||||
# Check config status
|
||||
python3 scripts/123yunpan.py config
|
||||
python3 scripts/123yunpan.py config check # 验证 token 有效性
|
||||
python3 scripts/123yunpan.py config export --output config.json
|
||||
python3 scripts/123yunpan.py config import --file config.json
|
||||
|
||||
# === File Management ===
|
||||
python3 scripts/123yunpan.py file list --parent-id 0 --limit 100
|
||||
python3 scripts/123yunpan.py file list --parent-id 0 --trashed # 只看回收站
|
||||
python3 scripts/123yunpan.py file list --parent-id 0 --show-trashed # 含回收站
|
||||
python3 scripts/123yunpan.py file search "Photoshop" --parent-id 27172609 --max-depth 3
|
||||
python3 scripts/123yunpan.py file tree --parent-id 0 --max-depth 3
|
||||
python3 scripts/123yunpan.py file storage-report --parent-id 0 --top 10
|
||||
python3 scripts/123yunpan.py file detail --file-id 12345
|
||||
python3 scripts/123yunpan.py file details-multi --file-ids "1,2,3"
|
||||
python3 scripts/123yunpan.py file list --search "keyword"
|
||||
python3 scripts/123yunpan.py file mkdir --name "New Folder" --parent-id 0
|
||||
python3 scripts/123yunpan.py file rename --file-id 12345 --new-name "renamed.txt"
|
||||
python3 scripts/123yunpan.py file rename-batch --file-ids "1,2,3" --new-names "a.txt,b.txt,c.txt"
|
||||
python3 scripts/123yunpan.py file move --file-ids "1,2,3" --target-parent-id 0
|
||||
python3 scripts/123yunpan.py file copy --file-id 12345 --target-parent-id 0
|
||||
python3 scripts/123yunpan.py file copy-batch --file-ids "1,2,3" --target-parent-id 0
|
||||
python3 scripts/123yunpan.py file copy-progress --task-id abc123
|
||||
python3 scripts/123yunpan.py file trash --file-ids "12345"
|
||||
python3 scripts/123yunpan.py file recover --file-id 12345
|
||||
python3 scripts/123yunpan.py file recover-by-path --file-id 12345 --target-parent-id 0 --filename "restored.txt"
|
||||
python3 scripts/123yunpan.py file download --file-id 12345 --output /path/to/save
|
||||
python3 scripts/123yunpan.py file download-batch --file-ids "1,2,3" --output-dir /path/to/save
|
||||
|
||||
# === Upload (V2 Recommended) ===
|
||||
python3 scripts/123yunpan.py upload --file /path/to/file.txt --parent-id 0
|
||||
|
||||
# === Space / User ===
|
||||
python3 scripts/123yunpan.py space # 空间占用报告(专业 + 标准,双配额)
|
||||
python3 scripts/123yunpan.py --json space # 机器可读版本
|
||||
python3 scripts/123yunpan.py user-info
|
||||
|
||||
# === Share Management ===
|
||||
python3 scripts/123yunpan.py share create --name "My Share" --file-ids "1,2,3" --expire 7
|
||||
# 超过 10 个链接自动生成 Excel(三列:文件名称 / 链接 / 路径)
|
||||
python3 scripts/123yunpan.py share create-batch --parent-id 27172609 --expire 0 -o result.xlsx
|
||||
# 覆盖自动导出阈值 / 关闭自动导出 / 断点续跑
|
||||
python3 scripts/123yunpan.py share create-batch --parent-id 27172609 --excel-threshold 50
|
||||
python3 scripts/123yunpan.py share create-batch --parent-id 27172609 --no-excel
|
||||
python3 scripts/123yunpan.py share create-batch --parent-id 27172609 --resume
|
||||
python3 scripts/123yunpan.py share list --limit 10
|
||||
python3 scripts/123yunpan.py share list --all-pages
|
||||
python3 scripts/123yunpan.py share stats --top 10
|
||||
# 注意:--share-ids / --file-ids 只接受单个值,多个 ID 必须写成一个逗号分隔的字符串
|
||||
python3 scripts/123yunpan.py share delete-batch --share-ids "1,2,3"
|
||||
python3 scripts/123yunpan.py share delete-batch --all -y # 删除全部(跳过确认)
|
||||
python3 scripts/123yunpan.py share update --share-ids "1,2" --traffic-switch 2
|
||||
python3 scripts/123yunpan.py share export --output shares.xlsx
|
||||
python3 scripts/123yunpan.py share export --output shares.json # 按扩展名自动判断格式
|
||||
python3 scripts/123yunpan.py share pay-create --name "Paid Share" --file-ids "1,2" --amount 10
|
||||
python3 scripts/123yunpan.py share pay-list --limit 10
|
||||
python3 scripts/123yunpan.py share pay-update --share-ids "1" --traffic-switch 1
|
||||
|
||||
# === Offline Download ===
|
||||
python3 scripts/123yunpan.py offline create --url "https://example.com/file.zip" --parent-id 0
|
||||
python3 scripts/123yunpan.py offline progress --task-id abc123
|
||||
python3 scripts/123yunpan.py offline list
|
||||
|
||||
# === Direct Link ===
|
||||
python3 scripts/123yunpan.py direct-link enable --file-id 12345
|
||||
python3 scripts/123yunpan.py direct-link disable --file-id 12345
|
||||
python3 scripts/123yunpan.py direct-link url --file-id 12345
|
||||
python3 scripts/123yunpan.py direct-link short-url --file-id 12345 # 短直链(更隐私)
|
||||
python3 scripts/123yunpan.py direct-link cache-refresh --file-id 12345
|
||||
python3 scripts/123yunpan.py direct-link ip-list
|
||||
python3 scripts/123yunpan.py direct-link ip-switch --switch 1
|
||||
python3 scripts/123yunpan.py direct-link ip-update --ips "1.1.1.1,2.2.2.2"
|
||||
python3 scripts/123yunpan.py direct-link offline-logs
|
||||
python3 scripts/123yunpan.py direct-link traffic-logs
|
||||
|
||||
# === Image Hosting (OSS) ===
|
||||
python3 scripts/123yunpan.py oss list --parent-id 0 --limit 100
|
||||
python3 scripts/123yunpan.py oss detail --file-id 12345
|
||||
python3 scripts/123yunpan.py oss delete --file-id 12345
|
||||
python3 scripts/123yunpan.py oss move --file-id 12345 --target-parent-id 0
|
||||
python3 scripts/123yunpan.py oss copy --file-ids "1,2,3" --target-parent-id 0
|
||||
python3 scripts/123yunpan.py oss copy-process --task-id abc123
|
||||
python3 scripts/123yunpan.py oss copy-fail --task-id abc123
|
||||
|
||||
# === Video Transcode ===
|
||||
# 注意:transcode 系列用 --parent-file-id,不是 --parent-id
|
||||
python3 scripts/123yunpan.py transcode list --parent-file-id 0 --limit 100
|
||||
python3 scripts/123yunpan.py transcode folder --parent-file-id 0
|
||||
python3 scripts/123yunpan.py transcode resolutions --file-id 12345
|
||||
python3 scripts/123yunpan.py transcode transcode --file-id 12345 --resolution 1080p
|
||||
python3 scripts/123yunpan.py transcode record --file-id 12345
|
||||
python3 scripts/123yunpan.py transcode result --file-id 12345
|
||||
python3 scripts/123yunpan.py transcode delete --file-id 12345
|
||||
python3 scripts/123yunpan.py transcode download --file-id 12345 --output /path/
|
||||
python3 scripts/123yunpan.py transcode download-m3u8 --file-id 12345 --output /path/
|
||||
python3 scripts/123yunpan.py transcode download-all --file-id 12345 --output /path/
|
||||
python3 scripts/123yunpan.py transcode upload-cloud --file-id 12345 --parent-id 0
|
||||
|
||||
# === Safebox ===
|
||||
python3 scripts/123yunpan.py safebox unlock --password "your_password"
|
||||
|
||||
# === Cache Management ===
|
||||
python3 scripts/123yunpan.py cache list # 列出所有缓存文件
|
||||
python3 scripts/123yunpan.py cache clear # 清除全部缓存
|
||||
python3 scripts/123yunpan.py cache clear --root-id 12345 # 清除指定文件夹的缓存
|
||||
python3 scripts/123yunpan.py cache info # 显示缓存目录信息
|
||||
```
|
||||
|
||||
## JSON Output Fields (`--json`)
|
||||
|
||||
加 `--json` 后所有命令输出单行 JSON,以下是主要命令的字段说明:
|
||||
|
||||
### `file list --json`
|
||||
```json
|
||||
{"code":0, "data":{"fileList":[
|
||||
{"fileId":12345, "filename":"报告.pdf", "size":1048576, "type":0,
|
||||
"parentFileID":0, "createAt":"2026-01-01T00:00:00Z", "trashed":0}
|
||||
], "lastFileId":12345}}
|
||||
```
|
||||
- `type`: 0=文件, 1=文件夹
|
||||
- `trashed`: 0=正常, 1=已删除
|
||||
- `size`: 字节数
|
||||
|
||||
### `file detail --json`
|
||||
```json
|
||||
{"fileId":12345, "filename":"报告.pdf", "size":1048576,
|
||||
"md5":"abc123...", "type":0, "parentFileID":0,
|
||||
"downloadUrl":"https://...", "createAt":"2026-01-01T00:00:00Z"}
|
||||
```
|
||||
|
||||
### `space --json`
|
||||
```json
|
||||
{"nickname": "暖心向阳335",
|
||||
"total_quota": 21474836480, "total_used": 9223372036854775807,
|
||||
"professional": {"used": 1.5e10, "quota": 2.0e10, "remain_days": 0,
|
||||
"permanent": true, "used_pct": 75.0},
|
||||
"standard": {"used": 1.2e9, "quota": 5.0e9, "remain_days": 0,
|
||||
"permanent": true, "used_pct": 24.0}}
|
||||
```
|
||||
- `professional` 满 → 开放平台上传 / 离线下载 / 转码入口被阻断
|
||||
- `remain_days` > 0 表示官方给的清理倒计时,超出后配额不可恢复
|
||||
|
||||
### `user-info --json`
|
||||
```json
|
||||
{"uid":1812749510, "nickname":"暖心向阳335",
|
||||
"spaceUsed":9223372036854775807, "spacePermanent":9223372036854775807,
|
||||
"vip":1}
|
||||
```
|
||||
- `spaceUsed`/`spacePermanent`: 字节数,用 `human_size()` 转换
|
||||
|
||||
### `share create --json`
|
||||
```json
|
||||
{"code":0, "data":{"shareID":5193007, "shareKey":"JyAKVv-o6bI"},
|
||||
"_shareUrl":"https://www.123pan.com/s/JyAKVv-o6bI"}
|
||||
```
|
||||
- `_shareUrl` 是 CLI 自动拼接的,不是 API 返回的
|
||||
|
||||
### `share list --json`
|
||||
```json
|
||||
{"code":0, "data":{"shareList":[
|
||||
{"shareId":5193007, "shareName":"分享",
|
||||
"shareKey":"JyAKVv-o6bI", "shareExpire":7,
|
||||
"previewCount":10, "downloadCount":5, "saveCount":2,
|
||||
"createAt":"2026-01-01T00:00:00Z"}
|
||||
], "lastShareId":5193007}}
|
||||
```
|
||||
|
||||
### `upload --json`
|
||||
```json
|
||||
{"code":0, "data":{"fileID":12345, "completed":true, "reuse":false}}
|
||||
```
|
||||
- `reuse`: true=秒传成功(文件已存在)
|
||||
|
||||
### 通用规则
|
||||
- 所有 API 返回格式: `{"code":0, "message":"ok", "data":{...}}`
|
||||
- `code=0` 表示成功,非 0 为错误
|
||||
- `file list` 和 `share list` 使用 `lastFileId`/`lastShareId` 做游标分页
|
||||
- 错误时返回: `{"code":错误码, "message":"错误描述"}`
|
||||
|
||||
## API Reference Summary
|
||||
|
||||
| Category | Endpoint | Method |
|
||||
|----------|----------|--------|
|
||||
| Cache | cache list | CLI |
|
||||
| Cache | cache clear | CLI |
|
||||
| Cache | cache info | CLI |
|
||||
| Auth | `/api/v1/access_token` | POST |
|
||||
| Auth (OAuth2) | `/api/v1/oauth2/access_token` | POST |
|
||||
| File List (v2) | `/api/v2/file/list` | GET |
|
||||
| File List (v1) | `/api/v1/file/list` | GET |
|
||||
| File Detail | `/api/v1/file/detail` | GET |
|
||||
| File Details (multi) | `/api/v1/file/infos` | POST |
|
||||
| Create Dir | `/upload/v1/file/mkdir` | POST |
|
||||
| Rename (single) | `/api/v1/file/name` | PUT |
|
||||
| Rename (batch) | `/api/v1/file/rename` | POST |
|
||||
| Move | `/api/v1/file/move` | POST |
|
||||
| Copy (single) | `/api/v1/file/copy` | POST |
|
||||
| Copy (batch) | `/api/v1/file/async/copy` | POST |
|
||||
| Copy Progress | `/api/v1/file/async/copy/process` | GET |
|
||||
| Trash | `/api/v1/file/trash` | POST |
|
||||
| Recover | `/api/v1/file/recover` | POST |
|
||||
| Recover (by path) | `/api/v1/file/recover/by_path` | POST |
|
||||
| Download Info | `/api/v1/file/download_info` | GET |
|
||||
| Upload V1 | `/upload/v1/file/create` | POST |
|
||||
| Upload V2 | `/upload/v2/file/create` | POST |
|
||||
| Upload Slice | `/upload/v2/file/slice` | POST |
|
||||
| Upload Complete | `/upload/v2/file/upload_complete` | POST |
|
||||
| Upload Domain | `/upload/v2/file/domain` | GET |
|
||||
| SHA1 Reuse | `/upload/v2/file/sha1_reuse` | POST |
|
||||
| Single Upload | `/upload/v2/file/single/create` | POST |
|
||||
| Share Create | `/api/v1/share/create` | POST |
|
||||
| Share List | `/api/v1/share/list` | GET |
|
||||
| Share Delete | `/api/v1/share/delete` | POST |
|
||||
| Share Update | `/api/v1/share/list/info` | PUT |
|
||||
| Pay Share Create | `/api/v1/share/content-payment/create` | POST |
|
||||
| Pay Share List | `/api/v1/share/payment/list` | GET |
|
||||
| Pay Share Update | `/api/v1/share/list/payment/info` | PUT |
|
||||
| Offline Download | `/api/v1/offline/download` | POST |
|
||||
| Offline Progress | `/api/v1/offline/download/process` | GET |
|
||||
| User Info | `/api/v1/user/info` | GET |
|
||||
| Direct Link Enable | `/api/v1/direct-link/enable` | POST |
|
||||
| Direct Link Disable | `/api/v1/direct-link/disable` | POST |
|
||||
| Direct Link URL | `/api/v1/direct-link/url` | GET |
|
||||
| Direct Link Cache | `/api/v1/direct-link/cache/refresh` | POST |
|
||||
| IP Blacklist Switch | `/api/v1/developer/config/forbide-ip/switch` | POST |
|
||||
| IP Blacklist Update | `/api/v1/developer/config/forbide-ip/update` | POST |
|
||||
| IP Blacklist List | `/api/v1/developer/config/forbide-ip/list` | GET |
|
||||
| Direct Link Logs | `/api/v1/direct-link/offline/logs` | GET |
|
||||
| Direct Link Traffic | `/api/v1/direct-link/log` | GET |
|
||||
| Image Upload Flow | `/upload/v1/oss/file/mkdir` | POST |
|
||||
| Image Create Dir | `/upload/v1/oss/file/mkdir` | POST |
|
||||
| Image Create | `/upload/v1/oss/file/create` | POST |
|
||||
| Image Upload URL | `/upload/v1/oss/file/get_upload_url` | POST |
|
||||
| Image Upload Done | `/upload/v1/oss/file/upload_complete` | POST |
|
||||
| Image Upload Result | `/upload/v1/oss/file/upload_async_result` | POST |
|
||||
| Image Copy | `/api/v1/oss/source/copy` | POST |
|
||||
| Image Copy Process | `/api/v1/oss/source/copy/process` | GET |
|
||||
| Image Copy Fail | `/api/v1/oss/source/copy/fail` | GET |
|
||||
| Image Move | `/api/v1/oss/file/move` | POST |
|
||||
| Image Delete | `/api/v1/oss/file/delete` | POST |
|
||||
| Image Detail | `/api/v1/oss/file/detail` | GET |
|
||||
| Image List | `/api/v1/oss/file/list` | POST |
|
||||
| Image Offline | `/api/v1/oss/offline/download` | POST |
|
||||
| Image Offline Process | `/api/v1/oss/offline/download/process` | GET |
|
||||
| Video Upload Cloud | `/api/v1/transcode/upload/from_cloud_disk` | POST |
|
||||
| Video Folder Info | `/api/v1/transcode/folder/info` | POST |
|
||||
| Video Resolutions | `/api/v1/transcode/video/resolutions` | POST |
|
||||
| Video Transcode List | `/api/v1/video/transcode/list` | GET |
|
||||
| Video Transcode | `/api/v1/transcode/video` | POST |
|
||||
| Video Record | `/api/v1/transcode/video/record` | POST |
|
||||
| Video Result | `/api/v1/transcode/video/result` | POST |
|
||||
| Video Delete | `/api/v1/transcode/delete` | POST |
|
||||
| Video Download | `/api/v1/transcode/file/download` | POST |
|
||||
| Video M3U8 Download | `/api/v1/transcode/m3u8_ts/download` | POST |
|
||||
| Video All Download | `/api/v1/transcode/file/download/all` | POST |
|
||||
| Safebox Unlock | `/api/v1/safebox/id` | GET |
|
||||
|
||||
## Short Direct Link (短直链)
|
||||
|
||||
除了分享链接和完整直链外,还可以生成**短直链** — 格式为 `https://{user_id}.v.123pan.cn/{user_id}/{file_id}`,不暴露文件名,更隐私友好。
|
||||
|
||||
### 生成短直链
|
||||
```bash
|
||||
# CLI 命令
|
||||
python3 scripts/123yunpan.py direct-link short-url --file-id 12345
|
||||
|
||||
# 输出示例
|
||||
{
|
||||
"short_url": "https://1812749510.v.123pan.cn/1812749510/12345",
|
||||
"user_id": 1812749510,
|
||||
"file_id": 12345
|
||||
}
|
||||
```
|
||||
|
||||
### 手动拼接(Python)
|
||||
```python
|
||||
import base64, json
|
||||
|
||||
def get_user_id_from_token(token):
|
||||
"""从 JWT token 解析 user_id,无需额外 API 调用"""
|
||||
parts = token.split(".")
|
||||
payload = parts[1] + "=" * (4 - len(parts[1]) % 4)
|
||||
return json.loads(base64.b64decode(payload)).get("id", 0)
|
||||
|
||||
user_id = get_user_id_from_token(access_token)
|
||||
short_url = f"https://{user_id}.v.123pan.cn/{user_id}/{file_id}"
|
||||
```
|
||||
|
||||
### 三种链接格式对比
|
||||
|
||||
| 格式 | 示例 | 特点 |
|
||||
|------|------|------|
|
||||
| 分享链接 | `https://www.123pan.com/s/JyAKVv-o6bI` | 需要登录/跳转,最常用 |
|
||||
| 完整直链 | `https://xxx.v.123pan.cn/xxx/xxx/filename.zip` | 直接下载,暴露文件名 |
|
||||
| 短直链 | `https://1812749510.v.123pan.cn/1812749510/12345` | 直接下载,不暴露文件名 |
|
||||
|
||||
> ⚠️ 短直链需要文件已在开启了直链功能的文件夹中,否则无法使用。
|
||||
|
||||
## Share URL Construction
|
||||
|
||||
The share create API returns `shareID` and `shareKey` — it does **NOT** return a full `shareUrl`. You must construct the URL yourself. The CLI supports two formats via `--url-format`:
|
||||
|
||||
| Format | CLI 参数 | URL 模式 | 示例 |
|
||||
|--------|----------|----------|------|
|
||||
| `short` (默认) | `--url-format short` | `https://www.123pan.com/s/{shareKey}` | `https://www.123pan.com/s/JyAKVv-o6bI` |
|
||||
| `uid` | `--url-format uid` | `https://{user_id}.share.123pan.cn/123pan/{shareKey}` | `https://666666666.share.123pan.cn/123pan/JyAKVv-o6bI` |
|
||||
|
||||
`uid` 格式自动从 JWT token 解析 `user_id`,无需额外 API 调用。解析失败时自动回退到 `short`。
|
||||
|
||||
```bash
|
||||
# 默认 short 格式
|
||||
python3 scripts/123yunpan.py share create --name "分享" --file-ids 123,456 --expire 7
|
||||
|
||||
# UID 格式
|
||||
python3 scripts/123yunpan.py share create --name "分享" --file-ids 123,456 --url-format uid
|
||||
|
||||
# 批量分享也支持
|
||||
python3 scripts/123yunpan.py share create-batch --parent-id 27172609 --url-format uid -o result.xlsx
|
||||
```
|
||||
|
||||
Example response from `POST /api/v1/share/create`:
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"message": "ok",
|
||||
"data": {
|
||||
"shareID": 5193007,
|
||||
"shareKey": "JyAKVv-o6bI"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Direct Link & Short Direct Link
|
||||
|
||||
The skill supports two types of direct links:
|
||||
|
||||
**Full direct link** (contains filename in URL):
|
||||
```
|
||||
https://{user_id}.v.123pan.cn/{user_id}/{file_id}/{filename}
|
||||
```
|
||||
|
||||
**Short direct link** (privacy-friendlier, no filename):
|
||||
```
|
||||
https://{user_id}.v.123pan.cn/{user_id}/{file_id}
|
||||
```
|
||||
|
||||
To generate short direct links, parse `user_id` from the JWT access token (no extra API call needed):
|
||||
```python
|
||||
import base64, json
|
||||
parts = token.split(".")
|
||||
payload = parts[1] + "=" * (4 - len(parts[1]) % 4)
|
||||
user_id = json.loads(base64.b64decode(payload))["id"]
|
||||
short_link = f"https://{user_id}.v.123pan.cn/{user_id}/{file_id}"
|
||||
```
|
||||
|
||||
> See `references/competitor-upload-patterns.md` for full upload workflow, SHA1 reuse, and WebDAV patterns.
|
||||
|
||||
## 空间规则:专业空间 / 标准空间(双配额)
|
||||
|
||||
根据官方 2026-09-24 公告,账号空间已拆分为**两套独立配额**,各有各的容量池与剩余天数:
|
||||
|
||||
| 空间类型 | 存放内容 | 与开放平台的关系 |
|
||||
|----------|----------|------------------|
|
||||
| **专业空间** | 本人持有的文件;≥10GB 文件;**直链**文件;转码文件;图床文件 | **任一项超额都会阻断开放平台的上传 / 离线下载 / 转码入口** |
|
||||
| **标准空间** | 转存(保存)的文件,<10GB;重复文件与回收站文件**不计入**容量 | 超额同样阻断上传 |
|
||||
|
||||
用 `space` 命令查看明细,CLI 会自动做两件事:
|
||||
|
||||
1. **使用率 ≥ 90%** → 红色预警,提示随时可能出现写入失败
|
||||
2. **专业空间剩余天数 > 0**(官方给定的清理倒计时,超出后配额不可恢复)→ 明确打印倒计时天数并标红
|
||||
|
||||
```bash
|
||||
python3 scripts/123yunpan.py --json space # 机器可读:professional / standard / total 三段
|
||||
```
|
||||
|
||||
`config check` 与 `upload` 命令在执行前都会做空间预检,专业空间已满时会直接给出阻断提示,避免上传中途才失败。
|
||||
|
||||
> ⚠️ **排查要点**:上传失败、离线下载任务创建失败,先看是**专业空间**还是**标准空间**超限 —— 两者是独立的,不能互相顶替。
|
||||
|
||||
## 分享链接 Excel 导出(三列)
|
||||
|
||||
`share create-batch` 在**有效链接数 > 阈值(默认 10)**时会自动生成 Excel 文件,列结构固定为三列:
|
||||
|
||||
| 列名 | 含义 | 示例 |
|
||||
|------|------|------|
|
||||
| **文件名称** | 不含分类前缀、不含扩展名的干净文件名 | `Cinema 4D R20`(原 `Cinema 4D/Cinema 4D R20.zip`) |
|
||||
| **链接** | 该文件的分享/直链 URL,单元格为可点击超链接 | `https://www.123pan.com/s/JyAKVv-o6bI` |
|
||||
| **路径** | 文件在云盘内的完整相对路径(含文件名与扩展名) | `软件/office/离线安装包/2021/ProPlus2021Retail.img` |
|
||||
|
||||
名称解析规则(`split_name_and_path()`):
|
||||
|
||||
1. 去掉扩展名 → `Cinema 4D R20.zip` → `Cinema 4D R20`
|
||||
2. 若去掉扩展名后的名字以「父目录名」开头,则补回父目录名避免歧义
|
||||
→ `Cinema 4D/Cinema 4D R20.zip` → `Cinema 4D R20`(而不是 ` R20`)
|
||||
|
||||
阈值控制:
|
||||
|
||||
```bash
|
||||
--excel-threshold N # 覆盖默认阈值(默认 10)
|
||||
--no-excel # 关闭自动导出,超过阈值也只在终端打印链接
|
||||
--resume # 断点续跑:读取 ~/.123yunpan_cache/share_batch_{root_id}.json,只处理未完成的文件
|
||||
```
|
||||
|
||||
> ✅ **零依赖**:xlsx 由 `zipfile` + 手写 OOXML XML 直接生成,不再需要 `openpyxl`。任何 Python 3 标准库环境都能出文件,不会出现"静默降级成 JSON"的情况。
|
||||
|
||||
## 限流与重试(QPS 保护)
|
||||
|
||||
官方开发须知明确给出各端点 QPS 上限,CLI 内置 `QPS_RULES` 表自动做节奏控制:
|
||||
|
||||
| 端点 | QPS |
|
||||
|------|-----|
|
||||
| `/api/v2/file/list` | **1.5(最严格)** |
|
||||
| `/api/v1/user/info` | 10 |
|
||||
| `/api/v1/access_token` | 10 |
|
||||
| `/api/v1/share/create` | 3 |
|
||||
| 其他 | 默认 1.39(保守兜底) |
|
||||
|
||||
配套行为:
|
||||
|
||||
- **自动节流**:每个端点按自己的 QPS 计算最小请求间隔,超限时 sleep 等待
|
||||
- **指数退避重试**:HTTP 429 或 5xx → `2^attempt` 秒后重试,最多 4 次
|
||||
- **401 自动刷新 Token**:带 `client_id/secret` 时自动刷新后重试一次
|
||||
- **审核驳回文件过滤**:`status > 100` 的文件无法分享,遍历阶段直接剔除,避免逐个试错
|
||||
|
||||
## Recursive File Listing
|
||||
|
||||
The `file list` API only returns **direct children** of a folder. To find all files in a multi-level directory tree, you must traverse recursively. See `references/bulk-operations.md` for BFS and layered traversal patterns.
|
||||
|
||||
**Quick pattern — find all files under a root folder:**
|
||||
```python
|
||||
from collections import deque
|
||||
|
||||
def find_all_files(root_id, list_folder_fn):
|
||||
queue = deque([(root_id, [])])
|
||||
all_files = []
|
||||
while queue:
|
||||
pid, path = queue.popleft()
|
||||
for item in list_folder_fn(pid):
|
||||
if item.get("trashed"):
|
||||
continue
|
||||
if item["type"] == 1: # folder
|
||||
queue.append((item["fileId"], path + [item["filename"]]))
|
||||
else: # file
|
||||
all_files.append({
|
||||
"fileId": item["fileId"],
|
||||
"filename": item["filename"],
|
||||
"path": "/".join(path),
|
||||
})
|
||||
return all_files
|
||||
```
|
||||
|
||||
> ⚠️ Deep traversal can involve hundreds of API calls. For 100+ folders, expect 5-10 minutes. Use checkpoint/resume (save progress to JSON every N items) to handle interruptions.
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
1. **API response format** (CRITICAL): The 123yunpan API returns `{code: 0, message: "ok", data: {...}}` — NOT `{result: {...}}`. Always parse the `data` field. Use `client._extract(resp)` — the CLI script handles both formats.
|
||||
|
||||
2. **Token expiry & auto-refresh**: access_token expires after a period. The CLI automatically refreshes on 401 responses using client_id/secret. For manual refresh, run `setup` or call `/api/v1/access_token`.
|
||||
|
||||
3. **File name restrictions**: Filenames must be <256 chars and cannot contain: `\\\\\\\"/:*?|><`
|
||||
|
||||
4. **Upload size**: Single file upload limit is 10GB via the API.
|
||||
|
||||
5. **`fileIDList` must be a string** (CRITICAL): When calling the share create API directly, `fileIDList` must be a **comma-separated string** (`"123,456"`), NOT a JSON array (`[123, 456]`). Passing an array causes the API to return "文件列表不能为空". The CLI handles this internally via `",".join(str(x) for x in file_ids)`. Max 100 file IDs per share. `shareExpire` must be 0, 1, 7, or 30.
|
||||
|
||||
6. **Share response has no `shareUrl`**: The share create response only returns `shareID` + `shareKey`. You must construct the URL. Use `--url-format short` (default, `https://www.123pan.com/s/{shareKey}`) or `--url-format uid` (`https://{user_id}.share.123pan.cn/123pan/{shareKey}`). See "Share URL Construction" section above.
|
||||
|
||||
7. **Pagination**: Use `lastFileId`/`lastShareId` for cursor-based pagination. When value is `-1`, you've reached the last page.
|
||||
|
||||
8. **Upload API domain routing** (CRITICAL): Upload endpoints are split across **two domains**:
|
||||
- Main API domain (`https://open-api.123pan.com`): `/upload/v2/file/create` (chunked create), `/upload/v1/file/create` (v1 create)
|
||||
- Upload domain (`https://openapi-upload.123242.com`): `/upload/v2/file/single/create` (single upload), `/upload/v2/file/slice` (chunked slice), `/upload/v2/file/upload_complete` (chunked complete)
|
||||
- Upload domain is obtained via **GET** `/upload/v2/file/domain` — it returns a **list** `["https://..."]`, not an object with `host` field.
|
||||
|
||||
9. **Single upload requires multipart/form-data** (CRITICAL): The `/upload/v2/file/single/create` endpoint does NOT accept JSON body. It requires `Content-Type: multipart/form-data` with fields `parentFileID`, `filename`, `etag`, `size` as text parts and the file content as a file part. Sending JSON results in a 500 server crash.
|
||||
|
||||
10. **`duplicate` parameter** (CRITICAL): The `duplicate` field in upload create causes "duplicate参数错误" when set to `0` (skip). **Do not send `duplicate` at all** (defaults to overwrite), or use `2` (overwrite) or `1` (rename). The value `0` is rejected by the API. The CLI now defaults to not sending this parameter (overwrite behavior).
|
||||
|
||||
11. **Trash behavior**: The v2 file list includes trashed files — filter by `trashed` field yourself. `trashed: 1` means the file is in trash.
|
||||
|
||||
12. **Batch operations rate limit**: The CLI now enforces per-endpoint QPS automatically via the built-in `QPS_RULES` table (see "限流与重试" section) — do NOT add manual `time.sleep()` on top of it, or you'll halve the throughput. For very long batches (2500+ files) still use checkpoint/resume: `--resume` reads `~/.123yunpan_cache/share_batch_{root_id}.json` and skips already-done files.
|
||||
|
||||
13. **Deep directory traversal**: Folders can be 4-5 levels deep. Use BFS with a queue for unknown structures, or layered traversal when structure is known. Each level requires a separate API call — 1400+ folders may take 10+ minutes. **Use BFS caching** (`~/.123yunpan_cache/`) to avoid repeated traversals — 57s → 0.15s on cache hit. CLI commands `file search` and `share create-batch` auto-use cache; add `--no-cache` to force refresh.
|
||||
|
||||
14. **Common error responses**:
|
||||
|
||||
| `code` | `message` | Meaning |
|
||||
|--------|-----------|---------|
|
||||
| `code` | HTTP | `message` | Meaning / 处理建议 |
|
||||
|--------|------|-----------|--------------------|
|
||||
| 0 | 200 | ok | Success |
|
||||
| 1 | 200 | 文件列表不能为空 | `fileIDList` is empty or wrong format (must be comma-separated string) |
|
||||
| 1 | 200 | duplicate参数错误 | `duplicate` field value is invalid (don't send `0`; use absent/`1`/`2`) |
|
||||
| 1 | 200 | 分片MD5不能为空 | Slice MD5 missing — check field name matches API expectation |
|
||||
| 1 | 200 | 该目录下文件名重复无法创建 | Filename already exists in target directory (use `--duplicate 2` to overwrite) |
|
||||
| 2 | 200 | 分享ID非法 | File ID doesn't exist or has no share permission |
|
||||
| 10 | 200 | 文件名已存在 | Target folder already has a file with that name |
|
||||
| 16 | 200 | 文件不存在 | File ID not found |
|
||||
| **20** | 200 | 分享文件涉嫌违规,禁止分享 | File content flagged by platform policy (e.g. `.alist` files) — CLI now filters `status > 100` files before sharing |
|
||||
| **401** | 401 | — | access_token 无效或已过期 → 自动刷新重试一次;仍失败则 `setup` 重新获取 |
|
||||
| **429** | 429 | — | 触发平台限流 → CLI 指数退避自动重试(最多 4 次),无需手动干预 |
|
||||
| **5066** | 200 | — | 文件不存在(开放平台语义),检查 fileId |
|
||||
| **5113** | 200 | — | 自用下载流量不足 → 升级 VIP 可获得无限流量下载 |
|
||||
|
||||
> CLI 内置 `ERROR_HINTS` 表,以上错误码会以中文人话 + 处置建议的形式打印,不会只丢一个裸错误码。
|
||||
|
||||
15. **审核驳回文件无法分享 (CRITICAL)**: Files with `status > 100` are rejected by content review and can never be shared. `share create-batch` filters them out during traversal instead of failing one-by-one. If a file you expect to share silently disappears from the result, check its `status` field first.
|
||||
|
||||
16. **专业空间 / 标准空间是两套独立配额 (CRITICAL)**: Per the 2026-09-24 announcement, exceeding **either** blocks open-platform uploads / offline downloads / transcoding. Standard space does **not** count duplicate files or trash files. Run `space` before a large upload to see which pool is actually full.
|
||||
|
||||
17. **Excel 导出是三列结构,不是「文件名 + 链接」**: Columns are fixed as 文件名称 / 链接 / 路径. The name column strips the folder prefix and extension (`Cinema 4D/Cinema 4D R20.zip` → `Cinema 4D R20`), while 路径 keeps the full relative path with filename (`软件/office/离线安装包/2021/ProPlus2021Retail.img`). Don't re-derive these columns downstream — read them as-is.
|
||||
|
||||
18. **Excel 导出零依赖,不要再去装 openpyxl**: xlsx is generated by hand via `zipfile` + OOXML XML (`write_xlsx()`). There is no `import openpyxl` anywhere in the script. Previously a missing openpyxl would silently degrade export to JSON — that can no longer happen.
|
||||
|
||||
19. **Packaging & versioning**: Version must be updated in **two places**: (1) `scripts/123yunpan.py` `VERSION = "X.Y.Z"`, (2) `SKILL.md` frontmatter (`version: X.Y.Z`), plus (3) `manifest.json` (`version`). No `zip`/`unzip` CLI — use Python's `zipfile` module. Package includes SKILL.md + README.md + scripts/ + references/ (no LICENSE per user preference). Exclude `__pycache__/`. Extraction for patching: `python3 -c "import zipfile; zipfile.ZipFile('pkg.zip').extractall('_tmp')"`. Repackaging: `cd _tmp && python3 -c "import zipfile,os; [zipfile.ZipFile('out.zip','w',zipfile.ZIP_DEFLATED).write(f, os.path.relpath(f,'.')) for r,d,fs in os.walk('.') for f in fs if '__pycache__' not in r]"`.
|
||||
|
||||
> ⚠️ Remember to also update the version in `scripts/123yunpan.py` `VERSION` when bumping the version in SKILL.md frontmatter and manifest.json.
|
||||
|
||||
20. **Argparse parameter names must match SKILL.md exactly**: Before adding/changing argparse arguments, cross-check every `--flag` in SKILL.md CLI examples. Mismatches (e.g. `--name` in code vs `--new-name` in docs) silently break user workflows. Use `parse_int_list()` (not `type=int, nargs="+"`) for all file-ID arguments to support comma-separated input (`--file-ids "1,2,3"`).
|
||||
- ⚠️ `--file-ids` / `--share-ids` / `--name` **只接受一个值**。多 ID 必须写成**一个带引号的逗号分隔字符串** `--file-ids "1,2,3"`;写成 `--file-ids 1 2 3` 会报 `unrecognized arguments: 2 3`,因为 `1 2 3` 会被当成多余的位置参数。
|
||||
- ⚠️ `--json` / `--no-color` 是**全局选项**,必须写在子命令**之前**(`--json config check`),写在后面报 `unrecognized arguments: --json`。
|
||||
- ⚠️ `transcode` 系列用 `--parent-file-id`,与 `file` 系列的 `--parent-id` **不是同一个参数**。
|
||||
- 可用 `.workbuddy/verify_doc_flags.py` 一键回归:它对文档里每一条 CLI 示例跑一遍 argparse 解析,找出「文档写了但代码没有」的选择器。
|
||||
|
||||
21. **`_refresh_token` uses imported names, not module paths**: The `ApiClient` methods use `Request` and `urlopen` (from `from urllib.request import ...`), NOT `urllib.request.Request`. Using the module path will crash with `NameError` since `urllib` is not imported as a module.
|
||||
|
||||
22. **BFS traversal optional params**: `bfs_list_files()` accepts `max_depth`, `use_cache`, `max_age` — always forward these from callers (e.g. `cmd_file_search`). Unused parameters silently cause unbounded traversal.
|
||||
|
||||
23. **Global options placement**: `--no-color` and `--json` must appear **before** the subcommand (e.g., `--json file list --parent-id 0`), not after. Placing them after the subcommand will cause argparse to ignore them.
|
||||
|
||||
24. **`file tree` vs recursive traversal**: `file tree` is a display-only convenience command that shows directory structure. For bulk operations (downloading, moving, sharing), use recursive BFS traversal or `file list` with pagination — `file tree` does not return file IDs suitable for batch operations.
|
||||
|
||||
25. **Share name is auto-truncated to 35 chars**: Paid shares require names < 35 characters; the CLI truncates automatically to avoid a 400 from the API. Don't pass a name longer than that expecting it round-trip.
|
||||
|
||||
26. **`share export` format is inferred from the file extension**: `--output shares.xlsx` → Excel, `--output shares.json` → JSON. There is **no `--format` flag** on `share export` (passing it yields `unrecognized arguments`). Both go through the same zero-dependency `write_xlsx()` / JSON path.
|
||||
|
||||
27. **`share pay-update` has no `--amount`**: Paid-share updates accept `--traffic-switch` / `--traffic-limit-switch` / `--traffic-limit` only. To change the price, recreate the share (or check the current API doc) rather than trying to patch `--amount`.
|
||||
|
||||
28. **`config import` safety**: Importing config will overwrite your current `~/.123yunpan_config.json`. Back up your existing config before importing. The CLI warns before overwriting if the file exists.
|
||||
|
||||
See `references/bulk-operations.md` for batch operation code patterns, BFS traversal, and background execution tips.
|
||||
|
||||
## Verification Checklist
|
||||
|
||||
- [ ] Environment variables `TWOPAN_CLIENT_ID`, `TWOPAN_CLIENT_SECRET`, `TWOPAN_ACCESS_TOKEN` are set
|
||||
- [ ] Run `123yunpan.py setup` to validate credentials and fetch token
|
||||
- [ ] Test with `123yunpan.py file list --parent-id 0 --limit 5`
|
||||
- [ ] Confirm token refresh works by checking `~/.123yunpan_config.json`
|
||||
- [ ] Run `123yunpan.py space` — two quotas (professional / standard) both reported
|
||||
- [ ] Run `123yunpan.py share create-batch --parent-id {id}` with > 10 files → xlsx auto-generated, 3 columns correct
|
||||
- [ ] Confirm no `openpyxl` dependency: `grep -c "import openpyxl" scripts/123yunpan.py` → `0`(脚本里只剩一句注释提到它)
|
||||
|
||||
## Changelog
|
||||
|
||||
### v1.3.0
|
||||
- **适配新空间规则**: 新增 `space` 命令,按官方 2026-09-24 公告拆分为**专业空间 / 标准空间**双配额展示;使用率 ≥90% 红色预警;专业空间剩余天数倒计时提示;`config check` 与 `upload` 增加空间预检
|
||||
- **改**: 分享链接 Excel 导出阈值 **20 → 10**(超过 10 个即自动生成 Excel),新增 `--excel-threshold` 覆盖与 `--no-excel` 关闭
|
||||
- **改**: Excel 列结构改为**三列** —— 文件名称(去分类前缀、去扩展名)/ 链接(超链接)/ 路径(云盘内完整相对路径)
|
||||
- **新增**: `--resume` 断点续跑,读取 `~/.123yunpan_cache/share_batch_{root_id}.json` 只处理未完成文件
|
||||
- **重写**: Excel 写入器改为**零依赖**(`zipfile` + 手写 OOXML),彻底移除 `openpyxl` 依赖,不再有"缺库静默降级 JSON"的问题
|
||||
- **重构**: API 客户端加入 `QPS_RULES` 端点级限流(`/api/v2/file/list` = 1.5 QPS 最严格)+ 429/5xx 指数退避重试(最多 4 次)+ 401 自动刷新 Token 重试一次
|
||||
- **新增**: `ERROR_HINTS` 中文错误码映射(401 / 429 / 5066 / 5113 / 20 等),失败时给出原因与处置建议
|
||||
- **修复**: 分享名称超 35 字符导致 API 400 → 主动截断;`status > 100`(审核驳回)文件在遍历阶段过滤,不再逐个试错
|
||||
|
||||
### v1.2.0 (2026-05-09)
|
||||
- **新增**: `--no-color` 和 `--json` 全局选项,支持禁用彩色输出和 JSON 格式输出
|
||||
- **新增**: `file tree` 命令 — 目录树展示,支持 `--parent-id` 和 `--max-depth` 参数
|
||||
- **新增**: `file download-batch` 命令 — 批量下载文件,支持 `--file-ids` 和 `--output-dir` 参数
|
||||
- **新增**: `file storage-report` 命令 — 存储分析,支持 `--parent-id` 和 `--top` 参数
|
||||
- **新增**: `offline list` 命令 — 列出离线下载任务(占位,待完善)
|
||||
- **新增**: `share export` 命令 — 导出分享记录到 Excel/JSON,支持 `--output` 和 `--format` 参数
|
||||
- **新增**: `config export` 命令 — 导出配置到 JSON,支持 `--output` 参数
|
||||
- **新增**: `config import` 命令 — 从 JSON 导入配置,支持 `--file` 参数
|
||||
|
||||
### v1.1.5 (2026-05-09)
|
||||
- **修复**: `upload` 命令的 `--duplicate` 参数默认值从 `0`(跳过)改为不发送(默认覆盖),避免API报错
|
||||
- **修复**: `_refresh_token` 方法使用了未导入的 `urllib.request` 模块,导致自动刷新Token时崩溃
|
||||
- **修复**: `file search --max-depth` 参数未实际生效,BFS遍历无深度限制
|
||||
- **修复**: 代码参数名与文档不一致(`--new-name`、`--target-parent-id`、`--ips`),统一到文档标准
|
||||
- **优化**: 所有 `--file-ids`/`--share-ids` 参数同时支持逗号分隔和空格分隔格式(如 `1,2,3` 或 `1 2 3`)
|
||||
- **新增**: `cache list` 命令 - 列出所有BFS缓存文件
|
||||
- **新增**: `cache clear` 命令 - 清除缓存(支持指定文件夹或全部清除)
|
||||
- **新增**: `cache info` 命令 - 显示缓存目录信息
|
||||
- **优化**: 批量操作进度显示增加ETA(预计剩余时间)估算
|
||||
- **优化**: 清理重复的import语句
|
||||
|
||||
### v1.1.4
|
||||
- 初始版本,支持70+ API端点
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"client_id": "YOUR_CLIENT_ID_HERE",
|
||||
"client_secret": "YOUR_CLIENT_SECRET_HERE",
|
||||
"access_token": "YOUR_ACCESS_TOKEN_HERE",
|
||||
"default_folder_id": 0
|
||||
}
|
||||
+180
@@ -0,0 +1,180 @@
|
||||
{
|
||||
"name": "123yunpan",
|
||||
"version": "1.3.0",
|
||||
"description": "123云盘开放平台 CLI 工具。支持文件管理、上传下载、分享链接、离线下载、直链、图床、视频转码等 70+ API 端点,专业/标准双空间配额报告,零依赖 Excel 导出。",
|
||||
"entry": "scripts/123yunpan.py",
|
||||
"config": {
|
||||
"env": [
|
||||
{
|
||||
"name": "TWOPAN_CLIENT_ID",
|
||||
"required": true,
|
||||
"description": "123云盘开发者 Client ID,从 https://www.123pan.com/member 购买开发者权益包获取"
|
||||
},
|
||||
{
|
||||
"name": "TWOPAN_CLIENT_SECRET",
|
||||
"required": true,
|
||||
"description": "123云盘开发者 Client Secret"
|
||||
},
|
||||
{
|
||||
"name": "TWOPAN_ACCESS_TOKEN",
|
||||
"required": true,
|
||||
"description": "123云盘 API Access Token(可通过 setup 命令自动获取)"
|
||||
}
|
||||
]
|
||||
},
|
||||
"security": {
|
||||
"warnings": [
|
||||
"access_token 和 client_secret 是敏感凭证,不要硬编码在脚本中",
|
||||
"批量删除操作(share delete-batch --all)会删除所有分享链接,请先用 --dry-run 预览",
|
||||
"深度目录遍历(100+ 文件夹)可能耗时 5-10 分钟,使用 BFS 缓存可加速",
|
||||
"专业空间或标准空间任一超额都会阻断开放平台上传,大批量上传前先跑 space 查看配额",
|
||||
"审核驳回文件(status > 100)无法分享,share create-batch 会在遍历阶段自动剔除",
|
||||
"WebDAV 上传有 30-90 秒索引延迟,上传后需轮询等待文件出现"
|
||||
],
|
||||
"suggested_isolation": {
|
||||
"config_file": "凭证存储在 ~/.123yunpan_config.json 或环境变量中",
|
||||
"env_file": "推荐使用环境变量,不要在代码中硬编码"
|
||||
}
|
||||
},
|
||||
"commands": {
|
||||
"config": {
|
||||
"description": "配置管理",
|
||||
"subcommands": ["show", "check", "export", "import"]
|
||||
},
|
||||
"file": {
|
||||
"description": "文件管理",
|
||||
"subcommands": ["list", "search", "tree", "detail", "details-multi", "mkdir", "rename", "rename-batch", "move", "copy", "copy-batch", "copy-progress", "trash", "recover", "recover-by-path", "download", "download-batch", "storage-report"]
|
||||
},
|
||||
"upload": {
|
||||
"description": "上传文件(V2 API,自动单次/分片上传)",
|
||||
"subcommands": []
|
||||
},
|
||||
"space": {
|
||||
"description": "空间占用报告(专业空间 / 标准空间双配额)"
|
||||
},
|
||||
"share": {
|
||||
"description": "分享管理",
|
||||
"subcommands": ["create", "create-batch", "list", "update", "stats", "delete-batch", "export", "pay-create", "pay-list", "pay-update"]
|
||||
},
|
||||
"offline": {
|
||||
"description": "离线下载",
|
||||
"subcommands": ["create", "progress", "list"]
|
||||
},
|
||||
"direct-link": {
|
||||
"description": "直链管理",
|
||||
"subcommands": ["enable", "disable", "url", "short-url", "cache-refresh", "ip-list", "ip-switch", "ip-update", "offline-logs", "traffic-logs"]
|
||||
},
|
||||
"oss": {
|
||||
"description": "图床管理",
|
||||
"subcommands": ["list", "detail", "delete", "move", "copy", "copy-process", "copy-fail"]
|
||||
},
|
||||
"transcode": {
|
||||
"description": "视频转码",
|
||||
"subcommands": ["list", "folder", "resolutions", "transcode", "record", "result", "delete", "download", "download-m3u8", "download-all", "upload-cloud"]
|
||||
},
|
||||
"safebox": {
|
||||
"description": "保险箱管理",
|
||||
"subcommands": ["unlock"]
|
||||
},
|
||||
"cache": {
|
||||
"description": "BFS 缓存管理",
|
||||
"subcommands": ["list", "clear", "info"]
|
||||
},
|
||||
"user-info": {
|
||||
"description": "获取用户信息"
|
||||
}
|
||||
},
|
||||
"global_options": {
|
||||
"--no-color": "禁用彩色输出(也支持 NO_COLOR 环境变量)",
|
||||
"--json": "JSON 机器可读输出(单行,无缩进)"
|
||||
},
|
||||
"inputs": {
|
||||
"file": {
|
||||
"type": "string",
|
||||
"description": "要上传的本地文件路径",
|
||||
"required": false
|
||||
},
|
||||
"parent-id": {
|
||||
"type": "number",
|
||||
"description": "目标文件夹 ID(默认 0 = 根目录)",
|
||||
"required": false,
|
||||
"default": 0
|
||||
},
|
||||
"file-ids": {
|
||||
"type": "string",
|
||||
"description": "文件 ID 列表(逗号分隔,如 1,2,3)",
|
||||
"required": false
|
||||
},
|
||||
"excel-threshold": {
|
||||
"type": "number",
|
||||
"description": "share create-batch 链接数超过该值时自动生成 Excel(默认 10)",
|
||||
"required": false,
|
||||
"default": 10
|
||||
},
|
||||
"url-format": {
|
||||
"type": "string",
|
||||
"description": "分享链接格式:official(默认) / web / uid / short",
|
||||
"required": false,
|
||||
"default": "official"
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
"file_list": {
|
||||
"description": "文件列表 JSON",
|
||||
"fields": {
|
||||
"fileId": "文件 ID(数字)",
|
||||
"filename": "文件名",
|
||||
"size": "文件大小(字节)",
|
||||
"type": "类型(0=文件, 1=文件夹)",
|
||||
"trashed": "是否在回收站(0=否, 1=是)",
|
||||
"parentFileID": "父文件夹 ID",
|
||||
"createAt": "创建时间"
|
||||
}
|
||||
},
|
||||
"share_create": {
|
||||
"description": "创建分享结果",
|
||||
"fields": {
|
||||
"shareID": "分享 ID",
|
||||
"shareKey": "分享密钥",
|
||||
"_shareUrl": "CLI 自动拼接的完整链接"
|
||||
}
|
||||
},
|
||||
"upload": {
|
||||
"description": "上传结果",
|
||||
"fields": {
|
||||
"fileID": "文件 ID",
|
||||
"completed": "是否完成",
|
||||
"reuse": "是否秒传(文件已存在)"
|
||||
}
|
||||
},
|
||||
"user_info": {
|
||||
"description": "用户信息",
|
||||
"fields": {
|
||||
"uid": "用户 ID",
|
||||
"nickname": "昵称",
|
||||
"spaceUsed": "已用空间(字节)",
|
||||
"vip": "是否 VIP"
|
||||
}
|
||||
},
|
||||
"space_report": {
|
||||
"description": "空间占用报告(专业 / 标准双配额)",
|
||||
"fields": {
|
||||
"total_quota": "总配额(字节)",
|
||||
"total_used": "总已用(字节)",
|
||||
"professional.used": "专业空间已用(本人文件 / ≥10GB / 直链 / 转码 / 图床)",
|
||||
"professional.quota": "专业空间配额",
|
||||
"professional.remain_days": "剩余清理天数,>0 时超出后配额不可恢复",
|
||||
"standard.used": "标准空间已用(转存文件,重复与回收站文件不计)",
|
||||
"standard.quota": "标准空间配额"
|
||||
}
|
||||
},
|
||||
"share_excel": {
|
||||
"description": "分享链接 Excel 三列结构",
|
||||
"columns": {
|
||||
"文件名称": "不含分类前缀与扩展名,如 Cinema 4D R20",
|
||||
"链接": "该文件的分享/直链 URL,单元格为可点击超链接",
|
||||
"路径": "云盘内完整相对路径,如 软件/office/离线安装包/2021/ProPlus2021Retail.img"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,283 @@
|
||||
# 123云盘批量操作指南
|
||||
|
||||
## 批量创建分享链接
|
||||
|
||||
为大量文件创建分享链接时,需要注意速率限制和断点续传。
|
||||
|
||||
### ⚠️ 关键:`fileIDList` 必须是字符串
|
||||
|
||||
API 要求 `fileIDList` 为**逗号分隔的字符串**,不是数组:
|
||||
```python
|
||||
# ✅ 正确
|
||||
body = {"fileIDList": "12345,67890", "shareName": "xxx", "shareExpire": 0}
|
||||
|
||||
# ❌ 错误 — 会返回 "文件列表不能为空"
|
||||
body = {"fileIDList": [12345, 67890], "shareName": "xxx", "shareExpire": 0}
|
||||
```
|
||||
|
||||
### 关键参数
|
||||
- API 端点: `POST /api/v1/share/create`
|
||||
- 单次最多 100 个 fileID
|
||||
- `shareExpire`: 0=永久, 1/7/30 天
|
||||
- 建议请求间隔: 0.5 秒(每秒不超过 2 个请求)
|
||||
|
||||
### ⚠️ 分享链接构建
|
||||
|
||||
API 返回 `shareID` + `shareKey`,**不会返回完整 URL**,需要自行拼接。支持两种格式:
|
||||
|
||||
```python
|
||||
# Short 格式 (默认)
|
||||
share_url = f"https://www.123pan.com/s/{share_key}"
|
||||
|
||||
# UID 格式 — 需要 user_id(可从 JWT token 解析,无需额外 API)
|
||||
share_url = f"https://{user_id}.share.123pan.cn/123pan/{share_key}"
|
||||
```
|
||||
|
||||
响应示例:
|
||||
```json
|
||||
{"code": 0, "message": "ok", "data": {"shareID": 5193007, "shareKey": "JyAKVv-o6bI"}}
|
||||
```
|
||||
→ Short: `https://www.123pan.com/s/JyAKVv-o6bI`
|
||||
→ UID: `https://666666666.share.123pan.cn/123pan/JyAKVv-o6bI`
|
||||
|
||||
### 完整示例:批量为文件创建分享并导出 Excel
|
||||
```python
|
||||
import json, time, urllib.request, urllib.parse
|
||||
from collections import deque
|
||||
|
||||
BASE = "https://open-api.123pan.com"
|
||||
|
||||
def get_token():
|
||||
with open(os.path.expanduser("~/.123yunpan_config.json")) as f:
|
||||
return json.load(f)["access_token"]
|
||||
|
||||
def api_get(path, params=None):
|
||||
url = BASE + path
|
||||
if params:
|
||||
url += "?" + urllib.parse.urlencode(params)
|
||||
req = urllib.request.Request(url, headers={
|
||||
"Authorization": f"Bearer {get_token()}", "Platform": "open_platform"})
|
||||
return json.loads(urllib.request.urlopen(req, timeout=30).read())
|
||||
|
||||
def api_post(path, body):
|
||||
url = BASE + path
|
||||
data = json.dumps(body).encode()
|
||||
req = urllib.request.Request(url, data=data, method="POST", headers={
|
||||
"Authorization": f"Bearer {get_token()}", "Platform": "open_platform",
|
||||
"Content-Type": "application/json"})
|
||||
return json.loads(urllib.request.urlopen(req, timeout=30).read())
|
||||
|
||||
def list_folder(parent_id):
|
||||
items, last_id = [], 0
|
||||
while True:
|
||||
params = {"parentFileId": parent_id, "limit": 100}
|
||||
if last_id:
|
||||
params["lastFileId"] = last_id
|
||||
resp = api_get("/api/v2/file/list", params)
|
||||
data = resp.get("data", {})
|
||||
items.extend(data.get("fileList", []))
|
||||
last_id = data.get("lastFileId", -1)
|
||||
if last_id == -1:
|
||||
break
|
||||
time.sleep(0.3)
|
||||
return items
|
||||
|
||||
def find_all_files(root_id):
|
||||
queue, all_files = deque([(root_id, [])]), []
|
||||
while queue:
|
||||
pid, path = queue.popleft()
|
||||
for item in list_folder(pid):
|
||||
if item.get("trashed"):
|
||||
continue
|
||||
if item["type"] == 1:
|
||||
queue.append((item["fileId"], path + [item["filename"]]))
|
||||
else:
|
||||
all_files.append({"fileId": item["fileId"],
|
||||
"filename": item["filename"], "path": "/".join(path)})
|
||||
return all_files
|
||||
|
||||
def create_share(file_id, name, user_id=None):
|
||||
"""创建分享并返回 URL。user_id 非空时使用 UID 格式。"""
|
||||
resp = api_post("/api/v1/share/create", {
|
||||
"fileIDList": str(file_id), # ← 字符串,不是数组!
|
||||
"shareName": name, "shareExpire": 0})
|
||||
data = resp.get("data", {})
|
||||
if resp.get("code") == 0:
|
||||
key = data["shareKey"]
|
||||
if user_id:
|
||||
return f"https://{user_id}.share.123pan.cn/123pan/{key}"
|
||||
return f"https://www.123pan.com/s/{key}"
|
||||
return None
|
||||
|
||||
# --- Usage ---
|
||||
all_files = find_all_files(root_folder_id)
|
||||
results = []
|
||||
for i, f in enumerate(all_files):
|
||||
url = create_share(f["fileId"], f["filename"])
|
||||
results.append({"name": f["filename"], "url": url})
|
||||
time.sleep(0.6)
|
||||
if (i + 1) % 10 == 0:
|
||||
json.dump(results, open("progress.json", "w"), ensure_ascii=False)
|
||||
```
|
||||
|
||||
### 断点续传模式
|
||||
```python
|
||||
# 保存进度
|
||||
results_file = "/tmp/shares_progress.json"
|
||||
if os.path.exists(results_file):
|
||||
results = json.load(open(results_file))
|
||||
done_ids = {r["fileId"] for r in results if r.get("share_url")}
|
||||
|
||||
# 跳过已完成的
|
||||
for item in all_files:
|
||||
if item["fileId"] in done_ids:
|
||||
continue
|
||||
# ... 创建分享 ...
|
||||
|
||||
# 每 N 条保存一次
|
||||
if done_count % 10 == 0:
|
||||
json.dump(results, open(results_file, "w"))
|
||||
```
|
||||
|
||||
### 速率控制
|
||||
```python
|
||||
import time
|
||||
time.sleep(0.5) # 每个请求后暂停
|
||||
# 每 5 个请求可额外暂停
|
||||
if (i + 1) % 5 == 0:
|
||||
time.sleep(1)
|
||||
```
|
||||
|
||||
## 短直链批量生成
|
||||
|
||||
为大量文件生成短直链(不暴露文件名),利用 JWT 解析 user_id,无需额外 API 调用:
|
||||
|
||||
```python
|
||||
import base64, json, time
|
||||
|
||||
def get_user_id_from_token(token):
|
||||
"""从 JWT token 解析 user_id"""
|
||||
parts = token.split(".")
|
||||
payload = parts[1] + "=" * (4 - len(parts[1]) % 4)
|
||||
return json.loads(base64.b64decode(payload)).get("id", 0)
|
||||
|
||||
# 一次性获取 user_id
|
||||
user_id = get_user_id_from_token(access_token)
|
||||
|
||||
# 批量生成短直链
|
||||
for file_id in file_ids:
|
||||
short_url = f"https://{user_id}.v.123pan.cn/{user_id}/{file_id}"
|
||||
print(f"File {file_id}: {short_url}")
|
||||
```
|
||||
|
||||
> ⚠️ 短直链要求文件在已开启直链功能的文件夹中。
|
||||
|
||||
## 深层目录遍历
|
||||
|
||||
123云盘文件夹可达 4-5 层深(学段/科目/版本/年级/文件)。
|
||||
|
||||
### BFS 遍历 + 缓存(推荐)
|
||||
|
||||
遍历大量文件夹时,API 调用次数 = 文件夹数量。100+ 文件夹需要 5-10 分钟。
|
||||
**关键优化:将遍历结果缓存到 JSON,5 分钟内复用,跳过所有 API 调用。**
|
||||
|
||||
实测效果:357 个文件遍历从 **57 秒 → 0.15 秒**(380 倍加速)。
|
||||
|
||||
```python
|
||||
import os, json, time
|
||||
from collections import deque
|
||||
|
||||
CACHE_DIR = os.path.expanduser("~/.123yunpan_cache")
|
||||
|
||||
def bfs_with_cache(client, root_id, max_age=300):
|
||||
"""BFS 遍历 + JSON 缓存"""
|
||||
cache_path = os.path.join(CACHE_DIR, f"files_{root_id}.json")
|
||||
os.makedirs(CACHE_DIR, exist_ok=True)
|
||||
|
||||
# 尝试加载缓存(默认 5 分钟有效)
|
||||
if os.path.exists(cache_path):
|
||||
age = time.time() - os.path.getmtime(cache_path)
|
||||
if age < max_age:
|
||||
with open(cache_path) as f:
|
||||
data = json.load(f)
|
||||
print(f"使用缓存 ({int(age)}秒前, {len(data['files'])} 个文件)")
|
||||
return data["files"]
|
||||
|
||||
# BFS 遍历
|
||||
queue = deque([(root_id, [])])
|
||||
all_files = []
|
||||
while queue:
|
||||
pid, path = queue.popleft()
|
||||
items = list_folder(pid) # 自动翻页
|
||||
for item in items:
|
||||
if item.get("trashed"):
|
||||
continue
|
||||
if item["type"] == 1:
|
||||
queue.append((item["fileId"], path + [item["filename"]]))
|
||||
else:
|
||||
all_files.append({
|
||||
"fileId": item["fileId"],
|
||||
"filename": item["filename"],
|
||||
"path": "/".join(path),
|
||||
"size": item.get("size", 0),
|
||||
})
|
||||
|
||||
# 保存缓存
|
||||
with open(cache_path, "w") as f:
|
||||
json.dump({"files": all_files, "ts": time.time()}, f, ensure_ascii=False)
|
||||
|
||||
return all_files
|
||||
```
|
||||
|
||||
> 缓存路径: `~/.123yunpan_cache/files_{root_id}.json`
|
||||
> 强制刷新: 删除缓存文件或传 `max_age=0`
|
||||
|
||||
### 分层遍历(已知结构时更快)
|
||||
```python
|
||||
# 已知 学段/科目/版本/年级/文件 结构
|
||||
for stage in list_folder(root_id):
|
||||
for subject in list_folder(stage["fileId"]):
|
||||
for version in list_folder(subject["fileId"]):
|
||||
for grade in list_folder(version["fileId"]):
|
||||
files = list_folder(grade["fileId"])
|
||||
```
|
||||
|
||||
### 翻页处理
|
||||
```python
|
||||
def list_folder(parent_id):
|
||||
items = []
|
||||
last_id = 0
|
||||
while True:
|
||||
params = {"parentFileId": parent_id, "limit": 100}
|
||||
if last_id:
|
||||
params["lastFileId"] = last_id
|
||||
resp = api_get("/api/v2/file/list", params)
|
||||
data = _extract(resp) # v1.2.0: always use _extract() for {code,data} wrappers
|
||||
items.extend(data.get("fileList", []))
|
||||
last_id = data.get("lastFileId", -1)
|
||||
if last_id == -1:
|
||||
break
|
||||
return items
|
||||
```
|
||||
|
||||
## 后台长时间运行
|
||||
|
||||
批量操作(如 2500+ 文件)需要 20+ 分钟,应在后台运行:
|
||||
|
||||
```bash
|
||||
python3 -u script.py 2>&1 | tee /tmp/progress.log
|
||||
```
|
||||
|
||||
监控进度:
|
||||
```bash
|
||||
tail -5 /tmp/progress.log
|
||||
```
|
||||
|
||||
## 环境限制:pip 不可用时安装 Python 包
|
||||
|
||||
系统 Python 受限(externally managed),用临时 venv:
|
||||
```bash
|
||||
uv venv /tmp/xlsx_env
|
||||
uv pip install openpyxl --python /tmp/xlsx_env/bin/python
|
||||
/tmp/xlsx_env/bin/python your_script.py
|
||||
```
|
||||
@@ -0,0 +1,124 @@
|
||||
# 123云盘上传模式 & 竞品分析
|
||||
|
||||
> 来源:分析 Skillhub 上 `123pan-upload` skill 的实现,提取可借鉴的模式。
|
||||
|
||||
## 短直链生成(Short Direct Link)
|
||||
|
||||
直链 URL 包含 user_id 和文件路径,较长且暴露用户信息。短直链只用 file_id,更隐私友好:
|
||||
|
||||
```python
|
||||
import base64, json
|
||||
|
||||
def get_user_id_from_token(token: str) -> int:
|
||||
"""从 JWT token 解析 user_id,无需额外 API 调用"""
|
||||
parts = token.split(".")
|
||||
if len(parts) >= 2:
|
||||
payload = parts[1]
|
||||
padding = 4 - len(payload) % 4
|
||||
if padding != 4:
|
||||
payload += "=" * padding
|
||||
data = json.loads(base64.b64decode(payload))
|
||||
return data.get("id", 0)
|
||||
return 0
|
||||
|
||||
def get_short_direct_link(user_id: int, file_id: int) -> str:
|
||||
return f"https://{user_id}.v.123pan.cn/{user_id}/{file_id}"
|
||||
```
|
||||
|
||||
**对比:**
|
||||
| 类型 | URL 格式 | 隐私性 | 可直链下载 |
|
||||
|------|---------|--------|-----------|
|
||||
| 分享链接 | `https://www.123pan.com/s/{shareKey}` | ✅ 好 | ❌ 需跳转 |
|
||||
| 完整直链 | `https://{user_id}.v.123pan.cn/{user_id}/{file_id}/{filename}` | ⚠️ 暴露路径 | ✅ |
|
||||
| 短直链 | `https://{user_id}.v.123pan.cn/{user_id}/{file_id}` | ⚠️ 暴露 user_id | ✅ |
|
||||
|
||||
## 上传流程(V2 API)
|
||||
|
||||
> ⚠️ **2026-05 验证**:以下流程经过实际 API 调用验证,修正了早期文档中的错误。
|
||||
|
||||
### 两个域名
|
||||
|
||||
| 域名 | 用途 | 端点 |
|
||||
|------|------|------|
|
||||
| `https://open-api.123pan.com` | 主 API(认证、文件管理、chunked create) | `/api/v1/*`, `/api/v2/*`, `/upload/v2/file/create` |
|
||||
| `https://openapi-upload.123242.com` | 上传专用(获取上传域名返回此值) | `/upload/v2/file/single/create`, `/upload/v2/file/slice`, `/upload/v2/file/upload_complete` |
|
||||
|
||||
获取上传域名:`GET /upload/v2/file/domain` → 返回 `["https://openapi-upload.123242.com"]`(列表,非对象)
|
||||
|
||||
### 单文件上传(<100MB)
|
||||
|
||||
```
|
||||
1. GET open-api.123pan.com/upload/v2/file/domain → 获取上传服务器地址
|
||||
2. POST openapi-upload.123242.com/upload/v2/file/single/create → multipart/form-data 上传
|
||||
```
|
||||
|
||||
**关键坑点**:
|
||||
- `single/create` **必须用 multipart/form-data**,不能用 JSON body。JSON 会导致 500 服务器崩溃。
|
||||
- 表单字段:`parentFileID`(text), `filename`(text), `etag`(text), `size`(text), `file`(file)
|
||||
- `duplicate` 字段:**不要传 `0`**(会导致 "duplicate参数错误")。不传默认覆盖,或传 `1`(重命名)/`2`(覆盖)。
|
||||
- 响应:`{"code":0, "data":{"fileID": 12345, "completed": true}}` — 无需额外上传步骤。
|
||||
|
||||
### 分片上传(100MB-10GB)
|
||||
|
||||
```
|
||||
1. POST open-api.123pan.com/upload/v2/file/create → 创建上传任务(主域名)
|
||||
2. POST openapi-upload.123242.com/upload/v2/file/slice → 逐片上传(上传域名)
|
||||
3. POST openapi-upload.123242.com/upload/v2/file/upload_complete → 完成上传(上传域名)
|
||||
```
|
||||
|
||||
**关键坑点**:
|
||||
- `create` 调用主 API 域名,不是上传域名!
|
||||
- `slice` 和 `upload_complete` 调用上传域名。
|
||||
- `slice` 请求体字段名为 `partMd5`(注意:不是 `sliceMD5`,后者会导致 500)。
|
||||
- 分片大小由 `create` 响应中的 `sliceSize` 决定(通常 16MB)。
|
||||
|
||||
### SHA1 秒传
|
||||
```python
|
||||
# create_file 响应中 reuse=true 表示文件已存在,跳过上传
|
||||
if create_result["reuse"]:
|
||||
return {"file_id": create_result["file_id"], ...}
|
||||
```
|
||||
|
||||
### 上传校验轮询
|
||||
```python
|
||||
# upload_complete 响应中 completed=false 时需轮询
|
||||
# 建议间隔:1 秒
|
||||
# 最大等待:~5 分钟/GB(10GB 文件约 50 分钟)
|
||||
# 文件大小越大,校验时间越长
|
||||
for attempt in range(max_retries):
|
||||
resp = api_post("/upload/v2/file/upload_complete", {"preuploadID": preupload_id})
|
||||
if resp["data"].get("completed"):
|
||||
return resp["data"] # 校验完成
|
||||
time.sleep(1) # 按 API 文档要求,每秒轮询一次
|
||||
```
|
||||
|
||||
### MD5 作为 Etag
|
||||
上传创建时需要提供文件的 MD5 作为 `etag` 字段:
|
||||
```python
|
||||
import hashlib
|
||||
etag = hashlib.md5(open(file_path, "rb").read()).hexdigest()
|
||||
```
|
||||
|
||||
## WebDAV 上传(>10GB 文件)
|
||||
|
||||
API 上传上限 10GB,超大文件可用 WebDAV + rclone:
|
||||
|
||||
```bash
|
||||
# 1. 配置 rclone(隔离配置,避免读取其他云服务凭证)
|
||||
rclone config --config ~/123pan-rclone.conf
|
||||
# 添加 "123pan-webdav" remote
|
||||
|
||||
# 2. 上传
|
||||
RCLONE_CONFIG=~/123pan-rclone.conf rclone copy /path/to/large/file 123pan-webdav:/folder -P
|
||||
|
||||
# 3. 上传后通过 API 查找文件(WebDAV 上传有 30-90 秒索引延迟)
|
||||
# 需要轮询 file/list 接口等待文件出现
|
||||
```
|
||||
|
||||
**注意:** WebDAV 上传后文件不会立即出现在 API 列表中,需要重试查找(最多 30 次,每次间隔 3 秒)。
|
||||
|
||||
## 安全实践
|
||||
|
||||
- rclone 配置隔离:用 `RCLONE_CONFIG` 环境变量指定独立配置文件
|
||||
- 凭证存储:用环境变量或 `.env` 文件,不要硬编码
|
||||
- Token 刷新:123yunpan.py CLI 已自动处理 401 响应时的 token 刷新
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user