分离出core和cli,更新文档,打包脚本

This commit is contained in:
保清 2026-02-08 17:34:55 +08:00
parent 18fcfc2c01
commit cb737c0d95
6 changed files with 1077 additions and 1525 deletions

View File

@ -1,86 +0,0 @@
// ==UserScript==
// @name 123云盘下载辅助
// @namespace https://github.com/Bao-qing/123pan
// @version 0.3
// @description 123 Cloud Drive Unlimited Flow
// @match https://www.123pan.com/*
// @match https://www.123pan.cn/*
// @match https://www.123865.com/*
// @match https://www.123684.com/*
// @grant none
// @author Qing
// ==/UserScript==
(function () {
// 重写 XMLHttpRequest
const originalXHR = window.XMLHttpRequest;
function newXHR() {
const realXHR = new originalXHR();
realXHR.open = function (method, url, async, user, password) {
this._url = url; // 记录请求的 URL
return originalXHR.prototype.open.apply(this, arguments);
};
realXHR.setRequestHeader = function (header, value) {
let headers = {
"user-agent": "123pan/v2.4.0(Android_7.1.2;Xiaomi)",
//"loginuuid": generateUUIDHex(),
"platform": "android",
"app-version": "61",
"x-app-version": "2.4.0"
}
// 如果header在列表中则修改
if (header.toLowerCase() in headers) {
value = headers[header.toLowerCase()];
} else {
console.log('header:', header);
}
return originalXHR.prototype.setRequestHeader.apply(this, arguments);
};
// 拦截响应内容,修改 DownloadUrl以适应网页端下载
realXHR.send = function () {
const xhrInstance = this;
this.addEventListener('readystatechange', function () {
let origin_url;
let new_url_no_redirect;
let base64data;
if (xhrInstance.readyState === 4 && xhrInstance.status === 200) {
// 解析响应的 JSON
let responseText = xhrInstance.responseText;
let responseJSON = JSON.parse(responseText);
console.log('Original Response:', responseJSON);
// 修改 DownloadUrl
if (responseJSON.data && responseJSON.data.DownloadUrl) {
origin_url = responseJSON.data.DownloadUrl;
new_url_no_redirect = origin_url + "&auto_redirect=0";
base64data = btoa(new_url_no_redirect);
responseJSON.data.DownloadUrl = "https://web-pro2.123952.com/download-v2/?params=" + base64data + "&is_s3=0";
console.log('Modified DownloadUrl:', responseJSON.data.DownloadUrl);
}
// 将修改后的 JSON 转为字符串
let modifiedResponseText = JSON.stringify(responseJSON);
// 使用 defineProperty 重写 responseText
Object.defineProperty(xhrInstance, 'responseText', {
get: function () {
return modifiedResponseText;
}
});
console.log('Modified Response:', modifiedResponseText);
}
});
return originalXHR.prototype.send.apply(this, arguments);
};
return realXHR;
}
window.XMLHttpRequest = newXHR;
})();

1031
123pan.py

File diff suppressed because it is too large Load Diff

445
README.md
View File

@ -1,18 +1,43 @@
# 123Pan 下载工具 # 1、123Pan Cli工具
123 网盘命令行工具,支持列出文件、下载、上传、分享、删除、创建目录及回收站管理。Android 客户端或 Web 协议,便于在本地批量管理与下载文件。 123 网盘命令行工具,支持列出文件、下载、上传、分享、删除、创建目录及回收站管理。
安卓客户端协议不受下载流量限制(推荐使用)。 <!-- TOC -->
* [1、123Pan Cli工具](#1123pan-cli工具)
* [1.1 特性](#11-特性)
* [1.2 脚本环境要求](#12-脚本环境要求)
* [1.3 安装与运行](#13-安装与运行)
* [1.3.1 脚本运行](#131-脚本运行)
* [1.3.2 下载release版](#132-下载release版)
* [1.4 配置文件JSON](#14-配置文件json)
* [1.5 常用命令(交互式)](#15-常用命令交互式)
* [2、123Pan接口模块pan123_core.py](#2123pan接口模块pan123_corepy)
* [2.1 核心类:`Pan123Core`](#21-核心类pan123core)
* [2.1.1 属性说明](#211-属性说明)
* [2.1.2 方法清单](#212-方法清单)
* [2.1.2.1 1登录操作](#2121-1登录操作)
* [2.1.2.2 2配置管理](#2122-2配置管理)
* [2.1.2.3 3目录操作](#2123-3目录操作)
* [2.1.2.4 4文件操作](#2124-4文件操作)
* [2.1.2.5 5用户信息](#2125-5用户信息)
* [2.2 工具类:`Pan123Tool`](#22-工具类pan123tool)
* [2.2.1 属性说明](#221-属性说明)
* [2.2.2 方法清单](#222-方法清单)
* [2.2.2.1 1配置管理](#2221-1配置管理)
* [2.2.2.2 2文件下载](#2222-2文件下载)
* [2.2.2.3 3文件上传](#2223-3文件上传)
* [2.3 全局配置参数](#23-全局配置参数)
* [2.3.1 协议相关](#231-协议相关)
* [2.3.2 设备伪装](#232-设备伪装)
* [2.4 错误码说明](#24-错误码说明)
* [2.5 典型使用示例](#25-典型使用示例)
* [3、下载说明](#3下载说明)
* [4、注意事项](#4注意事项)
* [5、免责声明](#5免责声明)
<!-- TOC -->
123download.js 是网页端下载油猴脚本最初版本,仍然可以使用,仅保留最基本的解锁下载功能,不再更新。 ## 1.1 特性
可以参考其他项目:
[123云盘解锁 (@QingJ)](https://greasyfork.org/zh-CN/scripts/519353-123%E4%BA%91%E7%9B%98%E8%A7%A3%E9%94%81)
[123 云盘会员青春版 (@hmjz100)](https://greasyfork.org/zh-CN/scripts/513528-123-%E4%BA%91%E7%9B%98%E4%BC%9A%E5%91%98%E9%9D%92%E6%98%A5%E7%89%88)
## 特性
- 登录 / 登出 - 登录 / 登出
- 列出当前目录文件ls - 列出当前目录文件ls
- 切换目录cd与刷新refresh / re - 切换目录cd与刷新refresh / re
@ -26,28 +51,36 @@
- 协议切换protocol android|web - 协议切换protocol android|web
- 支持保存配置到 JSON 文件authorization、device/os、protocol 等) - 支持保存配置到 JSON 文件authorization、device/os、protocol 等)
## 脚本环境要求 ## 1.2 脚本环境要求
- Python 3.7+
- 依赖库requests - Python 3
安装: - 依赖库requests
```bash 安装:
pip install requests ```bash
``` pip install requests
```
## 1.3 安装与运行
### 1.3.1 脚本运行
## 安装与运行
### 脚本运行
1. 克隆或下载本仓库到本地。 1. 克隆或下载本仓库到本地。
2. 进入项目目录。 2. 进入项目目录。
3. 运行脚本: 3. 运行脚本:
```bash ```bash
python 123pan.py python pan123_cli.py
``` ```
启动后会提示输入用户名 / 密码,或自动读取配置文件(默认 `123pan_config.json``123pan.txt`,脚本内部根据传入参数使用该文件)。 启动后会提示输入用户名 / 密码,或自动读取配置文件(默认 `123pan_config.json`,脚本内部根据传入参数使用该文件)。
### 下载release版
根据系统下载对应的 release 版本,解压后运行 `123pan.exe`Windows`123pan`Linux ### 1.3.2 下载release版
## 配置文件JSON
根据系统下载对应的 release 版本,解压后运行 `123pan.exe`Windows`123pan`Linux
## 1.4 配置文件JSON
脚本会读取并保存一个配置文件(示例 `123pan_config.json`),保存登录状态与偏好,格式示例: 脚本会读取并保存一个配置文件(示例 `123pan_config.json`),保存登录状态与偏好,格式示例:
```json
```json
{ {
"userName": "your_username", "userName": "your_username",
"passWord": "your_password", "passWord": "your_password",
@ -55,112 +88,288 @@
"deviceType": "M2007J20CI", "deviceType": "M2007J20CI",
"osVersion": "Android_10", "osVersion": "Android_10",
"protocol": "android" "protocol": "android"
} }
``` ```
注意:保存密码或 token 到本地会有安全风险,请在可信环境下使用并妥善保护该文件。 注意:保存密码或 token 到本地会有安全风险,请在可信环境下使用并妥善保护该文件。
## 常用命令(交互式) ## 1.5 常用命令(交互式)
在脚本交互提示符中输入命令,部分带参数: 在脚本交互提示符中输入命令,部分带参数:
- 直接输入编号 | 指令 | 用法示例 | 功能说明 |
- 若编号对应文件夹 → 进入该文件夹 |-----------------------------|----------------------------------------|----------------------------------|
- 若编号对应文件 → 直接下载该文件 | 直接输入编号 | `3` | 若编号对应文件夹 → 进入该文件夹;若为文件 → 直接下载该文件 |
| ls | `ls` | 显示当前目录的文件与文件夹列表 |
- ls | cd [编号&#124;..&#124;/] | `cd 3`、`cd ..`、`cd /` | 切换目录:进入指定编号的文件夹、返回上级、返回根目录 |
- 显示当前目录的文件与文件夹列表。 | mkdir [名称] | `mkdir test` | 在当前目录创建文件夹 |
| upload [路径] | `upload C:\Users\you\Desktop\file.txt` | 上传文件到当前目录(仅支持单个文件) |
| rm [编号] | `rm 2` | 删除当前列表中指定编号的文件/文件夹(移入回收站) |
| share [编号 ...] | `share 2 4` | 为指定文件创建一个或多个分享链接,可设置提取码(可为空) |
| link [编号] | `link 3` | 获取指定文件的直链地址 |
| download / d [编号] | `download 5``d 5` | 下载指定编号的文件或文件夹(文件夹将递归下载) |
| recycle | `recycle` | 查看回收站内容,可恢复指定编号项或输入 clear 清空回收站 |
| refresh / re | `refresh``re` | 刷新当前目录列表 |
| reload | `reload` | 重新加载配置文件并刷新目录 |
| login / logout | `login`、`logout` | 手动登录或登出(清除授权信息) |
| clearaccount | clearaccount | 清除已登录账号(包括用户名和密码) |
| more | `more` | 当目录分页未加载完时,继续加载更多内容 |
| protocol [android&#124;web] | `protocol web` | 切换通信协议(如 android/web并可选择保存配置 |
| exit | `exit` | 退出程序 |
- cd [编号|..|/] ---
- 切换目录:
- cd 3 —— 进入当前列表第3项如果是文件夹
- cd .. —— 返回上级。
- cd / —— 返回根目录。
- mkdir [名称]
- 在当前目录创建文件夹。例如mkdir test
- upload [路径]
- 上传文件到当前目录。例如upload C:\Users\you\Desktop\file.txt
- 仅支持文件,暂不支持目录批量上传。
- rm [编号]
- 删除当前列表中的文件/文件夹会移动到回收站。例如rm 2
- share [编号 ...]
- 为一个或多个文件创建分享链接例如share 2 4
- 程序会提示输入提取码(可留空)。
- link [编号]
- 获取文件直链。例如link 3
- download / d [编号]
- 下载指定编号的文件或文件夹(如果是文件夹,会递归下载)。
- 例如download 5
- recycle
- 显示回收站内容,并可恢复或清空。
- 可输入编号恢复,或输入 clear 清空回收站。
- refresh / re
- 刷新当前目录列表。
- reload
- 重新加载配置文件并刷新目录。
- login / logout
- login手动登录使用当前配置或提示输入
- logout登出并清除授权信息保存配置时会写入空 token
- more
- 如果当前目录分页未加载完,输入 more 继续加载更多文件。
- protocol [android|web]
- 切换协议。例protocol web
- 切换后会重新初始化请求头,并可选择保存到配置文件。
- exit
- 退出程序。
---
交互示例: 交互示例:
```
/> cd demo
无效命令,使用 '..' 返回上级,'/' 返回根目录,或输入文件夹编号
/> cd 1
当前目录为空
/demo1> ls
当前目录为空
/demo1> mkdir test
目录 'test' 创建成功
/demo1> 1 ```
当前目录为空 /> cd demo
/demo1/test> upload 123pan.py 无效命令,使用 '..' 返回上级,'/' 返回根目录,或输入文件夹编号
上传进度: 100.0% /> cd 1
上传完成,正在验证... 当前目录为空
文件上传成功 /demo1> ls
------------------------------------------------------------ 当前目录为空
编号 类型 大小 名称 /demo1> mkdir test
------------------------------------------------------------ 目录 'test' 创建成功
1 文件 38.66 KB 123pan.py
============================================================
/demo1/test> 1 /demo1> 1
开始下载: 123pan.py 当前目录为空
进度: 100.0% | 38.66 KB/38.66 KB | 10.29 MB/s /demo1/test> upload 123pan.py
下载完成: 123pan.py 上传进度: 100.0%
/demo1/test> 上传完成,正在验证...
文件上传成功
------------------------------------------------------------
编号 类型 大小 名称
------------------------------------------------------------
1 文件 38.66 KB 123pan.py
============================================================
/demo1/test> 1
开始下载: 123pan.py
进度: 100.0% | 38.66 KB/38.66 KB | 10.29 MB/s
下载完成: 123pan.py
/demo1/test>
``` ```
## 下载说明 # 2、123Pan接口模块pan123_core.py
以下是基于代码实现的 **123pan 网盘 API**,按类结构分类说明:
---
### 2.1 核心类:`Pan123Core`
负责与 123pan 服务器的通信,管理认证状态、目录浏览、文件操作等核心逻辑。
#### 2.1.1 属性说明
| 属性名 | 类型 | 描述 |
|-----------------|------------|-----------------------------|
| `user_name` | str | 登录账号(手机号/用户名) |
| `password` | str | 登录密码 |
| `authorization` | str | 认证 Token登录后自动填充 |
| `protocol` | str | 请求协议(`"android"` 或 `"web"` |
| `cwd_id` | int | 当前工作目录 ID0 表示根目录) |
| `file_list` | List[dict] | 当前目录文件列表 |
| `nick_name` | str | 当前用户昵称 |
| `uid` | int | 当前用户 UID |
---
#### 2.1.2 方法清单
##### 2.1.2.1 1登录操作
| 方法名 | 参数说明 | 返回值类型 | 功能描述 |
|-------------------|------|--------|----------------------------|
| `login()` | 无 | Result | 使用 `user_name/password` 登录 |
| `logout()` | 无 | Result | 登出并清除 Token |
| `check_login()` | 无 | Result | 检查当前 Token 是否有效 |
| `clear_account()` | 无 | Result | 清除账号信息(不保存配置) |
##### 2.1.2.2 2配置管理
| 方法名 | 参数说明 | 返回值类型 | 功能描述 |
|--------------------------|-----------------------------------|--------|----------------------|
| `load_config(cfg)` | `cfg`: 包含账号信息的字典(见下方配置参数) | Result | 加载配置并更新实例状态 |
| `get_current_config()` | 无 | dict | 获取当前配置账号、Token、协议等 |
| `set_protocol(protocol)` | `protocol`: `"android"``"web"` | Result | 切换请求协议 |
##### 2.1.2.3 3目录操作
| 方法名 | 参数说明 | 返回值类型 | 功能描述 |
|-----------------------------------------------|---------------------------------------------------------|--------|--------------|
| `list_dir(parent_id=None, page=1, limit=100)` | `parent_id`: 父目录 ID<br>`page`: 页码<br>`limit`: 单页数量 | Result | 获取单页文件列表 |
| `list_dir_all(parent_id=None, limit=100)` | 同上 | Result | 获取全部文件(自动翻页) |
| `mkdir(name)` | `name`: 目录名 | Result | 在当前目录创建子目录 |
| `cd(folder_index)` | `folder_index`: `file_list` 中的目标文件夹下标 | Result | 进入目标文件夹 |
| `cd_up()` | 无 | Result | 返回上级目录 |
| `cd_root()` | 无 | Result | 返回根目录 |
| `trash(file_data, delete=True)` | `file_data`: 文件信息字典<br>`delete`: 是否删除True=删除False=恢复) | Result | 删除或恢复文件 |
| `list_recycle()` | 无 | Result | 获取回收站文件列表 |
##### 2.1.2.4 4文件操作
| 方法名 | 参数说明 | 返回值类型 | 功能描述 |
|-------------------------------------------------------------------------|---------------------------------------------------------------------------------|--------|-----------------|
| `upload_file(file_path, duplicate=0, on_progress=None)` | `file_path`: 本地文件路径<br>`duplicate`: 冲突策略0=报错1=覆盖2=保留)<br>`on_progress`: 进度回调 | Result | 上传文件(支持秒传和分块上传) |
| `get_download_url(index)` | `index`: `file_list` 中的目标文件下标 | Result | 获取文件直链(自动处理重定向) |
| `share(file_ids, share_pwd="", expiration="2099-12-12T08:00:00+08:00")` | `file_ids`: 文件 ID 列表<br>`share_pwd`: 提取码<br>`expiration`: 过期时间 | Result | 创建分享链接 |
##### 2.1.2.5 5用户信息
| 方法名 | 参数说明 | 返回值类型 | 功能描述 |
|-------------------|------|--------|------------------------|
| `get_user_info()` | 无 | Result | 获取当前用户信息UID、昵称、空间用量等 |
---
### 2.2 工具类:`Pan123Tool`
基于 `Pan123Core` 提供文件交互功能(依赖文件系统操作)。
#### 2.2.1 属性说明
| 属性名 | 类型 | 描述 |
|---------------|------------|-----------------------------------|
| `core` | Pan123Core | 关联的核心实例 |
| `config_file` | str | 配置文件路径(默认 `"123pan_config.json"` |
---
#### 2.2.2 方法清单
##### 2.2.2.1 1配置管理
| 方法名 | 参数说明 | 返回值类型 | 功能描述 |
|---------------------------|------|--------|------------|
| `load_config_from_file()` | 无 | Result | 从文件加载配置 |
| `save_config_to_file()` | 无 | Result | 将当前配置保存到文件 |
##### 2.2.2.2 2文件下载
| 方法名 | 参数说明 | 返回值类型 | 功能描述 |
|--------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------|--------|--------|
| `download_file(index, save_dir="download", on_progress=None, overwrite=False, skip_existing=False)` | `index`: 文件列表下标<br>`save_dir`: 保存路径<br>`on_progress`: 进度回调<br>`overwrite`: 是否覆盖<br>`skip_existing`: 是否跳过已存在文件 | Result | 下载单个文件 |
| `download_directory(directory, save_dir="download", on_progress=None, overwrite=False, skip_existing=False)` | `directory`: 目录信息字典<br>其他参数同上 | Result | 递归下载目录 |
##### 2.2.2.3 3文件上传
| 方法名 | 参数说明 | 返回值类型 | 功能描述 |
|---------------------------------------------------------|----------------------------|--------|-------------------|
| `upload_file(file_path, duplicate=0, on_progress=None)` | 同 `Pan123Core.upload_file` | Result | 上传文件(与 Core 方法一致) |
---
### 2.3 全局配置参数
#### 2.3.1 协议相关
| 参数名 | 默认值 | 描述 |
|-----------------------|----------------------------|-----------------|
| `API_BASE_URL` | `"https://www.123pan.com"` | API 根地址 |
| `TIMEOUT_DEFAULT` | `15` | 默认请求超时时间(秒) |
| `UPLOAD_CHUNK_SIZE` | `5*1024*1024` | 分块上传单块大小5MB |
| `DOWNLOAD_CHUNK_SIZE` | `8192` | 下载流式读取单块大小8KB |
#### 2.3.2 设备伪装
| 参数名 | 默认值 | 描述 |
|----------------|-----|-------------------|
| `DEVICE_TYPES` | 见代码 | 可选 Android 设备型号列表 |
| `OS_VERSIONS` | 见代码 | 可选 Android 系统版本列表 |
---
### 2.4 错误码说明
| 错误码 | 含义 | 可能触发场景 |
|------|--------|----------------------------|
| 0 | 操作成功 | 所有接口成功时返回 |
| -1 | 网络请求失败 | 连接超时、SSL 错误等 |
| 5060 | 文件名冲突 | 上传时 `duplicate=0` 且目标文件已存在 |
| 1 | 本地文件冲突 | 下载时目标文件已存在 |
---
### 2.5 典型使用示例
```python
import json
from pan123_core import Pan123Core, Pan123Tool, Pan123EventType, format_size
# 初始化核心对象Android 协议)
core = Pan123Core(
user_name="13800138000",
password="your_password",
protocol=Pan123Core.PROTOCOL_ANDROID
)
# 登录
result = core.login()
if result["code"] != 0:
raise Exception("登录失败")
# 创建工具类实例
tool = Pan123Tool(core)
# 下载文件
def _download_progress(data) -> None:
if data.get("type") == Pan123EventType.DOWNLOAD_PROGRESS:
downloaded = data.get("downloaded", 0)
total = data.get("total", 0)
speed = data.get("speed", 0)
if total > 0:
pct = downloaded / total * 100
print(
f"\r进度: {pct:.1f}% | {format_size(downloaded)}/{format_size(total)} | {format_size(int(speed))}/s",
end=" ",
flush=True,
)
elif data.get("type") == Pan123EventType.DOWNLOAD_START_FILE:
print(f"开始下载: {data.get('file_name', '未知文件')} ({format_size(data.get('file_size', 0))})")
elif data.get("type") == Pan123EventType.DOWNLOAD_START_DIRECTORY:
print(f"开始下载目录: {data.get('dir_name', '未知目录')}")
else:
print(json.dumps(data, indent=2))
result = tool.download_file(
index=0,
save_dir="downloads",
on_progress=lambda e: print(f"下载进度: {e['percent']:.2f}%")
)
# 上传文件
result = core.upload_file(
file_path="local_file.txt",
duplicate=1, # 覆盖已有文件
on_progress=None # ...
)
# 创建分享链接
result = core.share(
file_ids=[12345],
share_pwd="123456",
expiration="2026-12-31T23:59:59+08:00"
)
```
---
# 3、下载说明
- 下载到脚本所在目录的 `download` 文件夹,下载过程中使用临时后缀 `.123pan`,下载完成后会重命名为原文件名。 - 下载到脚本所在目录的 `download` 文件夹,下载过程中使用临时后缀 `.123pan`,下载完成后会重命名为原文件名。
- 如果文件已存在,会提示覆盖 / 跳过 / 全部覆盖 / 全部跳过等选项。 - 如果文件已存在,会提示覆盖 / 跳过 / 全部覆盖 / 全部跳过等选项。
## 注意事项 # 4、注意事项
- 本工具用于学习与自用场景,请勿用于违法用途。对任何滥用造成的后果,本人概不负责。 - 本工具用于学习与自用场景,请勿用于违法用途。对任何滥用造成的后果,本人概不负责。
- 模拟客户端协议可能存在账号或服务端策略风险,请谨慎使用。 - 模拟客户端协议可能存在账号或服务端策略风险,请谨慎使用。
- 建议不要在公用或不受信任的机器上保存明文密码或授权信息。 - 建议不要在公用或不受信任的机器上保存明文密码或授权信息。
## 免责声明 # 5、免责声明
本工具用于学习场景,请勿用于违法用途。对任何滥用造成的后果,作者概不负责。 本工具用于学习场景,请勿用于违法用途。对任何滥用造成的后果,作者概不负责。
任何未经允许的api调用都是不被官方允许的对于因此产生的账号风险、数据损失等后果自负。

View File

@ -1 +1 @@
pyinstaller -F 123pan.py --icon favicon.ico --clean --noconfirm pyinstaller -F pan123_cli.py --icon favicon.ico --clean --noconfirm

View File

@ -2,11 +2,12 @@
123pan 控制台交互界面 仅负责用户 IO所有业务调用 Pan123Core 123pan 控制台交互界面 仅负责用户 IO所有业务调用 Pan123Core
""" """
import json
import os import os
import sys import sys
from typing import List from typing import Dict
from pan123_core import Pan123Core, format_size, make_result from pan123_core import Pan123Core, Pan123Tool, Pan123EventType, format_size
# ──────────────── 颜色工具 ──────────────── # ──────────────── 颜色工具 ────────────────
@ -50,7 +51,9 @@ class Pan123CLI:
exit - 退出程序""" exit - 退出程序"""
def __init__(self, config_file: str = "123pan_config.json"): def __init__(self, config_file: str = "123pan_config.json"):
self.core = Pan123Core(config_file=config_file) self.config_file: str = config_file
self.core = Pan123Core()
self.tool = Pan123Tool(self.core)
self._download_mode: int = 0 # 0=询问, 3=全部覆盖, 4=全部跳过 self._download_mode: int = 0 # 0=询问, 3=全部覆盖, 4=全部跳过
# ──────────────── 启动 ──────────────── # ──────────────── 启动 ────────────────
@ -62,11 +65,36 @@ class Pan123CLI:
os.system("") os.system("")
self._print_banner() self._print_banner()
self._init_login() if not self._init_login():
print(colored("无法登录", Color.RED))
a = input("输入1重新输入账号和密码输入2清除登录信息其他键退出: ")
if a == "1":
user_name = input("请输入用户名: ")
password = input("请输入密码: ")
if not user_name or not password:
print("用户名和密码不能为空,程序退出")
return
self.core.load_config({
"userName": user_name,
"passWord": password,
"authorization": ""
})
self.save_config()
return self.run()
if a == "2":
self._do_clear_account()
return self.run()
return
self.save_config()
self.core.refresh() # 加载文件列表
self.core.get_user_info()
self._show_files()
while True: while True:
try: try:
prompt = colored(f"{self.core.cwd_path}>", Color.RED) + " " prompt = colored(f"{self.core.cwd_path}>", Color.RED) + " "
print(colored(f'用户:{self.core.nick_name}', Color.GREEN))
command = input(prompt).strip() command = input(prompt).strip()
if not command: if not command:
continue continue
@ -82,26 +110,39 @@ class Pan123CLI:
def _print_banner(self) -> None: def _print_banner(self) -> None:
print("=" * 60) print("=" * 60)
print("123网盘客户端".center(56)) print("123网盘CLI客户端".center(56))
print("=" * 60) print("=" * 60)
def _init_login(self) -> None: def _init_login(self) -> bool:
"""尝试加载配置 -> 尝试访问目录 -> 必要时登录""" """尝试加载配置 -> 尝试访问目录 -> 必要时登录"""
self.core.load_config() res = self.load_config()
r = self.core.init_login_state()
if r["code"] < 0:
print(colored("登录失败", Color.YELLOW))
print(r["message"])
return False
return True
r = self.core.refresh() def load_config(self) -> Dict:
if r["code"] != 0: """加载配置"""
# 需要登录 try:
if not self.core.user_name: with open(self.config_file, "r", encoding="utf-8") as f:
self.core.user_name = input("请输入用户名: ") cfg = json.load(f)
if not self.core.password: except FileNotFoundError:
self.core.password = input("请输入密码: ") user_name = input("请输入用户名: ")
lr = self.core.login() password = input("请输入密码: ")
self._print_result(lr) cfg = {
if lr["code"] == 0: "userName": user_name,
self.core.refresh() "passWord": password,
"authorization": ""
}
return self.core.load_config(cfg)
self._show_files() def save_config(self) -> None:
"""保存配置"""
cfg = self.core.get_current_config()
with open(self.config_file, "w", encoding="utf-8") as f:
json.dump(cfg, f, indent=2, ensure_ascii=False)
# ──────────────── 命令分发 ──────────────── # ──────────────── 命令分发 ────────────────
@ -185,6 +226,7 @@ class Pan123CLI:
def _do_logout(self) -> None: def _do_logout(self) -> None:
r = self.core.logout() r = self.core.logout()
self.save_config()
self._print_result(r) self._print_result(r)
def _do_clear_account(self) -> None: def _do_clear_account(self) -> None:
@ -192,6 +234,7 @@ class Pan123CLI:
confirm = input("确定要清除已登录账号信息吗?(y/N): ").strip().lower() confirm = input("确定要清除已登录账号信息吗?(y/N): ").strip().lower()
if confirm == 'y': if confirm == 'y':
r = self.core.clear_account() r = self.core.clear_account()
self.save_config()
self._print_result(r) self._print_result(r)
else: else:
print("操作已取消") print("操作已取消")
@ -275,7 +318,7 @@ class Pan123CLI:
return return
r = self.core.get_download_url(int(arg) - 1) r = self.core.get_download_url(int(arg) - 1)
if r["code"] == 0: if r["code"] == 0:
print(f"文件直链: {r['data']['url']}") print(f"文件直链: \n{r['data']['url']}")
else: else:
self._print_result(r) self._print_result(r)
@ -292,7 +335,7 @@ class Pan123CLI:
overwrite = self._download_mode == 3 overwrite = self._download_mode == 3
skip = self._download_mode == 4 skip = self._download_mode == 4
r = self.core.download_file( r = self.tool.download_file(
idx, idx,
on_progress=self._download_progress, on_progress=self._download_progress,
overwrite=overwrite, overwrite=overwrite,
@ -311,7 +354,7 @@ class Pan123CLI:
return return
elif choice == "3": elif choice == "3":
self._download_mode = 3 self._download_mode = 3
r = self.core.download_file(idx, on_progress=self._download_progress, overwrite=True) r = self.tool.download_file(idx, on_progress=self._download_progress, overwrite=True)
print() # 换行 print() # 换行
self._print_result(r) self._print_result(r)
@ -378,17 +421,29 @@ class Pan123CLI:
# ──────────────── 进度回调 ──────────────── # ──────────────── 进度回调 ────────────────
@staticmethod @staticmethod
def _download_progress(downloaded: int, total: int, speed: float) -> None: def _download_progress(data) -> None:
if total > 0: if data.get("type") == Pan123EventType.DOWNLOAD_PROGRESS:
pct = downloaded / total * 100 downloaded = data.get("downloaded", 0)
print( total = data.get("total", 0)
f"\r进度: {pct:.1f}% | {format_size(downloaded)}/{format_size(total)} | {format_size(int(speed))}/s", speed = data.get("speed", 0)
end=" ", if total > 0:
flush=True, pct = downloaded / total * 100
) print(
f"\r进度: {pct:.1f}% | {format_size(downloaded)}/{format_size(total)} | {format_size(int(speed))}/s",
end=" ",
flush=True,
)
elif data.get("type") == Pan123EventType.DOWNLOAD_START_FILE:
print(f"开始下载: {data.get('file_name', '未知文件')} ({format_size(data.get('file_size', 0))})")
elif data.get("type") == Pan123EventType.DOWNLOAD_START_DIRECTORY:
print(f"开始下载目录: {data.get('dir_name', '未知目录')}")
else:
print(json.dumps(data, indent=2))
@staticmethod @staticmethod
def _upload_progress(uploaded: int, total: int) -> None: def _upload_progress(data) -> None:
uploaded = data.get("uploaded", 0)
total = data.get("total", 0)
if total > 0: if total > 0:
pct = uploaded / total * 100 pct = uploaded / total * 100
print(f"\r上传进度: {pct:.1f}%", end="", flush=True) print(f"\r上传进度: {pct:.1f}%", end="", flush=True)
@ -397,4 +452,4 @@ class Pan123CLI:
# ──────────────── 入口 ──────────────── # ──────────────── 入口 ────────────────
if __name__ == "__main__": if __name__ == "__main__":
Pan123CLI().run() Pan123CLI().run()

File diff suppressed because it is too large Load Diff