lightpdf-aipdf-mcp 0.1.31__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,20 @@
1
+ # Python-generated files
2
+ __pycache__/
3
+ *.py[oc]
4
+ build/
5
+ dist/
6
+ wheels/
7
+ *.egg-info
8
+
9
+ # Virtual environments
10
+ .venv/
11
+ node_modules/
12
+
13
+ # macOS system files
14
+ .DS_Store
15
+
16
+ mcp_server/demo/
17
+ mcp_server/docs/
18
+ homepage/backend/uploads
19
+ .version
20
+ lightpdf-mcp.zip
@@ -0,0 +1,208 @@
1
+ Metadata-Version: 2.4
2
+ Name: lightpdf-aipdf-mcp
3
+ Version: 0.1.31
4
+ Summary: MCP Server for LightPDF AI-PDF
5
+ Author: LightPDF Team
6
+ License: Proprietary
7
+ Requires-Python: >=3.12
8
+ Requires-Dist: httpx-sse>=0.4.0
9
+ Requires-Dist: httpx>=0.28.1
10
+ Requires-Dist: mcp>=1.6.0
11
+ Requires-Dist: pydantic-settings>=2.8.1
12
+ Requires-Dist: pydantic>=2.11.2
13
+ Requires-Dist: python-dotenv>=1.1.0
14
+ Requires-Dist: uvicorn>=0.34.0
15
+ Description-Content-Type: text/markdown
16
+
17
+ # LightPDF AI助手 MCP Server
18
+
19
+ LightPDF AI助手 MCP Server 是一个基于 MCP (Model Control Protocol) 的文档格式转换工具,支持多种文档格式之间的相互转换以及PDF的各种处理功能。该工具需要在支持 MCP 功能的软件(如 Cursor)中配置使用。
20
+
21
+ ## 配置说明
22
+
23
+ 在 Cursor 中配置 LightPDF AI助手 MCP Server 工具需要修改 `~/.cursor/mcp.json` 文件。添加以下配置:
24
+
25
+ ```json
26
+ {
27
+ "mcpServers": {
28
+ "lightpdf": {
29
+ "command": "uvx",
30
+ "args": [
31
+ "lightpdf-aipdf-mcp@latest"
32
+ ],
33
+ "env": {
34
+ "API_KEY": "your_api_key_here"
35
+ }
36
+ }
37
+ }
38
+ }
39
+ ```
40
+
41
+ 配置说明:
42
+ - `command`: 使用 `uvx` 命令运行工具
43
+ - `args`: 命令行参数
44
+ - 使用最新版的 `lightpdf-aipdf-mcp` 包
45
+ - `env`: 环境变量配置
46
+ - `API_KEY`: 你的API密钥
47
+
48
+ 配置完成后,重启 Cursor 使配置生效。
49
+
50
+ ## 功能特性
51
+
52
+ - 支持多种文档格式的转换
53
+ - 支持本地文件和网络URL
54
+ - 支持批量转换
55
+ - 支持PDF文件的高级处理功能(水印去除/添加、添加页码、压缩、加密/解密等)
56
+ - 并发处理,提高转换效率
57
+ - 详细的转换进度和结果报告
58
+
59
+ ## 支持的格式
60
+
61
+ ### PDF转换为其他格式
62
+ - Word (DOCX)
63
+ - Excel (XLSX)
64
+ - PowerPoint (PPTX)
65
+ - 图片 (JPG, JPEG, PNG, BMP, GIF)
66
+ - HTML
67
+ - 文本 (TXT)
68
+
69
+ ### 其他格式转换为PDF
70
+ - Word (DOCX)
71
+ - Excel (XLSX)
72
+ - PowerPoint (PPTX)
73
+ - 图片 (JPG, JPEG, PNG, BMP, GIF)
74
+ - CAD (DWG)
75
+ - CAJ
76
+ - OFD
77
+
78
+ ## PDF处理功能
79
+
80
+ ### PDF水印处理
81
+ - 支持去除PDF文档中的水印
82
+ - 支持添加自定义文本水印
83
+ - 可设置水印位置、透明度、角度和应用页面范围
84
+
85
+ ### PDF页码添加
86
+ - 支持在PDF文档中添加页码
87
+ - 可自定义起始页码
88
+ - 可选择页码位置(左上、上中、右上、左下、下中、右下)
89
+ - 可设置页码边距
90
+
91
+ ### PDF编辑功能
92
+ - **拆分PDF**: 支持按页码范围拆分,或每页生成一个新文件
93
+ - **合并PDF**: 将多个PDF文件合并为一个
94
+ - **旋转PDF**: 支持90°、180°、270°旋转,可指定页面范围
95
+ - **压缩PDF**: 优化PDF文件大小,可调整图像质量
96
+
97
+ ### PDF安全功能
98
+ - **加密PDF**: 添加密码保护
99
+ - **解密PDF**: 移除密码保护(需提供原密码)
100
+
101
+ ## 使用方法
102
+
103
+ 1. 确保你使用的软件支持 MCP 功能(如 Cursor)
104
+ 2. 在软件中完成 LightPDF AI助手 MCP Server 工具的配置
105
+ 3. 调用相应的工具,指定输入文件和所需参数
106
+ 4. 处理完成后,系统会提供结果文件的在线下载链接
107
+
108
+ ### 格式转换
109
+ 使用 `convert_document` 工具,指定输入文件和目标格式。
110
+
111
+ ### PDF处理功能
112
+ - 去除水印: `remove_watermark`
113
+ - 添加水印: `add_watermark`
114
+ - 添加页码: `add_page_numbers`
115
+ - 拆分PDF: `split_pdf`
116
+ - 合并PDF: `merge_pdfs`
117
+ - 旋转PDF: `rotate_pdf`
118
+ - 压缩PDF: `compress_pdf`
119
+ - 加密PDF: `protect_pdf`
120
+ - 解密PDF: `unlock_pdf`
121
+
122
+ ## 参数说明
123
+
124
+ ### 格式转换工具 (convert_document)
125
+ - `files`: 要转换的文件对象列表
126
+ - `path`: 文件路径或URL
127
+ - `password`: 文档密码(如有)
128
+ - `format`: 目标格式,支持:
129
+ - `pdf`: 转换为PDF格式
130
+ - `docx`: 转换为Word格式
131
+ - `xlsx`: 转换为Excel格式
132
+ - `pptx`: 转换为PowerPoint格式
133
+ - `jpg`/`jpeg`: 转换为JPG格式
134
+ - `png`: 转换为PNG格式
135
+ - `bmp`: 转换为BMP格式
136
+ - `gif`: 转换为GIF格式
137
+ - `html`: 转换为HTML格式
138
+ - `txt`: 转换为文本格式
139
+
140
+ ### 去除水印工具 (remove_watermark)
141
+ - `files`: 要处理的PDF文件对象列表
142
+ - `path`: 文件路径或URL
143
+ - `password`: 文档密码(如有)
144
+
145
+ ### 添加水印工具 (add_watermark)
146
+ - `files`: 要处理的PDF文件对象列表
147
+ - `text`: 水印文本内容
148
+ - `position`: 水印位置,可选值包括"center"、"topleft"等
149
+ - `opacity`: 透明度(0.0-1.0)
150
+ - `deg`: 旋转角度
151
+ - `range`: 页面范围,如 "1,3,5-7"
152
+ - `layout`: 水印显示位置,"on"(在内容上)或"under"(在内容下)
153
+ - 可选的字体设置: `font_family`、`font_size`、`font_color`
154
+
155
+ ### 添加页码工具 (add_page_numbers)
156
+ - `files`: 要处理的PDF文件对象列表
157
+ - `start_num`: 起始页码,默认为1
158
+ - `position`: 页码位置(1-6),对应左上/上中/右上/左下/下中/右下
159
+ - `margin`: 页码边距,可选值为10/30/60,默认为30
160
+
161
+ ### 拆分PDF工具 (split_pdf)
162
+ - `files`: 要处理的PDF文件对象列表
163
+ - `split_type`: 拆分类型
164
+ - `every`: 每页拆分为一个文件
165
+ - `page`: 按页面规则拆分(默认)
166
+ - `pages`: 拆分页面规则,如 "1,3,5-7"
167
+ - `merge_all`: 是否合并拆分后的文件(0=否,1=是)
168
+
169
+ ### 合并PDF工具 (merge_pdfs)
170
+ - `files`: 要合并的PDF文件对象列表(至少两个文件)
171
+
172
+ ### 旋转PDF工具 (rotate_pdf)
173
+ - `files`: 要处理的PDF文件对象列表
174
+ - `angle`: 旋转角度,可选值为90、180、270
175
+ - `pages`: 页面范围,如 "1,3,5-7"
176
+
177
+ ### 压缩PDF工具 (compress_pdf)
178
+ - `files`: 要处理的PDF文件对象列表
179
+ - `image_quantity`: 图像质量(1-100),值越低压缩率越高,默认为60
180
+
181
+ ### 加密PDF工具 (protect_pdf)
182
+ - `files`: 要处理的PDF文件对象列表
183
+ - `password`: 要设置的新密码
184
+
185
+ ### 解密PDF工具 (unlock_pdf)
186
+ - `files`: 要处理的PDF文件对象列表
187
+ - `path`: 文件路径或URL
188
+ - `password`: 文档的当前密码(必需)
189
+
190
+ ## 注意事项
191
+
192
+ 1. 确保有足够的磁盘空间用于文件转换
193
+ 2. 对于网络URL,确保URL可访问且文件大小在合理范围内
194
+ 3. 批量转换时会自动控制并发数,避免过度占用系统资源
195
+ 4. 某些格式转换可能需要较长时间,请耐心等待
196
+ 5. 水印去除功能仅适用于PDF格式文件
197
+ 6. 复杂水印可能无法完全去除,效果取决于水印类型
198
+ 7. 所有PDF编辑功能只支持PDF格式的输入文件
199
+
200
+ ## 错误处理
201
+
202
+ 工具会提供详细的错误信息,常见的错误包括:
203
+
204
+ - API密钥未配置或无效
205
+ - 文件格式不支持
206
+ - 文件不存在或无法访问
207
+ - 网络连接问题
208
+ - PDF密码不正确或文件受保护
@@ -0,0 +1,192 @@
1
+ # LightPDF AI助手 MCP Server
2
+
3
+ LightPDF AI助手 MCP Server 是一个基于 MCP (Model Control Protocol) 的文档格式转换工具,支持多种文档格式之间的相互转换以及PDF的各种处理功能。该工具需要在支持 MCP 功能的软件(如 Cursor)中配置使用。
4
+
5
+ ## 配置说明
6
+
7
+ 在 Cursor 中配置 LightPDF AI助手 MCP Server 工具需要修改 `~/.cursor/mcp.json` 文件。添加以下配置:
8
+
9
+ ```json
10
+ {
11
+ "mcpServers": {
12
+ "lightpdf": {
13
+ "command": "uvx",
14
+ "args": [
15
+ "lightpdf-aipdf-mcp@latest"
16
+ ],
17
+ "env": {
18
+ "API_KEY": "your_api_key_here"
19
+ }
20
+ }
21
+ }
22
+ }
23
+ ```
24
+
25
+ 配置说明:
26
+ - `command`: 使用 `uvx` 命令运行工具
27
+ - `args`: 命令行参数
28
+ - 使用最新版的 `lightpdf-aipdf-mcp` 包
29
+ - `env`: 环境变量配置
30
+ - `API_KEY`: 你的API密钥
31
+
32
+ 配置完成后,重启 Cursor 使配置生效。
33
+
34
+ ## 功能特性
35
+
36
+ - 支持多种文档格式的转换
37
+ - 支持本地文件和网络URL
38
+ - 支持批量转换
39
+ - 支持PDF文件的高级处理功能(水印去除/添加、添加页码、压缩、加密/解密等)
40
+ - 并发处理,提高转换效率
41
+ - 详细的转换进度和结果报告
42
+
43
+ ## 支持的格式
44
+
45
+ ### PDF转换为其他格式
46
+ - Word (DOCX)
47
+ - Excel (XLSX)
48
+ - PowerPoint (PPTX)
49
+ - 图片 (JPG, JPEG, PNG, BMP, GIF)
50
+ - HTML
51
+ - 文本 (TXT)
52
+
53
+ ### 其他格式转换为PDF
54
+ - Word (DOCX)
55
+ - Excel (XLSX)
56
+ - PowerPoint (PPTX)
57
+ - 图片 (JPG, JPEG, PNG, BMP, GIF)
58
+ - CAD (DWG)
59
+ - CAJ
60
+ - OFD
61
+
62
+ ## PDF处理功能
63
+
64
+ ### PDF水印处理
65
+ - 支持去除PDF文档中的水印
66
+ - 支持添加自定义文本水印
67
+ - 可设置水印位置、透明度、角度和应用页面范围
68
+
69
+ ### PDF页码添加
70
+ - 支持在PDF文档中添加页码
71
+ - 可自定义起始页码
72
+ - 可选择页码位置(左上、上中、右上、左下、下中、右下)
73
+ - 可设置页码边距
74
+
75
+ ### PDF编辑功能
76
+ - **拆分PDF**: 支持按页码范围拆分,或每页生成一个新文件
77
+ - **合并PDF**: 将多个PDF文件合并为一个
78
+ - **旋转PDF**: 支持90°、180°、270°旋转,可指定页面范围
79
+ - **压缩PDF**: 优化PDF文件大小,可调整图像质量
80
+
81
+ ### PDF安全功能
82
+ - **加密PDF**: 添加密码保护
83
+ - **解密PDF**: 移除密码保护(需提供原密码)
84
+
85
+ ## 使用方法
86
+
87
+ 1. 确保你使用的软件支持 MCP 功能(如 Cursor)
88
+ 2. 在软件中完成 LightPDF AI助手 MCP Server 工具的配置
89
+ 3. 调用相应的工具,指定输入文件和所需参数
90
+ 4. 处理完成后,系统会提供结果文件的在线下载链接
91
+
92
+ ### 格式转换
93
+ 使用 `convert_document` 工具,指定输入文件和目标格式。
94
+
95
+ ### PDF处理功能
96
+ - 去除水印: `remove_watermark`
97
+ - 添加水印: `add_watermark`
98
+ - 添加页码: `add_page_numbers`
99
+ - 拆分PDF: `split_pdf`
100
+ - 合并PDF: `merge_pdfs`
101
+ - 旋转PDF: `rotate_pdf`
102
+ - 压缩PDF: `compress_pdf`
103
+ - 加密PDF: `protect_pdf`
104
+ - 解密PDF: `unlock_pdf`
105
+
106
+ ## 参数说明
107
+
108
+ ### 格式转换工具 (convert_document)
109
+ - `files`: 要转换的文件对象列表
110
+ - `path`: 文件路径或URL
111
+ - `password`: 文档密码(如有)
112
+ - `format`: 目标格式,支持:
113
+ - `pdf`: 转换为PDF格式
114
+ - `docx`: 转换为Word格式
115
+ - `xlsx`: 转换为Excel格式
116
+ - `pptx`: 转换为PowerPoint格式
117
+ - `jpg`/`jpeg`: 转换为JPG格式
118
+ - `png`: 转换为PNG格式
119
+ - `bmp`: 转换为BMP格式
120
+ - `gif`: 转换为GIF格式
121
+ - `html`: 转换为HTML格式
122
+ - `txt`: 转换为文本格式
123
+
124
+ ### 去除水印工具 (remove_watermark)
125
+ - `files`: 要处理的PDF文件对象列表
126
+ - `path`: 文件路径或URL
127
+ - `password`: 文档密码(如有)
128
+
129
+ ### 添加水印工具 (add_watermark)
130
+ - `files`: 要处理的PDF文件对象列表
131
+ - `text`: 水印文本内容
132
+ - `position`: 水印位置,可选值包括"center"、"topleft"等
133
+ - `opacity`: 透明度(0.0-1.0)
134
+ - `deg`: 旋转角度
135
+ - `range`: 页面范围,如 "1,3,5-7"
136
+ - `layout`: 水印显示位置,"on"(在内容上)或"under"(在内容下)
137
+ - 可选的字体设置: `font_family`、`font_size`、`font_color`
138
+
139
+ ### 添加页码工具 (add_page_numbers)
140
+ - `files`: 要处理的PDF文件对象列表
141
+ - `start_num`: 起始页码,默认为1
142
+ - `position`: 页码位置(1-6),对应左上/上中/右上/左下/下中/右下
143
+ - `margin`: 页码边距,可选值为10/30/60,默认为30
144
+
145
+ ### 拆分PDF工具 (split_pdf)
146
+ - `files`: 要处理的PDF文件对象列表
147
+ - `split_type`: 拆分类型
148
+ - `every`: 每页拆分为一个文件
149
+ - `page`: 按页面规则拆分(默认)
150
+ - `pages`: 拆分页面规则,如 "1,3,5-7"
151
+ - `merge_all`: 是否合并拆分后的文件(0=否,1=是)
152
+
153
+ ### 合并PDF工具 (merge_pdfs)
154
+ - `files`: 要合并的PDF文件对象列表(至少两个文件)
155
+
156
+ ### 旋转PDF工具 (rotate_pdf)
157
+ - `files`: 要处理的PDF文件对象列表
158
+ - `angle`: 旋转角度,可选值为90、180、270
159
+ - `pages`: 页面范围,如 "1,3,5-7"
160
+
161
+ ### 压缩PDF工具 (compress_pdf)
162
+ - `files`: 要处理的PDF文件对象列表
163
+ - `image_quantity`: 图像质量(1-100),值越低压缩率越高,默认为60
164
+
165
+ ### 加密PDF工具 (protect_pdf)
166
+ - `files`: 要处理的PDF文件对象列表
167
+ - `password`: 要设置的新密码
168
+
169
+ ### 解密PDF工具 (unlock_pdf)
170
+ - `files`: 要处理的PDF文件对象列表
171
+ - `path`: 文件路径或URL
172
+ - `password`: 文档的当前密码(必需)
173
+
174
+ ## 注意事项
175
+
176
+ 1. 确保有足够的磁盘空间用于文件转换
177
+ 2. 对于网络URL,确保URL可访问且文件大小在合理范围内
178
+ 3. 批量转换时会自动控制并发数,避免过度占用系统资源
179
+ 4. 某些格式转换可能需要较长时间,请耐心等待
180
+ 5. 水印去除功能仅适用于PDF格式文件
181
+ 6. 复杂水印可能无法完全去除,效果取决于水印类型
182
+ 7. 所有PDF编辑功能只支持PDF格式的输入文件
183
+
184
+ ## 错误处理
185
+
186
+ 工具会提供详细的错误信息,常见的错误包括:
187
+
188
+ - API密钥未配置或无效
189
+ - 文件格式不支持
190
+ - 文件不存在或无法访问
191
+ - 网络连接问题
192
+ - PDF密码不正确或文件受保护
@@ -0,0 +1,32 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "lightpdf-aipdf-mcp"
7
+ version = "0.1.31"
8
+ description = "MCP Server for LightPDF AI-PDF"
9
+ readme = "README.md"
10
+ requires-python = ">=3.12"
11
+ license = { text = "Proprietary" }
12
+ authors = [
13
+ {name = "LightPDF Team"}
14
+ ]
15
+ dependencies = [
16
+ "mcp>=1.6.0",
17
+ "httpx>=0.28.1",
18
+ "httpx-sse>=0.4.0",
19
+ "python-dotenv>=1.1.0",
20
+ "uvicorn>=0.34.0",
21
+ "pydantic>=2.11.2",
22
+ "pydantic-settings>=2.8.1",
23
+ ]
24
+
25
+ [project.scripts]
26
+ lightpdf-aipdf-mcp = "lightpdf_aipdf_mcp:main"
27
+
28
+ [tool.hatch.build.targets.wheel]
29
+ packages = ["src/lightpdf_aipdf_mcp"]
30
+
31
+ [tool.hatch.build]
32
+ exclude = ["docs/", "__pycache__/", "publish.sh", "run.py", ".env", ".gitignore"]
@@ -0,0 +1,8 @@
1
+ from . import server
2
+
3
+ def main():
4
+ """Main entry point for the package."""
5
+ server.cli_main()
6
+
7
+ # 只导出main函数
8
+ __all__ = ['main']
@@ -0,0 +1,181 @@
1
+ """通用工具模块"""
2
+ import asyncio
3
+ import json
4
+ import os
5
+ import tempfile
6
+ import time
7
+ import urllib.parse
8
+ from dataclasses import dataclass, field
9
+ from typing import List, Optional, Dict, Any, Tuple
10
+
11
+ import httpx
12
+ from mcp.types import TextContent, LoggingMessageNotification, LoggingMessageNotificationParams, LoggingLevel
13
+
14
+ @dataclass
15
+ class BaseResult:
16
+ """基础结果数据类"""
17
+ success: bool
18
+ file_path: str
19
+ error_message: Optional[str] = None
20
+ download_url: Optional[str] = None
21
+
22
+ class Logger:
23
+ """日志记录器类"""
24
+ def __init__(self, context, collect_info: bool = True):
25
+ self.context = context
26
+ self.collect_info = collect_info
27
+ self._info_log = []
28
+
29
+ async def log(self, level: str, message: str, add_to_result: bool = True):
30
+ """记录日志消息"""
31
+ if self.collect_info and add_to_result:
32
+ self._info_log.append(message)
33
+
34
+ level_map = {
35
+ "debug": LoggingLevel.Debug,
36
+ "info": LoggingLevel.Info,
37
+ "warning": LoggingLevel.Warning,
38
+ "error": LoggingLevel.Error
39
+ }
40
+
41
+ mcp_level = level_map.get(level.lower(), LoggingLevel.Info)
42
+
43
+ # 使用更简洁的send_log_message方法
44
+ await self.context.session.send_log_message(mcp_level, message)
45
+
46
+ async def error(self, message: str, error_class=RuntimeError):
47
+ """记录错误并引发异常"""
48
+ await self.log("error", message)
49
+ raise error_class(message)
50
+
51
+ def get_result_info(self) -> List[str]:
52
+ """获取收集的信息日志"""
53
+ return self._info_log
54
+
55
+ class FileHandler:
56
+ """文件处理工具类"""
57
+ def __init__(self, logger: Logger):
58
+ self.logger = logger
59
+
60
+ @staticmethod
61
+ def is_url(path: str) -> bool:
62
+ """检查路径是否为URL"""
63
+ return path.startswith(("http://", "https://"))
64
+
65
+ @staticmethod
66
+ def get_file_extension(file_path: str) -> str:
67
+ """获取文件扩展名(小写)"""
68
+ if "?" in file_path: # 处理URL中的查询参数
69
+ file_path = file_path.split("?")[0]
70
+ return os.path.splitext(file_path)[1].lower()
71
+
72
+ @staticmethod
73
+ def get_input_format(file_path: str):
74
+ """根据文件路径获取输入格式
75
+
76
+ 此方法需要导入InputFormat和INPUT_EXTENSIONS,
77
+ 但为避免循环导入,由调用者提供转换逻辑
78
+ """
79
+ ext = FileHandler.get_file_extension(file_path)
80
+ return ext
81
+
82
+ @staticmethod
83
+ def get_available_output_formats(input_format):
84
+ """获取指定输入格式支持的输出格式
85
+
86
+ 此方法需要导入FORMAT_CONVERSION_MAP,
87
+ 但为避免循环导入,由调用者提供转换逻辑
88
+ """
89
+ # 实际实现在converter.py
90
+ return {}
91
+
92
+ async def validate_file_exists(self, file_path: str) -> Tuple[bool, bool]:
93
+ """验证文件是否存在
94
+
95
+ Args:
96
+ file_path: 文件路径
97
+
98
+ Returns:
99
+ tuple: (文件是否存在, 是否为URL)
100
+ """
101
+ is_url = self.is_url(file_path)
102
+ if not is_url and not os.path.exists(file_path):
103
+ await self.logger.error(f"文件不存在:{file_path}", FileNotFoundError)
104
+ return False, is_url
105
+ return True, is_url
106
+
107
+ class BaseApiClient:
108
+ """API客户端基类"""
109
+ def __init__(self, logger: Logger, file_handler: FileHandler):
110
+ self.logger = logger
111
+ self.file_handler = file_handler
112
+ self.api_key = os.getenv("API_KEY")
113
+ # 子类必须设置api_base_url
114
+ self.api_base_url = None
115
+
116
+ async def _wait_for_task(self, client: httpx.AsyncClient, task_id: str, operation_type: str = "处理") -> str:
117
+ """等待任务完成并返回下载链接
118
+
119
+ Args:
120
+ client: HTTP客户端
121
+ task_id: 任务ID
122
+ operation_type: 操作类型描述,用于日志,默认为"处理"
123
+
124
+ Returns:
125
+ str: 下载链接
126
+
127
+ Raises:
128
+ RuntimeError: 如果任务失败或超时
129
+ """
130
+ headers = {"X-API-KEY": self.api_key}
131
+ MAX_ATTEMPTS = 100
132
+
133
+ for attempt in range(MAX_ATTEMPTS):
134
+ await asyncio.sleep(3)
135
+
136
+ status_response = await client.get(
137
+ f"{self.api_base_url}/{task_id}",
138
+ headers=headers
139
+ )
140
+
141
+ if status_response.status_code != 200:
142
+ await self.logger.log("warning", f"获取任务状态失败。状态码: {status_response.status_code}")
143
+ continue
144
+
145
+ status_result = status_response.json()
146
+ state = status_result.get("data", {}).get("state")
147
+ progress = status_result.get("data", {}).get("progress", 0)
148
+
149
+ if state == 1: # 完成
150
+ download_url = status_result.get("data", {}).get("file")
151
+ if not download_url:
152
+ await self.logger.error(f"任务完成但未找到下载链接。任务状态:{json.dumps(status_result, ensure_ascii=False)}")
153
+ return download_url
154
+ elif state < 0: # 失败
155
+ await self.logger.error(f"任务失败: {json.dumps(status_result, ensure_ascii=False)}")
156
+ else: # 进行中
157
+ await self.logger.log("debug", f"{operation_type}进度: {progress}%", add_to_result=False)
158
+
159
+ await self.logger.error(f"超过最大尝试次数({MAX_ATTEMPTS}),任务未完成")
160
+
161
+ async def _handle_api_response(self, response: httpx.Response, error_prefix: str) -> str:
162
+ """处理API响应并提取任务ID
163
+
164
+ Args:
165
+ response: API响应
166
+ error_prefix: 错误消息前缀
167
+
168
+ Returns:
169
+ str: 任务ID
170
+
171
+ Raises:
172
+ RuntimeError: 如果响应无效或任务ID缺失
173
+ """
174
+ if response.status_code != 200:
175
+ await self.logger.error(f"{error_prefix}失败。状态码: {response.status_code}\n响应: {response.text}")
176
+
177
+ result = response.json()
178
+ if "data" not in result or "task_id" not in result["data"]:
179
+ await self.logger.error(f"无法获取任务ID。API响应:{json.dumps(result, ensure_ascii=False)}")
180
+
181
+ return result["data"]["task_id"]