lightpdf-aipdf-mcp 0.1.145__py3-none-any.whl → 0.1.147__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.
- lightpdf_aipdf_mcp/create_pdf.py +100 -35
- lightpdf_aipdf_mcp/server.py +14 -1
- {lightpdf_aipdf_mcp-0.1.145.dist-info → lightpdf_aipdf_mcp-0.1.147.dist-info}/METADATA +1 -1
- {lightpdf_aipdf_mcp-0.1.145.dist-info → lightpdf_aipdf_mcp-0.1.147.dist-info}/RECORD +6 -6
- {lightpdf_aipdf_mcp-0.1.145.dist-info → lightpdf_aipdf_mcp-0.1.147.dist-info}/WHEEL +0 -0
- {lightpdf_aipdf_mcp-0.1.145.dist-info → lightpdf_aipdf_mcp-0.1.147.dist-info}/entry_points.txt +0 -0
lightpdf_aipdf_mcp/create_pdf.py
CHANGED
@@ -167,7 +167,6 @@ class PDFCreator(BaseApiClient):
|
|
167
167
|
async def _get_real_user_info(self, language: str) -> dict:
|
168
168
|
"""获取真实的用户信息"""
|
169
169
|
import platform
|
170
|
-
import socket
|
171
170
|
|
172
171
|
# 获取真实的用户代理字符串
|
173
172
|
system = platform.system()
|
@@ -225,9 +224,12 @@ class PDFCreator(BaseApiClient):
|
|
225
224
|
return language_codes.get(language, "en-US,en;q=0.9")
|
226
225
|
|
227
226
|
def _get_local_ip(self) -> str:
|
228
|
-
"""获取本地IP
|
227
|
+
"""获取本地IP地址的稳定方法,优先获取物理网络接口IP"""
|
229
228
|
import socket
|
230
229
|
|
230
|
+
# 收集所有可能的IP地址
|
231
|
+
candidate_ips = []
|
232
|
+
|
231
233
|
# 方法1: 尝试连接外部DNS服务器
|
232
234
|
try:
|
233
235
|
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
|
@@ -236,9 +238,9 @@ class PDFCreator(BaseApiClient):
|
|
236
238
|
try:
|
237
239
|
s.connect((dns_server, 80))
|
238
240
|
ip = s.getsockname()[0]
|
239
|
-
# 验证是否为私有IP地址
|
240
241
|
if self._is_valid_local_ip(ip):
|
241
|
-
|
242
|
+
candidate_ips.append(ip)
|
243
|
+
break # 找到第一个有效IP就停止
|
242
244
|
except Exception:
|
243
245
|
continue
|
244
246
|
except Exception:
|
@@ -249,58 +251,121 @@ class PDFCreator(BaseApiClient):
|
|
249
251
|
hostname = socket.gethostname()
|
250
252
|
ip_list = socket.gethostbyname_ex(hostname)[2]
|
251
253
|
for ip in ip_list:
|
252
|
-
if self._is_valid_local_ip(ip):
|
253
|
-
|
254
|
+
if self._is_valid_local_ip(ip) and ip not in candidate_ips:
|
255
|
+
candidate_ips.append(ip)
|
254
256
|
except Exception:
|
255
257
|
pass
|
256
258
|
|
257
|
-
# 方法3:
|
259
|
+
# 方法3: 使用系统网络接口信息
|
258
260
|
try:
|
259
|
-
import subprocess
|
260
261
|
import platform
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
result = subprocess.run(['
|
265
|
-
|
266
|
-
if 'IPv4' in line and 'Address' in line:
|
267
|
-
ip = line.split(':')[-1].strip()
|
268
|
-
if self._is_valid_local_ip(ip):
|
269
|
-
return ip
|
270
|
-
else:
|
271
|
-
# Unix/Linux/macOS系统
|
272
|
-
result = subprocess.run(['hostname', '-I'], capture_output=True, text=True)
|
262
|
+
if platform.system() != "Windows":
|
263
|
+
# Unix/Linux/macOS系统:尝试使用更详细的网络接口信息
|
264
|
+
import subprocess
|
265
|
+
result = subprocess.run(['ip', 'route', 'get', '8.8.8.8'],
|
266
|
+
capture_output=True, text=True)
|
273
267
|
if result.returncode == 0:
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
268
|
+
for line in result.stdout.split('\n'):
|
269
|
+
if 'src' in line:
|
270
|
+
parts = line.split()
|
271
|
+
if 'src' in parts:
|
272
|
+
src_index = parts.index('src')
|
273
|
+
if src_index + 1 < len(parts):
|
274
|
+
ip = parts[src_index + 1]
|
275
|
+
if self._is_valid_local_ip(ip) and ip not in candidate_ips:
|
276
|
+
candidate_ips.append(ip)
|
277
|
+
break
|
278
278
|
except Exception:
|
279
279
|
pass
|
280
280
|
|
281
|
-
#
|
282
|
-
|
283
|
-
|
284
|
-
s.bind(('', 0))
|
285
|
-
ip = s.getsockname()[0]
|
286
|
-
if self._is_valid_local_ip(ip):
|
287
|
-
return ip
|
288
|
-
except Exception:
|
289
|
-
pass
|
281
|
+
# 选择最优IP地址
|
282
|
+
if candidate_ips:
|
283
|
+
return self._select_best_ip(candidate_ips)
|
290
284
|
|
291
285
|
# 最后的后备方案
|
292
286
|
return "127.0.0.1"
|
293
287
|
|
288
|
+
def _select_best_ip(self, candidate_ips: list) -> str:
|
289
|
+
"""从候选IP中选择最佳的IP地址"""
|
290
|
+
import ipaddress
|
291
|
+
|
292
|
+
# 按优先级排序IP地址
|
293
|
+
def ip_priority(ip: str) -> int:
|
294
|
+
try:
|
295
|
+
ip_obj = ipaddress.ip_address(ip)
|
296
|
+
|
297
|
+
# 最高优先级:公网IP
|
298
|
+
if ip_obj.is_global:
|
299
|
+
return 1
|
300
|
+
|
301
|
+
# 高优先级:常见的家庭/办公网络
|
302
|
+
common_networks = [
|
303
|
+
ipaddress.ip_network('192.168.0.0/16'), # 家庭网络
|
304
|
+
ipaddress.ip_network('10.0.0.0/8'), # 企业网络
|
305
|
+
ipaddress.ip_network('172.16.0.0/12'), # 企业网络(但要避免虚拟网络段)
|
306
|
+
]
|
307
|
+
|
308
|
+
for network in common_networks:
|
309
|
+
if ip_obj in network:
|
310
|
+
# 进一步细分优先级
|
311
|
+
if ip.startswith('192.168.1.') or ip.startswith('192.168.0.'):
|
312
|
+
return 2 # 最常见的家庭网络
|
313
|
+
elif ip.startswith('192.168.'):
|
314
|
+
return 3 # 其他家庭网络
|
315
|
+
elif ip.startswith('10.'):
|
316
|
+
return 4 # 企业网络
|
317
|
+
else:
|
318
|
+
return 5 # 其他私有网络
|
319
|
+
|
320
|
+
# 较低优先级:其他私有IP
|
321
|
+
if ip_obj.is_private:
|
322
|
+
return 6
|
323
|
+
|
324
|
+
# 最低优先级:其他地址
|
325
|
+
return 7
|
326
|
+
|
327
|
+
except ValueError:
|
328
|
+
return 99 # 无效IP地址
|
329
|
+
|
330
|
+
# 按优先级排序并返回最佳IP
|
331
|
+
candidate_ips.sort(key=ip_priority)
|
332
|
+
return candidate_ips[0]
|
333
|
+
|
294
334
|
def _is_valid_local_ip(self, ip: str) -> bool:
|
295
|
-
"""验证IP地址是否为有效的本地IP"""
|
335
|
+
"""验证IP地址是否为有效的本地IP,排除虚拟网络接口"""
|
296
336
|
import ipaddress
|
297
337
|
|
298
338
|
try:
|
299
339
|
ip_obj = ipaddress.ip_address(ip)
|
340
|
+
|
300
341
|
# 排除回环地址和链路本地地址
|
301
342
|
if ip_obj.is_loopback or ip_obj.is_link_local:
|
302
343
|
return False
|
303
|
-
|
344
|
+
|
345
|
+
# 排除常见的虚拟网络IP段
|
346
|
+
virtual_networks = [
|
347
|
+
# Docker默认网段
|
348
|
+
ipaddress.ip_network('172.17.0.0/16'),
|
349
|
+
ipaddress.ip_network('172.18.0.0/16'),
|
350
|
+
ipaddress.ip_network('172.19.0.0/16'),
|
351
|
+
ipaddress.ip_network('172.20.0.0/16'),
|
352
|
+
# VMware默认网段
|
353
|
+
ipaddress.ip_network('192.168.56.0/24'),
|
354
|
+
ipaddress.ip_network('192.168.57.0/24'),
|
355
|
+
# VirtualBox默认网段
|
356
|
+
ipaddress.ip_network('192.168.100.0/24'),
|
357
|
+
# Hyper-V默认网段
|
358
|
+
ipaddress.ip_network('172.16.0.0/12'),
|
359
|
+
# 其他常见虚拟网段
|
360
|
+
ipaddress.ip_network('10.0.75.0/24'), # Parallels
|
361
|
+
ipaddress.ip_network('169.254.0.0/16'), # APIPA
|
362
|
+
]
|
363
|
+
|
364
|
+
# 检查是否在虚拟网络范围内
|
365
|
+
for network in virtual_networks:
|
366
|
+
if ip_obj in network:
|
367
|
+
return False
|
368
|
+
|
304
369
|
return True
|
305
370
|
except ValueError:
|
306
371
|
return False
|
lightpdf_aipdf_mcp/server.py
CHANGED
@@ -149,6 +149,11 @@ async def process_conversion_file(
|
|
149
149
|
# 处理extra_params
|
150
150
|
if extra_params is None:
|
151
151
|
extra_params = {}
|
152
|
+
|
153
|
+
# 参数名称映射:将image_quality映射为image-quality
|
154
|
+
if "image_quality" in extra_params:
|
155
|
+
extra_params["image-quality"] = extra_params.pop("image_quality")
|
156
|
+
|
152
157
|
# 直接传递 merge_all 参数(如有)
|
153
158
|
# 其它逻辑交由 converter.convert_file 处理
|
154
159
|
return await converter.convert_file(file_path, format, extra_params, password, original_name)
|
@@ -526,6 +531,13 @@ async def handle_list_tools() -> list[types.Tool]:
|
|
526
531
|
"type": "boolean",
|
527
532
|
"default": False,
|
528
533
|
"description": "Only effective when converting Excel to PDF. If true, each sheet will be forced to fit into a single PDF page (even if content overflows; no additional pages will be created). If false, each sheet may be split into multiple PDF pages if the content is too large."
|
534
|
+
},
|
535
|
+
"image_quality": {
|
536
|
+
"type": "integer",
|
537
|
+
"minimum": 0,
|
538
|
+
"maximum": 200,
|
539
|
+
"default": 100,
|
540
|
+
"description": "Image quality setting, 0-200. Only effective when converting PDF to image formats (jpg, jpeg, png). Higher values produce better quality but larger file sizes."
|
529
541
|
}
|
530
542
|
},
|
531
543
|
"required": ["files", "format"]
|
@@ -1377,7 +1389,7 @@ async def handle_call_tool(name: str, arguments: dict | None) -> list[types.Text
|
|
1377
1389
|
"convert_document": {
|
1378
1390
|
"format_key": "format", # 从arguments获取format
|
1379
1391
|
"is_edit_operation": False,
|
1380
|
-
"param_keys": ["merge_all", "one_page_per_sheet"]
|
1392
|
+
"param_keys": ["merge_all", "one_page_per_sheet", "image_quality"]
|
1381
1393
|
},
|
1382
1394
|
"remove_watermark": {
|
1383
1395
|
"format": "doc-repair", # 固定format
|
@@ -1485,6 +1497,7 @@ async def handle_call_tool(name: str, arguments: dict | None) -> list[types.Text
|
|
1485
1497
|
"format": "png", # 提取图片的默认格式
|
1486
1498
|
"page_size": "",
|
1487
1499
|
"resolution": 0,
|
1500
|
+
"image_quality": 100, # PDF转图片的图片质量默认值
|
1488
1501
|
}
|
1489
1502
|
|
1490
1503
|
if name in TOOL_CONFIG:
|
@@ -1,13 +1,13 @@
|
|
1
1
|
lightpdf_aipdf_mcp/__init__.py,sha256=PPnAgpvJLYLVOTxnHDmJAulFnHJD6wuTwS6tRGjqq6s,141
|
2
2
|
lightpdf_aipdf_mcp/common.py,sha256=VOipRuz2veRMhpvr0lJ2nZwuEZntx1MiRxDSNx0fSWs,9310
|
3
3
|
lightpdf_aipdf_mcp/converter.py,sha256=r8iO5R5vLNNKWdb6WSnwzTwwmp2TvgLXSIvvA4y___o,15336
|
4
|
-
lightpdf_aipdf_mcp/create_pdf.py,sha256=
|
4
|
+
lightpdf_aipdf_mcp/create_pdf.py,sha256=FKWttbR48foiwUwmSsCN6n6PfI25IpaXETPUscz9DjI,16073
|
5
5
|
lightpdf_aipdf_mcp/editor.py,sha256=BR-sWW9L7tybEPOhdc8W-uwdBoom19EPTmGDvy_2gMc,27941
|
6
6
|
lightpdf_aipdf_mcp/ocr.py,sha256=IyzxisA6qtXcGTHZofpUYXYDdcIjUaaHcVUKpM7DH9A,2832
|
7
|
-
lightpdf_aipdf_mcp/server.py,sha256=
|
7
|
+
lightpdf_aipdf_mcp/server.py,sha256=Y4jF1nl-YCCZkbLaiuEZ2EuSnK0Am8OaxN9hGzBDWNQ,81468
|
8
8
|
lightpdf_aipdf_mcp/summarizer.py,sha256=UPAftDKjp2NFE2Wfoi2yAsGfaWqihu-c_W_BwfhVy0c,3671
|
9
9
|
lightpdf_aipdf_mcp/translator.py,sha256=nuZa4FpsA0xeRWAEGqSPIM55aJuazJX1m32uajowo7I,2778
|
10
|
-
lightpdf_aipdf_mcp-0.1.
|
11
|
-
lightpdf_aipdf_mcp-0.1.
|
12
|
-
lightpdf_aipdf_mcp-0.1.
|
13
|
-
lightpdf_aipdf_mcp-0.1.
|
10
|
+
lightpdf_aipdf_mcp-0.1.147.dist-info/METADATA,sha256=SSOOrZXi6fTDgfnSJFrUJuScJ-AwodAEEy0OIjax-Pk,8120
|
11
|
+
lightpdf_aipdf_mcp-0.1.147.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
12
|
+
lightpdf_aipdf_mcp-0.1.147.dist-info/entry_points.txt,sha256=X7TGUe52N4sYH-tYt0YUGApeJgw-efQlZA6uAZmlmr4,63
|
13
|
+
lightpdf_aipdf_mcp-0.1.147.dist-info/RECORD,,
|
File without changes
|
{lightpdf_aipdf_mcp-0.1.145.dist-info → lightpdf_aipdf_mcp-0.1.147.dist-info}/entry_points.txt
RENAMED
File without changes
|