media-agent-mcp 2.6.5__tar.gz → 2.6.6__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.
Files changed (56) hide show
  1. {media_agent_mcp-2.6.5 → media_agent_mcp-2.6.6}/PKG-INFO +1 -1
  2. {media_agent_mcp-2.6.5 → media_agent_mcp-2.6.6}/pyproject.toml +1 -1
  3. {media_agent_mcp-2.6.5 → media_agent_mcp-2.6.6}/src/media_agent_mcp/async_server.py +79 -1
  4. {media_agent_mcp-2.6.5 → media_agent_mcp-2.6.6}/src/media_agent_mcp/server.py +72 -35
  5. {media_agent_mcp-2.6.5 → media_agent_mcp-2.6.6}/src/media_agent_mcp.egg-info/PKG-INFO +1 -1
  6. {media_agent_mcp-2.6.5 → media_agent_mcp-2.6.6}/README.md +0 -0
  7. {media_agent_mcp-2.6.5 → media_agent_mcp-2.6.6}/setup.cfg +0 -0
  8. {media_agent_mcp-2.6.5 → media_agent_mcp-2.6.6}/src/media_agent_mcp/__init__.py +0 -0
  9. {media_agent_mcp-2.6.5 → media_agent_mcp-2.6.6}/src/media_agent_mcp/ai_models/__init__.py +0 -0
  10. {media_agent_mcp-2.6.5 → media_agent_mcp-2.6.6}/src/media_agent_mcp/ai_models/omni_human.py +0 -0
  11. {media_agent_mcp-2.6.5 → media_agent_mcp-2.6.6}/src/media_agent_mcp/ai_models/openaiedit.py +0 -0
  12. {media_agent_mcp-2.6.5 → media_agent_mcp-2.6.6}/src/media_agent_mcp/ai_models/seed16.py +0 -0
  13. {media_agent_mcp-2.6.5 → media_agent_mcp-2.6.6}/src/media_agent_mcp/ai_models/seedance.py +0 -0
  14. {media_agent_mcp-2.6.5 → media_agent_mcp-2.6.6}/src/media_agent_mcp/ai_models/seededit.py +0 -0
  15. {media_agent_mcp-2.6.5 → media_agent_mcp-2.6.6}/src/media_agent_mcp/ai_models/seedream.py +0 -0
  16. {media_agent_mcp-2.6.5 → media_agent_mcp-2.6.6}/src/media_agent_mcp/ai_models/tts.py +0 -0
  17. {media_agent_mcp-2.6.5 → media_agent_mcp-2.6.6}/src/media_agent_mcp/async_wrapper.py +0 -0
  18. {media_agent_mcp-2.6.5 → media_agent_mcp-2.6.6}/src/media_agent_mcp/audio/combiner.py +0 -0
  19. {media_agent_mcp-2.6.5 → media_agent_mcp-2.6.6}/src/media_agent_mcp/audio/speed_controller.py +0 -0
  20. {media_agent_mcp-2.6.5 → media_agent_mcp-2.6.6}/src/media_agent_mcp/audio/tts.py +0 -0
  21. {media_agent_mcp-2.6.5 → media_agent_mcp-2.6.6}/src/media_agent_mcp/be/README.md +0 -0
  22. {media_agent_mcp-2.6.5 → media_agent_mcp-2.6.6}/src/media_agent_mcp/be/__init__.py +0 -0
  23. {media_agent_mcp-2.6.5 → media_agent_mcp-2.6.6}/src/media_agent_mcp/be/__pycache__/__init__.cpython-312.pyc +0 -0
  24. {media_agent_mcp-2.6.5 → media_agent_mcp-2.6.6}/src/media_agent_mcp/be/__pycache__/app.cpython-312.pyc +0 -0
  25. {media_agent_mcp-2.6.5 → media_agent_mcp-2.6.6}/src/media_agent_mcp/be/__pycache__/routes_media.cpython-312.pyc +0 -0
  26. {media_agent_mcp-2.6.5 → media_agent_mcp-2.6.6}/src/media_agent_mcp/be/__pycache__/routes_omni.cpython-312.pyc +0 -0
  27. {media_agent_mcp-2.6.5 → media_agent_mcp-2.6.6}/src/media_agent_mcp/be/__pycache__/routes_subtitles.cpython-312.pyc +0 -0
  28. {media_agent_mcp-2.6.5 → media_agent_mcp-2.6.6}/src/media_agent_mcp/be/__pycache__/utils.cpython-312.pyc +0 -0
  29. {media_agent_mcp-2.6.5 → media_agent_mcp-2.6.6}/src/media_agent_mcp/be/app.py +0 -0
  30. {media_agent_mcp-2.6.5 → media_agent_mcp-2.6.6}/src/media_agent_mcp/be/fonts/en/EduNSWACTCursive-VariableFont_wght.ttf +0 -0
  31. {media_agent_mcp-2.6.5 → media_agent_mcp-2.6.6}/src/media_agent_mcp/be/fonts/en/MozillaText-VariableFont_wght.ttf +0 -0
  32. {media_agent_mcp-2.6.5 → media_agent_mcp-2.6.6}/src/media_agent_mcp/be/fonts/en/Roboto_Condensed-Regular.ttf +0 -0
  33. {media_agent_mcp-2.6.5 → media_agent_mcp-2.6.6}/src/media_agent_mcp/be/fonts/zh/MaShanZheng-Regular.ttf +0 -0
  34. {media_agent_mcp-2.6.5 → media_agent_mcp-2.6.6}/src/media_agent_mcp/be/fonts/zh/NotoSerifSC-VariableFont_wght.ttf +0 -0
  35. {media_agent_mcp-2.6.5 → media_agent_mcp-2.6.6}/src/media_agent_mcp/be/fonts/zh/ZCOOLXiaoWei-Regular.ttf +0 -0
  36. {media_agent_mcp-2.6.5 → media_agent_mcp-2.6.6}/src/media_agent_mcp/be/pyproject.toml +0 -0
  37. {media_agent_mcp-2.6.5 → media_agent_mcp-2.6.6}/src/media_agent_mcp/be/routes_media.py +0 -0
  38. {media_agent_mcp-2.6.5 → media_agent_mcp-2.6.6}/src/media_agent_mcp/be/routes_subtitles.py +0 -0
  39. {media_agent_mcp-2.6.5 → media_agent_mcp-2.6.6}/src/media_agent_mcp/be/utils.py +0 -0
  40. {media_agent_mcp-2.6.5 → media_agent_mcp-2.6.6}/src/media_agent_mcp/be/uv.lock +0 -0
  41. {media_agent_mcp-2.6.5 → media_agent_mcp-2.6.6}/src/media_agent_mcp/install_tools/__init__.py +0 -0
  42. {media_agent_mcp-2.6.5 → media_agent_mcp-2.6.6}/src/media_agent_mcp/install_tools/installer.py +0 -0
  43. {media_agent_mcp-2.6.5 → media_agent_mcp-2.6.6}/src/media_agent_mcp/media_selectors/__init__.py +0 -0
  44. {media_agent_mcp-2.6.5 → media_agent_mcp-2.6.6}/src/media_agent_mcp/media_selectors/image_selector.py +0 -0
  45. {media_agent_mcp-2.6.5 → media_agent_mcp-2.6.6}/src/media_agent_mcp/media_selectors/video_selector.py +0 -0
  46. {media_agent_mcp-2.6.5 → media_agent_mcp-2.6.6}/src/media_agent_mcp/storage/__init__.py +0 -0
  47. {media_agent_mcp-2.6.5 → media_agent_mcp-2.6.6}/src/media_agent_mcp/storage/tos_client.py +0 -0
  48. {media_agent_mcp-2.6.5 → media_agent_mcp-2.6.6}/src/media_agent_mcp/video/__init__.py +0 -0
  49. {media_agent_mcp-2.6.5 → media_agent_mcp-2.6.6}/src/media_agent_mcp/video/processor.py +0 -0
  50. {media_agent_mcp-2.6.5 → media_agent_mcp-2.6.6}/src/media_agent_mcp/video/stack.py +0 -0
  51. {media_agent_mcp-2.6.5 → media_agent_mcp-2.6.6}/src/media_agent_mcp/video/subtitle.py +0 -0
  52. {media_agent_mcp-2.6.5 → media_agent_mcp-2.6.6}/src/media_agent_mcp.egg-info/SOURCES.txt +0 -0
  53. {media_agent_mcp-2.6.5 → media_agent_mcp-2.6.6}/src/media_agent_mcp.egg-info/dependency_links.txt +0 -0
  54. {media_agent_mcp-2.6.5 → media_agent_mcp-2.6.6}/src/media_agent_mcp.egg-info/entry_points.txt +0 -0
  55. {media_agent_mcp-2.6.5 → media_agent_mcp-2.6.6}/src/media_agent_mcp.egg-info/requires.txt +0 -0
  56. {media_agent_mcp-2.6.5 → media_agent_mcp-2.6.6}/src/media_agent_mcp.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: media-agent-mcp
3
- Version: 2.6.5
3
+ Version: 2.6.6
4
4
  Summary: A Model Context Protocol server for media processing with AI tools
5
5
  Author-email: Media Agent Team <team@mediaagent.com>
6
6
  Keywords: mcp,ai,media,video,image,processing
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "media-agent-mcp"
3
- version = "2.6.5"
3
+ version = "2.6.6"
4
4
  description = "A Model Context Protocol server for media processing with AI tools"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.12"
@@ -26,6 +26,10 @@ from dotenv import load_dotenv
26
26
  import uvicorn
27
27
  import anyio
28
28
  from functools import wraps
29
+ import os
30
+ import sys
31
+ import signal
32
+ import subprocess
29
33
 
30
34
  def async_retry(max_retries=3, delay=2):
31
35
  def decorator(func):
@@ -132,6 +136,80 @@ class ReconnectableSSEMiddleware:
132
136
  # 不抛出异常,允许客户端重连
133
137
  return
134
138
 
139
+ # 在出现错误时重启应用的中间件
140
+ class RestartOnErrorMiddleware:
141
+ def __init__(self, app):
142
+ self.app = app
143
+ self.restart_cooldown = 5 # 重启冷却时间(秒)
144
+ self.script_path = os.path.abspath(sys.argv[0])
145
+ self.args = sys.argv[1:]
146
+
147
+ async def __call__(self, scope, receive, send):
148
+ try:
149
+ await self.app(scope, receive, send)
150
+ except anyio.ClosedResourceError:
151
+ logger.warning("检测到 ClosedResourceError,准备重启应用...")
152
+ # 等待一段时间,确保日志输出
153
+ await asyncio.sleep(1)
154
+
155
+ # 先终止当前进程的服务器,释放端口
156
+ logger.info("正在关闭当前服务器以释放端口...")
157
+ # 发送SIGTERM信号给当前进程,但不立即退出
158
+ # 这会触发uvicorn的关闭流程,释放端口
159
+ os.kill(os.getpid(), signal.SIGUSR1)
160
+
161
+ # 等待端口释放
162
+ await asyncio.sleep(2)
163
+
164
+ # 启动新进程
165
+ logger.info(f"启动新进程: {self.script_path} {' '.join(self.args)}")
166
+ subprocess.Popen([sys.executable, self.script_path] + self.args)
167
+
168
+ # 等待冷却时间
169
+ logger.info(f"等待 {self.restart_cooldown} 秒后终止当前进程...")
170
+ await asyncio.sleep(self.restart_cooldown)
171
+
172
+ # 终止当前进程
173
+ logger.info("终止当前进程")
174
+ os.kill(os.getpid(), signal.SIGTERM)
175
+ return
176
+
177
+ # 重置超过窗口期的错误计数
178
+ if current_time - self.last_error_time > self.error_window:
179
+ self.error_count = 0
180
+
181
+ self.last_error_time = current_time
182
+ self.error_count += 1
183
+
184
+ logger.warning(f"SSE client disconnected (ClosedResourceError). Error count: {self.error_count}/{self.max_errors}")
185
+
186
+ # 如果错误次数超过阈值,重启应用
187
+ if self.error_count >= self.max_errors:
188
+ logger.error(f"Too many connection errors ({self.error_count}). Restarting application...")
189
+
190
+ # 启动一个新进程来重启应用
191
+ # 使用当前脚本的路径和参数
192
+ args = sys.argv.copy()
193
+
194
+ # 确保我们不会立即重启(可能导致无限循环)
195
+ logger.info(f"Cooling down for {self.restart_cooldown} seconds before restart...")
196
+ await asyncio.sleep(self.restart_cooldown)
197
+
198
+ # 使用subprocess启动新进程
199
+ subprocess.Popen(args, env=os.environ.copy())
200
+
201
+ # 发送SIGTERM信号给当前进程
202
+ logger.info("Sending termination signal to current process...")
203
+ os.kill(os.getpid(), signal.SIGTERM)
204
+
205
+ # 等待进程终止
206
+ await asyncio.sleep(1)
207
+
208
+ # 如果进程还在运行,强制退出
209
+ sys.exit(0)
210
+
211
+ return
212
+
135
213
  # Initialize FastMCP server (will be configured in main function)
136
214
  load_dotenv()
137
215
  mcp = FastMCP("Media-Agent-MCP-Async")
@@ -576,7 +654,7 @@ def main():
576
654
  mcp.settings.port = args.port
577
655
  # Use uvicorn to run SSE app with extended keep-alive timeout (5 minutes)
578
656
  uvicorn.run(
579
- ReconnectableSSEMiddleware(mcp.sse_app()),
657
+ RestartOnErrorMiddleware(mcp.sse_app()),
580
658
  host=args.host,
581
659
  port=args.port,
582
660
  timeout_keep_alive=300
@@ -22,6 +22,11 @@ from dotenv import load_dotenv
22
22
  import uvicorn
23
23
  import anyio
24
24
  import uuid
25
+ import os
26
+ import sys
27
+ import signal
28
+ import subprocess
29
+ import asyncio
25
30
 
26
31
  from mcp.server.fastmcp import FastMCP
27
32
 
@@ -57,46 +62,78 @@ class IgnoreClosedResourceErrorMiddleware:
57
62
  class ReconnectableSSEMiddleware:
58
63
  def __init__(self, app):
59
64
  self.app = app
60
- self.connections = {}
65
+ self.connections = {} # 存储活跃连接
61
66
 
62
67
  async def __call__(self, scope, receive, send):
63
- # Generate a unique connection ID
64
- connection_id = str(uuid.uuid4())
68
+ # 为每个连接生成唯一ID
69
+ connection_id = scope.get('client', ('unknown', 0))[0] + ':' + str(scope.get('client', ('unknown', 0))[1])
65
70
 
66
- # Store connection info
67
- if scope["type"] == "http":
68
- self.connections[connection_id] = {
69
- "status": "active",
70
- "path": scope.get("path", ""),
71
- "created_at": anyio.current_time()
72
- }
73
-
74
- # Wrap the send function to track connection state
75
- original_send = send
71
+ # 包装send函数以跟踪连接状态
72
+ original_send = send
73
+
74
+ async def wrapped_send(message):
75
+ if message.get('type') == 'http.response.start':
76
+ # 记录新连接
77
+ self.connections[connection_id] = {'active': True}
78
+ logger.info(f"New SSE connection established: {connection_id}")
79
+ elif message.get('type') == 'http.response.body' and message.get('more_body', False) is False:
80
+ # 连接结束
81
+ if connection_id in self.connections:
82
+ self.connections[connection_id]['active'] = False
83
+ logger.info(f"SSE connection closed normally: {connection_id}")
76
84
 
77
- async def wrapped_send(message):
78
- if message.get("type") == "http.response.body" and message.get("more_body", False) is False:
79
- # Connection is closing normally
80
- if connection_id in self.connections:
81
- self.connections[connection_id]["status"] = "closed"
82
- return await original_send(message)
85
+ # 调用原始send
86
+ await original_send(message)
87
+
88
+ try:
89
+ # 使用包装后的send函数
90
+ await self.app(scope, receive, wrapped_send)
91
+ except anyio.ClosedResourceError:
92
+ # 客户端断开连接
93
+ if connection_id in self.connections:
94
+ self.connections[connection_id]['active'] = False
83
95
 
84
- try:
85
- # Use the wrapped send function
86
- await self.app(scope, receive, wrapped_send)
87
- except anyio.ClosedResourceError:
88
- # Client disconnected, but we'll allow reconnection
89
- if connection_id in self.connections:
90
- self.connections[connection_id]["status"] = "disconnected"
91
- logger.warning(f"SSE client disconnected (ClosedResourceError). Connection {connection_id[:8]} marked for reconnection.")
92
- return
93
- finally:
94
- # Clean up connection tracking after some time
95
- if connection_id in self.connections:
96
- self.connections.pop(connection_id, None)
97
- else:
98
- # For non-HTTP connections, just pass through
96
+ logger.warning(f"SSE client disconnected (ClosedResourceError): {connection_id}. Client can reconnect.")
97
+ # 不抛出异常,允许客户端重连
98
+ return
99
+
100
+ # 在出现错误时重启应用的中间件
101
+ class RestartOnErrorMiddleware:
102
+ def __init__(self, app):
103
+ self.app = app
104
+ self.restart_cooldown = 5 # 重启冷却时间(秒)
105
+ self.script_path = os.path.abspath(sys.argv[0])
106
+ self.args = sys.argv[1:]
107
+
108
+ async def __call__(self, scope, receive, send):
109
+ try:
99
110
  await self.app(scope, receive, send)
111
+ except anyio.ClosedResourceError:
112
+ logger.warning("检测到 ClosedResourceError,准备重启应用...")
113
+ # 等待一段时间,确保日志输出
114
+ await asyncio.sleep(1)
115
+
116
+ # 启动新进程
117
+ logger.info(f"启动新进程: {self.script_path} {' '.join(self.args)}")
118
+ subprocess.Popen([sys.executable, self.script_path] + self.args)
119
+
120
+ # 等待冷却时间
121
+ logger.info(f"等待 {self.restart_cooldown} 秒后终止当前进程...")
122
+ await asyncio.sleep(self.restart_cooldown)
123
+
124
+ # 终止当前进程
125
+ logger.info("终止当前进程")
126
+ os.kill(os.getpid(), signal.SIGTERM)
127
+ return
128
+
129
+
130
+
131
+
132
+
133
+
134
+
135
+
136
+ return
100
137
 
101
138
  # Initialize FastMCP server (will be configured in main function)
102
139
  load_dotenv()
@@ -636,7 +673,7 @@ def main():
636
673
  # Configure and run the server
637
674
  if args.transport == 'sse':
638
675
  # SSE transport
639
- uvicorn.run(ReconnectableSSEMiddleware(mcp.create_sse_app()), host=args.host, port=args.port)
676
+ uvicorn.run(RestartOnErrorMiddleware(mcp.create_sse_app()), host=args.host, port=args.port)
640
677
  else:
641
678
  # STDIO transport (default)
642
679
  mcp.run()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: media-agent-mcp
3
- Version: 2.6.5
3
+ Version: 2.6.6
4
4
  Summary: A Model Context Protocol server for media processing with AI tools
5
5
  Author-email: Media Agent Team <team@mediaagent.com>
6
6
  Keywords: mcp,ai,media,video,image,processing