--- 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端点