lightpdf-aipdf-mcp 0.1.39__py3-none-any.whl → 0.1.40__py3-none-any.whl

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.
@@ -2,14 +2,11 @@
2
2
  import asyncio
3
3
  import json
4
4
  import os
5
- import tempfile
6
5
  import time
7
- import urllib.parse
8
- from dataclasses import dataclass, field
6
+ from dataclasses import dataclass
9
7
  from typing import List, Optional, Dict, Any, Tuple
10
8
 
11
9
  import httpx
12
- from mcp.types import TextContent, LoggingMessageNotification, LoggingMessageNotificationParams, LoggingLevel
13
10
 
14
11
  @dataclass
15
12
  class BaseResult:
@@ -40,7 +37,7 @@ class Logger:
40
37
 
41
38
  mcp_level = level_map.get(level.lower(), "info")
42
39
 
43
- # 使用更简洁的send_log_message方法
40
+ # 直接调用session的send_log_message方法
44
41
  await self.context.session.send_log_message(mcp_level, message)
45
42
 
46
43
  async def error(self, message: str, error_class=RuntimeError):
@@ -61,6 +58,11 @@ class FileHandler:
61
58
  def is_url(path: str) -> bool:
62
59
  """检查路径是否为URL"""
63
60
  return path.startswith(("http://", "https://"))
61
+
62
+ @staticmethod
63
+ def is_oss_path(path: str) -> bool:
64
+ """检查路径是否为OSS路径"""
65
+ return path.startswith("oss://")
64
66
 
65
67
  @staticmethod
66
68
  def get_file_extension(file_path: str) -> str:
@@ -89,20 +91,27 @@ class FileHandler:
89
91
  # 实际实现在converter.py
90
92
  return {}
91
93
 
92
- async def validate_file_exists(self, file_path: str) -> Tuple[bool, bool]:
94
+ async def validate_file_exists(self, file_path: str) -> bool:
93
95
  """验证文件是否存在
94
96
 
95
97
  Args:
96
98
  file_path: 文件路径
97
99
 
98
100
  Returns:
99
- tuple: (文件是否存在, 是否为URL)
101
+ bool: 文件是否存在
100
102
  """
101
103
  is_url = self.is_url(file_path)
102
- if not is_url and not os.path.exists(file_path):
104
+ is_oss = self.is_oss_path(file_path)
105
+
106
+ # 对于URL或OSS路径,假设它们是有效的
107
+ if is_url or is_oss:
108
+ return True
109
+
110
+ if not os.path.exists(file_path):
103
111
  await self.logger.error(f"文件不存在:{file_path}", FileNotFoundError)
104
- return False, is_url
105
- return True, is_url
112
+ return False
113
+
114
+ return True
106
115
 
107
116
  class BaseApiClient:
108
117
  """API客户端基类"""
@@ -171,43 +171,57 @@ class Converter(BaseApiClient):
171
171
  is_special_operation = format in ["doc-repair", "number-pdf"]
172
172
  actual_output_format = "pdf" if is_special_operation else format
173
173
 
174
- # 验证输入文件格式
175
- input_format = self.file_handler.get_input_format(file_path)
176
- if not input_format and not is_special_operation:
177
- await self.logger.error(f"不支持的输入文件格式: {self.file_handler.get_file_extension(file_path)}")
178
-
179
- # 如果是去除水印操作,检查是否PDF文件
180
- if format == "doc-repair" and input_format != InputFormat.PDF:
181
- await self.logger.error("去除水印功能仅支持PDF文件")
174
+ # 检查是否为URL或OSS路径,如果是则跳过文件格式检查
175
+ is_remote_path = self.file_handler.is_url(file_path) or self.file_handler.is_oss_path(file_path)
176
+
177
+ if not is_remote_path:
178
+ # 只对本地文件进行格式验证
182
179
 
183
- # 如果是添加页码操作,检查是否PDF文件
184
- if format == "number-pdf" and input_format != InputFormat.PDF:
185
- await self.logger.error("添加页码功能仅支持PDF文件")
180
+ # 验证输入文件格式
181
+ input_format = self.file_handler.get_input_format(file_path)
182
+ if not input_format and not is_special_operation:
183
+ await self.logger.error(f"不支持的输入文件格式: {self.file_handler.get_file_extension(file_path)}")
186
184
 
187
- # 验证输出格式(除去特殊操作外)
188
- if not is_special_operation:
189
- try:
190
- output_format = OutputFormat(format)
191
- except ValueError:
192
- await self.logger.error(f"不支持的输出格式: {format}")
185
+ # 如果是去除水印操作,检查是否PDF文件
186
+ if format == "doc-repair" and input_format != InputFormat.PDF:
187
+ await self.logger.error("去除水印功能仅支持PDF文件")
188
+
189
+ # 如果是添加页码操作,检查是否PDF文件
190
+ if format == "number-pdf" and input_format != InputFormat.PDF:
191
+ await self.logger.error("添加页码功能仅支持PDF文件")
192
+
193
+ # 验证输出格式(除去特殊操作外)
194
+ if not is_special_operation:
195
+ try:
196
+ output_format = OutputFormat(format)
197
+ except ValueError:
198
+ await self.logger.error(f"不支持的输出格式: {format}")
193
199
 
194
- # 验证格式转换是否支持
195
- if input_format: # 确保input_format有效
196
- available_formats = self.file_handler.get_available_output_formats(input_format)
197
- if output_format not in available_formats:
198
- await self.logger.error(
199
- f"不支持从 {input_format.value} 格式转换为 {output_format.value} 格式。"
200
- f"支持的输出格式: {', '.join(f.value for f in available_formats)}"
201
- )
200
+ # 验证格式转换是否支持
201
+ if input_format: # 确保input_format有效
202
+ available_formats = self.file_handler.get_available_output_formats(input_format)
203
+ if output_format not in available_formats:
204
+ await self.logger.error(
205
+ f"不支持从 {input_format.value} 格式转换为 {output_format.value} 格式。"
206
+ f"支持的输出格式: {', '.join(f.value for f in available_formats)}"
207
+ )
202
208
  else:
203
- # 对于特殊操作,设置输出格式为PDF
204
- output_format = OutputFormat.PDF
209
+ # 远程路径的情况,设置必要的变量以便后续使用
210
+ if not is_special_operation:
211
+ try:
212
+ output_format = OutputFormat(format)
213
+ except ValueError:
214
+ await self.logger.error(f"不支持的输出格式: {format}")
215
+ else:
216
+ output_format = OutputFormat.PDF
217
+
218
+ # 对于远程路径,无法确定输入格式,但为了让后续代码能正常运行,设置一个默认值
219
+ input_format = None
205
220
 
206
221
  # 验证文件
207
- file_exists_result = await self.file_handler.validate_file_exists(file_path)
208
- if not file_exists_result[0]:
222
+ exists = await self.file_handler.validate_file_exists(file_path)
223
+ if not exists:
209
224
  return ConversionResult(success=False, file_path=file_path, error_message="文件不存在")
210
- is_url = file_exists_result[1]
211
225
 
212
226
  # 操作描述
213
227
  if format == "doc-repair":
@@ -215,7 +229,13 @@ class Converter(BaseApiClient):
215
229
  elif format == "number-pdf":
216
230
  operation_desc = "添加页码"
217
231
  else:
218
- operation_desc = f"将 {input_format.value.upper()} 转换为 {output_format.value.upper()} 格式"
232
+ if is_remote_path:
233
+ operation_desc = f"转换为 {output_format.value.upper()} 格式"
234
+ else:
235
+ if input_format:
236
+ operation_desc = f"将 {input_format.value.upper()} 转换为 {output_format.value.upper()} 格式"
237
+ else:
238
+ operation_desc = f"转换为 {output_format.value.upper()} 格式"
219
239
  await self.logger.log("info", f"正在{operation_desc}...")
220
240
 
221
241
  import httpx
@@ -230,7 +250,7 @@ class Converter(BaseApiClient):
230
250
  extra_params["password"] = password
231
251
 
232
252
  # 创建转换任务
233
- task_id = await self._create_task(client, file_path, format, is_url, extra_params)
253
+ task_id = await self._create_task(client, file_path, format, extra_params)
234
254
 
235
255
  # 等待任务完成
236
256
  download_url = await self._wait_for_task(client, task_id, "转换")
@@ -253,14 +273,13 @@ class Converter(BaseApiClient):
253
273
  download_url=None
254
274
  )
255
275
 
256
- async def _create_task(self, client: httpx.AsyncClient, file_path: str, format: str, is_url: bool, extra_params: dict = None) -> str:
276
+ async def _create_task(self, client: httpx.AsyncClient, file_path: str, format: str, extra_params: dict = None) -> str:
257
277
  """创建转换任务
258
278
 
259
279
  Args:
260
280
  client: HTTP客户端
261
281
  file_path: 文件路径
262
282
  format: 目标格式,特殊格式"doc-repair"用于去除水印,"number-pdf"用于添加页码
263
- is_url: 是否URL路径
264
283
  extra_params: 额外API参数(可选)
265
284
 
266
285
  Returns:
@@ -275,7 +294,20 @@ class Converter(BaseApiClient):
275
294
  if extra_params:
276
295
  data.update(extra_params)
277
296
 
278
- if is_url:
297
+ # 检查是否为OSS路径
298
+ if self.file_handler.is_oss_path(file_path):
299
+ # OSS路径处理方式,与URL类似,但提取resource_id
300
+ data["resource_id"] = file_path.split("oss://")[1]
301
+ # 使用JSON方式时添加Content-Type
302
+ headers["Content-Type"] = "application/json"
303
+ response = await client.post(
304
+ self.api_base_url,
305
+ json=data,
306
+ headers=headers
307
+ )
308
+ # 检查是否为URL路径
309
+ elif self.file_handler.is_url(file_path):
310
+ data["url"] = file_path
279
311
  # 使用JSON方式时添加Content-Type
280
312
  headers["Content-Type"] = "application/json"
281
313
  response = await client.post(
@@ -40,6 +40,10 @@ class Editor(BaseApiClient):
40
40
  Returns:
41
41
  bool: 如果是PDF文件则返回True,否则返回False
42
42
  """
43
+ # 对于URL或OSS路径,跳过文件格式检查
44
+ if self.file_handler.is_url(file_path) or self.file_handler.is_oss_path(file_path):
45
+ return True
46
+
43
47
  input_format = self.file_handler.get_input_format(file_path)
44
48
  if input_format != InputFormat.PDF:
45
49
  await self.logger.error(f"此功能仅支持PDF文件,当前文件格式为: {self.file_handler.get_file_extension(file_path)}")
@@ -120,7 +124,8 @@ class Editor(BaseApiClient):
120
124
  if not await self._validate_pdf_file(pdf_file):
121
125
  return EditResult(success=False, file_path=pdf_file, error_message="非PDF文件")
122
126
 
123
- if not (await self.file_handler.validate_file_exists(pdf_file))[0]:
127
+ exists = await self.file_handler.validate_file_exists(pdf_file)
128
+ if not exists:
124
129
  return EditResult(success=False, file_path=pdf_file, error_message="文件不存在")
125
130
 
126
131
  # 记录操作描述
@@ -368,10 +373,9 @@ class Editor(BaseApiClient):
368
373
  return EditResult(success=False, file_path=file_path, error_message="未找到API_KEY。请在客户端配置API_KEY环境变量。")
369
374
 
370
375
  # 验证文件
371
- file_exists_result = await self.file_handler.validate_file_exists(file_path)
372
- if not file_exists_result[0]:
376
+ exists = await self.file_handler.validate_file_exists(file_path)
377
+ if not exists:
373
378
  return EditResult(success=False, file_path=file_path, error_message="文件不存在")
374
- is_url = file_exists_result[1]
375
379
 
376
380
  async with httpx.AsyncClient(timeout=120.0) as client:
377
381
  try:
@@ -384,7 +388,7 @@ class Editor(BaseApiClient):
384
388
  extra_params["password"] = password
385
389
 
386
390
  # 创建编辑任务
387
- task_id = await self._create_task(client, file_path, edit_type, is_url, extra_params)
391
+ task_id = await self._create_task(client, file_path, edit_type, extra_params)
388
392
 
389
393
  # 等待任务完成
390
394
  download_url = await self._wait_for_task(client, task_id, "编辑")
@@ -407,14 +411,13 @@ class Editor(BaseApiClient):
407
411
  download_url=None
408
412
  )
409
413
 
410
- async def _create_task(self, client: httpx.AsyncClient, file_path: str, edit_type: EditType, is_url: bool, extra_params: Dict[str, Any] = None) -> str:
414
+ async def _create_task(self, client: httpx.AsyncClient, file_path: str, edit_type: EditType, extra_params: Dict[str, Any] = None) -> str:
411
415
  """创建编辑任务
412
416
 
413
417
  Args:
414
418
  client: HTTP客户端
415
419
  file_path: 文件路径
416
420
  edit_type: 编辑操作类型
417
- is_url: 是否URL路径
418
421
  extra_params: 额外API参数(可选)
419
422
 
420
423
  Returns:
@@ -429,7 +432,19 @@ class Editor(BaseApiClient):
429
432
  if extra_params:
430
433
  data.update(extra_params)
431
434
 
432
- if is_url:
435
+ # 检查是否为OSS路径
436
+ if self.file_handler.is_oss_path(file_path):
437
+ # OSS路径处理方式,与URL类似,但提取resource_id
438
+ data["resource_id"] = file_path.split("oss://")[1]
439
+ # 使用JSON方式时添加Content-Type
440
+ headers["Content-Type"] = "application/json"
441
+ response = await client.post(
442
+ self.api_base_url,
443
+ json=data,
444
+ headers=headers
445
+ )
446
+ # 检查是否为URL路径
447
+ elif self.file_handler.is_url(file_path):
433
448
  data["url"] = file_path
434
449
  # 使用JSON方式时添加Content-Type
435
450
  headers["Content-Type"] = "application/json"
@@ -476,12 +491,19 @@ class Editor(BaseApiClient):
476
491
  files = {}
477
492
 
478
493
  for i, file_path in enumerate(file_paths):
494
+ # 检查是否为URL或OSS路径
479
495
  if self.file_handler.is_url(file_path):
480
- # 对于URL,添加到inputs数组
496
+ # 对于URL或OSS路径,添加到inputs数组
481
497
  input_item = {"url": file_path}
482
498
  if password:
483
499
  input_item["password"] = password
484
500
  url_inputs.append(input_item)
501
+ elif self.file_handler.is_oss_path(file_path):
502
+ # 对于OSS路径,添加到inputs数组
503
+ input_item = {"resource_id": file_path.split("oss://")[1]}
504
+ if password:
505
+ input_item["password"] = password
506
+ url_inputs.append(input_item)
485
507
  else:
486
508
  # 记录本地文件,需要使用form方式
487
509
  local_files.append(file_path)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lightpdf-aipdf-mcp
3
- Version: 0.1.39
3
+ Version: 0.1.40
4
4
  Summary: MCP Server for LightPDF AI-PDF
5
5
  Author: LightPDF Team
6
6
  License: Proprietary
@@ -0,0 +1,9 @@
1
+ lightpdf_aipdf_mcp/__init__.py,sha256=PPnAgpvJLYLVOTxnHDmJAulFnHJD6wuTwS6tRGjqq6s,141
2
+ lightpdf_aipdf_mcp/common.py,sha256=zhd_XxO7cBiQ7A6fc1JJjcpJfRWCQBi3pfBSsrBRusg,6568
3
+ lightpdf_aipdf_mcp/converter.py,sha256=SbP5CpDn1suoq9QSApw6kgqJXEc4gwF7Po3oLo5namg,13313
4
+ lightpdf_aipdf_mcp/editor.py,sha256=6ELqub8uOW6kwkVz52YdFUyDmkMsN1BkTApSw6Bi-lg,22802
5
+ lightpdf_aipdf_mcp/server.py,sha256=jXAcfLWhef5E3cle28bytDTTuMiwTMYAglbAFVF6Wlw,35737
6
+ lightpdf_aipdf_mcp-0.1.40.dist-info/METADATA,sha256=V8FmqYq5cSQU1BILhURaTMHRtEsHqawRUfhAI_wYwd8,7931
7
+ lightpdf_aipdf_mcp-0.1.40.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
8
+ lightpdf_aipdf_mcp-0.1.40.dist-info/entry_points.txt,sha256=X7TGUe52N4sYH-tYt0YUGApeJgw-efQlZA6uAZmlmr4,63
9
+ lightpdf_aipdf_mcp-0.1.40.dist-info/RECORD,,
@@ -1,9 +0,0 @@
1
- lightpdf_aipdf_mcp/__init__.py,sha256=PPnAgpvJLYLVOTxnHDmJAulFnHJD6wuTwS6tRGjqq6s,141
2
- lightpdf_aipdf_mcp/common.py,sha256=ZPm_Wzn7GpMLl9xJXOrTgNgd0hqdI931JVKm57WRa14,6433
3
- lightpdf_aipdf_mcp/converter.py,sha256=5oTLHxXOiZ9_PDatWiYbgPZ0LFFqE0L4qHvamX3g9lM,11732
4
- lightpdf_aipdf_mcp/editor.py,sha256=mbCGXd1Xzu-ibDWJnn11fOP5Xlf1Vqxi0yQ8Vs_yUKg,21787
5
- lightpdf_aipdf_mcp/server.py,sha256=jXAcfLWhef5E3cle28bytDTTuMiwTMYAglbAFVF6Wlw,35737
6
- lightpdf_aipdf_mcp-0.1.39.dist-info/METADATA,sha256=USG1KkrGGjJ75mf2hB0fuoFTas5noxVK0VcxlTN_Ooc,7931
7
- lightpdf_aipdf_mcp-0.1.39.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
8
- lightpdf_aipdf_mcp-0.1.39.dist-info/entry_points.txt,sha256=X7TGUe52N4sYH-tYt0YUGApeJgw-efQlZA6uAZmlmr4,63
9
- lightpdf_aipdf_mcp-0.1.39.dist-info/RECORD,,