media-agent-mcp 2.7.0__py3-none-any.whl → 2.7.2__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.
@@ -122,8 +122,8 @@ if __name__ == '__main__':
122
122
  time1 = time.time()
123
123
 
124
124
  print(generate_video_from_omni_human(
125
- image_url="https://ark-content-generation-v2-ap-southeast-1.tos-ap-southeast-1.volces.com/seedream-3-0-t2i/0217564979274996d76cd7acfd572da46abbcedd46e0f23a31db6.jpeg?X-Tos-Algorithm=TOS4-HMAC-SHA256&X-Tos-Credential=AKLTYWJkZTExNjA1ZDUyNDc3YzhjNTM5OGIyNjBhNDcyOTQ%2F20250829%2Fap-southeast-1%2Ftos%2Frequest&X-Tos-Date=20250829T200528Z&X-Tos-Expires=86400&X-Tos-Signature=abf8c7d516eecd4543691ffee5c3c5bcc7e38d5d047caf7f2fcfe3e41ab897d3&X-Tos-SignedHeaders=host",
126
- audio_url="https://carey.tos-ap-southeast-1.bytepluses.com/media_agent/2025-08-30/a72d04349e81454c8065674792256a1d.mp3"
125
+ image_url="https://carey.tos-ap-southeast-1.bytepluses.com/Art%20Portrait/Art%20Portrait/Art%20Portrait/Art%20Portrait%20(1).jpg",
126
+ audio_url="https://carey.tos-ap-southeast-1.bytepluses.com/media_agent/2025-09-02/66620bed2a5f4b2cbc641559ff93a2ed.mp3"
127
127
  ))
128
128
 
129
129
  print(time.time() - time1)
@@ -449,8 +449,6 @@ def main():
449
449
  help='Host for SSE transport (default: 127.0.0.1)')
450
450
  parser.add_argument('--port', type=int, default=8000,
451
451
  help='Port for SSE transport (default: 8000)')
452
- parser.add_argument('--version', action='store_true',
453
- help='Show version information')
454
452
  parser.add_argument('--run-be', action='store_true',
455
453
  help='Run the backend server')
456
454
  parser.add_argument('--be-host', type=str, default='0.0.0.0',
@@ -460,10 +458,6 @@ def main():
460
458
 
461
459
  args = parser.parse_args()
462
460
 
463
- if args.version:
464
- print("Async Media Agent MCP Server v0.1.0")
465
- return
466
-
467
461
  if args.run_be:
468
462
  logger.info(f"Starting backend server on {args.be_host}:{args.be_port}...")
469
463
  be_path = os.path.abspath(os.path.join(os.path.dirname(__file__), 'be'))
@@ -473,22 +467,20 @@ def main():
473
467
  try:
474
468
  subprocess.run(
475
469
  [
476
- sys.executable,
477
- "-c",
478
- (
479
- "import sys, app; "
480
- "flask_app = getattr(app, '_flask_app', getattr(app, 'app', None)); "
481
- "assert flask_app is not None, 'No Flask application instance found in app.py'; "
482
- "flask_app.run(host=sys.argv[1], port=int(sys.argv[2]), debug=False)"
483
- ),
484
- args.be_host,
485
- str(args.be_port),
470
+ "gunicorn",
471
+ "--workers",
472
+ "4",
473
+ "--bind",
474
+ f"{args.be_host}:{args.be_port}",
475
+ "--log-level",
476
+ "debug",
477
+ "app:app",
486
478
  ],
487
479
  cwd=be_path,
488
480
  check=True
489
481
  )
490
482
  except FileNotFoundError:
491
- logger.error("`uvicorn` command not found. Please ensure it is installed in your environment.")
483
+ logger.error("`gunicorn` command not found. Please ensure it is installed in your environment.")
492
484
  except subprocess.CalledProcessError as e:
493
485
  logger.error(f"Failed to start backend server: {e}")
494
486
  return
@@ -528,7 +520,7 @@ def main():
528
520
  mcp.sse_app(),
529
521
  host=args.host,
530
522
  port=args.port,
531
- timeout_keep_alive=1200
523
+ timeout_keep_alive=300
532
524
  )
533
525
  else:
534
526
  # Default stdio transport
media_agent_mcp/be/app.py CHANGED
@@ -6,6 +6,7 @@ from flask import Flask
6
6
  # Register blueprints from modularized routes
7
7
  from media_agent_mcp.be.routes_media import media_bp
8
8
  from media_agent_mcp.be.routes_subtitles import subtitles_bp
9
+ from media_agent_mcp.be.routes_omni_human import omni_human_bp
9
10
 
10
11
 
11
12
  logger = logging.getLogger(__name__)
@@ -14,6 +15,7 @@ app = Flask(__name__)
14
15
  # Register blueprints (keep original paths unchanged)
15
16
  app.register_blueprint(media_bp)
16
17
  app.register_blueprint(subtitles_bp)
18
+ app.register_blueprint(omni_human_bp)
17
19
 
18
20
 
19
21
  if __name__ == "__main__":
@@ -5,6 +5,7 @@ import subprocess
5
5
  import tempfile
6
6
  from pathlib import Path
7
7
  from typing import List, Optional, Dict, Any
8
+ from concurrent.futures import ThreadPoolExecutor
8
9
 
9
10
  from flask import Blueprint, jsonify, request, send_file, after_this_request
10
11
 
@@ -160,13 +161,15 @@ def concat_videos():
160
161
  pass
161
162
  return response
162
163
 
163
- logger.info('Downloading videos')
164
+ logger.info('Downloading videos concurrently')
164
165
  video_paths = []
165
- for url in video_urls:
166
- dl = download_video_from_url(url)
166
+ with ThreadPoolExecutor(max_workers=min(len(video_urls), 5)) as executor:
167
+ download_results = list(executor.map(download_video_from_url, video_urls))
168
+
169
+ for dl in download_results:
167
170
  if dl.get("status") == "error":
168
171
  return jsonify(dl), 400
169
- path = Path(dl["data"]["file_path"]) # type: ignore[index]
172
+ path = Path(dl["data"]["file_path"])
170
173
  temp_files.append(path)
171
174
  video_paths.append(path)
172
175
 
@@ -0,0 +1,34 @@
1
+ from flask import Blueprint, request, jsonify
2
+ from media_agent_mcp.video.omni_human import generate_video_from_omni_human
3
+
4
+ omni_human_bp = Blueprint("omni_human", __name__)
5
+
6
+ @omni_human_bp.post("/generate-video-from-omni-human")
7
+ def generate_video():
8
+ """
9
+ Generates a video from an image and audio using the Omni Human API.
10
+ """
11
+ try:
12
+ data = request.get_json(silent=True) or {}
13
+ image_url = data.get("image_url")
14
+ audio_url = data.get("audio_url")
15
+
16
+ if not image_url or not audio_url:
17
+ return jsonify({
18
+ "status": "error",
19
+ "data": None,
20
+ "message": "Fields image_url and audio_url are required"
21
+ }), 400
22
+
23
+ video_url = generate_video_from_omni_human(image_url, audio_url)
24
+
25
+ return jsonify({
26
+ "status": "success",
27
+ "data": {
28
+ "video_url": video_url
29
+ },
30
+ "message": "Video generated successfully"
31
+ })
32
+
33
+ except Exception as e:
34
+ return jsonify({"status": "error", "data": None, "message": str(e)}), 500
@@ -0,0 +1,118 @@
1
+ import hashlib
2
+ import json
3
+ import os
4
+ import random
5
+ import time
6
+ from typing import Dict, Any
7
+ import requests
8
+
9
+
10
+ def _generate_signature(nonce: int, timestamp: int, security_key: str) -> str:
11
+ """
12
+ Generates a signature for the API request.
13
+ """
14
+ keys = [str(nonce), str(security_key), str(timestamp)]
15
+ keys.sort()
16
+ key_str = "".join(keys).encode("utf-8")
17
+ signature = hashlib.sha1(key_str).hexdigest()
18
+ return signature.lower()
19
+
20
+
21
+ def _submit_task(image_url: str, audio_url: str, api_key: str, security_key: str) -> str:
22
+ """
23
+ Submits a video generation task.
24
+ """
25
+ submit_task_url = "https://cv-api.byteintlapi.com/api/common/v2/submit_task"
26
+ timestamp = int(time.time())
27
+ nonce = random.randint(0, (1 << 31) - 1)
28
+ signature = _generate_signature(nonce, timestamp, security_key)
29
+
30
+ params = {
31
+ "api_key": api_key,
32
+ "timestamp": str(timestamp),
33
+ "nonce": str(nonce),
34
+ "sign": signature,
35
+ }
36
+ headers = {"Content-Type": "application/json"}
37
+ body = {
38
+ "req_key": "realman_avatar_picture_omni_cv",
39
+ "image_url": image_url,
40
+ "audio_url": audio_url,
41
+ }
42
+
43
+ response = requests.post(submit_task_url, params=params, headers=headers, json=body)
44
+ response.raise_for_status()
45
+ data = response.json()
46
+ if data["code"] != 10000:
47
+ raise Exception(f"Failed to submit task: {data['message']}")
48
+ return data["data"]["task_id"]
49
+
50
+
51
+ def _get_task_result(task_id: str, api_key: str, security_key: str) -> Dict[str, Any]:
52
+ """
53
+ Gets the result of a video generation task.
54
+ """
55
+ get_result_url = "https://cv-api.byteintlapi.com/api/common/v2/get_result"
56
+ timestamp = int(time.time())
57
+ nonce = random.randint(0, (1 << 31) - 1)
58
+ signature = _generate_signature(nonce, timestamp, security_key)
59
+
60
+ params = {
61
+ "api_key": api_key,
62
+ "timestamp": str(timestamp),
63
+ "nonce": str(nonce),
64
+ "sign": signature,
65
+ }
66
+ headers = {"Content-Type": "application/json"}
67
+ body = {
68
+ "req_key": "realman_avatar_picture_omni_cv",
69
+ "task_id": task_id,
70
+ }
71
+
72
+ response = requests.post(get_result_url, params=params, headers=headers, json=body)
73
+ print(response.text)
74
+ response.raise_for_status()
75
+ return response.json()
76
+
77
+
78
+ def generate_video_from_omni_human(image_url: str, audio_url: str) -> str:
79
+ """
80
+ Generates a video from an image and audio using the Omni Human API.
81
+
82
+ Args:
83
+ image_url: The URL of the portrait image.
84
+ audio_url: The URL of the audio.
85
+
86
+ Returns:
87
+ The URL of the generated video.
88
+ """
89
+ api_key = os.environ.get("OMNI_HUMAN_AK")
90
+ security_key = os.environ.get("OMNI_HUMAN_SK")
91
+
92
+ if not api_key or not security_key:
93
+ raise ValueError("OMNI_HUMAN_AK and OMNI_HUMAN_SK environment variables must be set")
94
+
95
+ task_id = _submit_task(image_url, audio_url, api_key, security_key)
96
+ print('Submitted task, task_id:', task_id)
97
+ while True:
98
+ result = _get_task_result(task_id, api_key, security_key)
99
+ if result["code"] != 10000:
100
+ raise Exception(f"Failed to get task result: {result['message']}")
101
+
102
+ status = result.get("data", {}).get("status")
103
+ if status == "done":
104
+ # Parse resp_data JSON string to get video_url
105
+ resp_data_str = result["data"].get("resp_data", "{}")
106
+ try:
107
+ resp_data = json.loads(resp_data_str)
108
+ video_url = resp_data.get("video_url")
109
+ if video_url:
110
+ return video_url
111
+ else:
112
+ raise Exception(f"No video_url found in response: {resp_data}")
113
+ except json.JSONDecodeError as e:
114
+ raise Exception(f"Failed to parse resp_data JSON: {e}")
115
+ elif status in ["failed", "error"]:
116
+ raise Exception(f"Video generation failed: {result}")
117
+
118
+ time.sleep(5)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: media-agent-mcp
3
- Version: 2.7.0
3
+ Version: 2.7.2
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
@@ -26,6 +26,7 @@ Requires-Dist: loguru>=0.7.3
26
26
  Requires-Dist: imageio-ffmpeg>=0.4.0
27
27
  Requires-Dist: Flask>=3.0.0
28
28
  Requires-Dist: pydub>=0.25.1
29
+ Requires-Dist: gunicorn>=22.0.0
29
30
  Requires-Dist: audioop-lts; python_version >= "3.13"
30
31
  Requires-Dist: google-genai>=1.33.0
31
32
  Requires-Dist: mcp==1.11.0
@@ -1,8 +1,8 @@
1
1
  media_agent_mcp/__init__.py,sha256=s1rx7OkUul6PrxbxziYAV6c0AuamPlnelltQieHqX_I,340
2
- media_agent_mcp/async_server.py,sha256=boXtiFLw2mnr6ZWNZTRhchd4KAZXhbGRnOFqX-ZaJNk,19872
2
+ media_agent_mcp/async_server.py,sha256=gsc_VbZd8PwzuPc9UmtTgAYPyBzh3PNoTUNM3yTsgp4,19418
3
3
  media_agent_mcp/async_wrapper.py,sha256=hiiBhhz9WeVDfSBWVh6ovhf5jeP5ZbsieBbz9P-KPn0,15351
4
4
  media_agent_mcp/ai_models/__init__.py,sha256=2kHzTYwjQw89U4QGDq0e2WqJScqDkDNlDaWHGak5JeY,553
5
- media_agent_mcp/ai_models/omni_human.py,sha256=d6fll3qqj41Wmv6K0dd8iA_LL7kJWqHuEgxbmqf9JbY,4670
5
+ media_agent_mcp/ai_models/omni_human.py,sha256=WAWO6pI4go9FLpVwxMvizz1Ays66CMUi8XkGZmIu08w,4337
6
6
  media_agent_mcp/ai_models/openaiedit.py,sha256=uu4d2BgXSrjWRdNPs_SryI9muxO93pItVtEze9nDhjc,9776
7
7
  media_agent_mcp/ai_models/seed16.py,sha256=cX0ZONj2Jpu_dzSIq8oXSJfnsfGWVcaEmWyRxg6jMfQ,5110
8
8
  media_agent_mcp/ai_models/seedance.py,sha256=ni7LtXn4jTn5wX2NtcWDMj5Eea8LoP1QLYgwSx_GvBs,9014
@@ -14,18 +14,20 @@ media_agent_mcp/audio/speed_controller.py,sha256=IS9glznrTtbNR1ZHmaJe8Js545ttbWO
14
14
  media_agent_mcp/audio/tts.py,sha256=5lWZLXm56OUzUQYXAOu0sLUCp9FNDBrlrtvzQScXgHU,22524
15
15
  media_agent_mcp/be/README.md,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
16
  media_agent_mcp/be/__init__.py,sha256=pyMpzXQGyOOjKlxKfYZur8EmoJcNIDQSM6Zgp63HPVE,97
17
- media_agent_mcp/be/app.py,sha256=_Tq7LJ9BOWcELvebYnHKCM42jY6e3fCXDtm-kDfTDvw,945
17
+ media_agent_mcp/be/app.py,sha256=XdTI6fGELc083H8xEYGAcX1PJ6ISgWFu9R9C8i74_lo,1046
18
18
  media_agent_mcp/be/pyproject.toml,sha256=2c-vG61OpjH9Z2lz2aRoiDR047A-bOv3tFYKdxKXX68,199
19
- media_agent_mcp/be/routes_media.py,sha256=SsLNi0L3b8bbQpJV3sEiAwJw3S5PmjeCYnpQtVCjSGE,14454
19
+ media_agent_mcp/be/routes_media.py,sha256=yBDQkOx5t7v9gkSoxGnaE4Okoia3SueX8m2N5ljKpeE,14623
20
+ media_agent_mcp/be/routes_omni_human.py,sha256=SSmiGLvSJAQniwg-oODkoQnANL5iCuAKnwDTVQsPSlA,1097
20
21
  media_agent_mcp/be/routes_subtitles.py,sha256=jZYsPqyPqvIyuFyCnh7-EUpqjz-syqeqBUlhwwX0eLA,6833
21
22
  media_agent_mcp/be/utils.py,sha256=YQB_Fzwz0jhfJCOEHtahrv33VQuZZ3-_v5B8qGt3gkY,8027
22
23
  media_agent_mcp/be/uv.lock,sha256=U49x5h0EU0Sd2dn3EVvBbppM-XiujHk3Suw_z2U94SY,23502
23
- media_agent_mcp/be/__pycache__/__init__.cpython-312.pyc,sha256=ypxitYvyIAWOULz0FiRF3Wr_tlx4wKjUyTjnv-fKgsk,232
24
- media_agent_mcp/be/__pycache__/app.cpython-312.pyc,sha256=FCJLa895c4fIVJaQYDn4ipv3Spto5__lW-374cnwRNk,26673
25
- media_agent_mcp/be/__pycache__/routes_media.cpython-312.pyc,sha256=CQfwVSouBToVKnIoS1ZitthS_zjWhT97h0KOFIlO7gs,11094
24
+ media_agent_mcp/be/__pycache__/__init__.cpython-312.pyc,sha256=5vVeA7KwnQ_acE3Mdjd36WbzeqRjhO6umus1RyAoxO4,225
25
+ media_agent_mcp/be/__pycache__/app.cpython-312.pyc,sha256=QK7vq-fgrSYTb0laVfU3Yx_lug6CVVOR2KhgXtgpjFU,1401
26
+ media_agent_mcp/be/__pycache__/routes_media.cpython-312.pyc,sha256=vVIANk-HcRxYGftPDh7XeUywHrk3cGXdGe0ckyOhytw,16374
26
27
  media_agent_mcp/be/__pycache__/routes_omni.cpython-312.pyc,sha256=ZwJctEAhdE74byCLlJ_F23j0pfVcIeLbvtKeygARdr0,12443
27
- media_agent_mcp/be/__pycache__/routes_subtitles.cpython-312.pyc,sha256=5Fy7RKhr535LpAj4UVT8bBAipSwgmTm1Q2b_1_evzPU,7916
28
- media_agent_mcp/be/__pycache__/utils.cpython-312.pyc,sha256=jsHMVf1DBvQa2gHPF7_OCIFnL2tAyUXBX5H_XTKXSyA,10161
28
+ media_agent_mcp/be/__pycache__/routes_omni_human.cpython-312.pyc,sha256=VgJeY1aneYy4P0GEiyhpSUgTPLQf4664YeRXNpZ9dns,1485
29
+ media_agent_mcp/be/__pycache__/routes_subtitles.cpython-312.pyc,sha256=W931dzRYi1Dk-YhWdLXfOUJE2Zjj741Fehxd_ZuNtfs,7883
30
+ media_agent_mcp/be/__pycache__/utils.cpython-312.pyc,sha256=EKoj5x6Bt8Nv80tjL06ElO_9ZkvUBKvECzEEIKZ0oI0,11450
29
31
  media_agent_mcp/be/fonts/en/EduNSWACTCursive-VariableFont_wght.ttf,sha256=1SSZL9RXj58M7uJ_F68fRR1VQqxF6ZWK7mtoRCU-0hU,663016
30
32
  media_agent_mcp/be/fonts/en/MozillaText-VariableFont_wght.ttf,sha256=_hP_0MLtUZ6o1vG9yKaCNO0HpxXDET7DzzwicmUCSSo,84740
31
33
  media_agent_mcp/be/fonts/en/Roboto_Condensed-Regular.ttf,sha256=2Ob3xaE1bUHdgrDAAhn-vSHHkcxc0ez4oDI8vq_mSWw,145908
@@ -40,11 +42,12 @@ media_agent_mcp/media_selectors/video_selector.py,sha256=yuPbZMRm7fM0lyhoHaVN0-g
40
42
  media_agent_mcp/storage/__init__.py,sha256=eio7ZiSeLjCxICSZwZisiR7wKJfXlT2PV7aDEQKBUQQ,215
41
43
  media_agent_mcp/storage/tos_client.py,sha256=t2I-q63tMBahm4Ze1VRiN2Kn-VJGAjgqI0HAzJEJr7Y,3138
42
44
  media_agent_mcp/video/__init__.py,sha256=tfz22XEeFSeuKa3AggYCE0vCDt4IwXRCKW6avofyUsY,325
45
+ media_agent_mcp/video/omni_human.py,sha256=vXGQM9pjbKdyhUkXVnsyLIfTobXokOa-PEZOz17yOpo,3908
43
46
  media_agent_mcp/video/processor.py,sha256=twfqmN5DbVryjDawZUcqTUcnglcBJYpUbAnApqHgD0c,12787
44
47
  media_agent_mcp/video/stack.py,sha256=pyoJiJ9NhU1tjy2l3kARI9sWFoC00Fj97psxYOBi2NU,1736
45
48
  media_agent_mcp/video/subtitle.py,sha256=TlrWVhWJqYTUJpnVz7eccwMAn8ixfrRzRxS6ETMY-DM,16323
46
- media_agent_mcp-2.7.0.dist-info/METADATA,sha256=LFjhceAxy7-5kSQoeuYu0MHb5aX_aA2yp1tJgegWjW4,11305
47
- media_agent_mcp-2.7.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
48
- media_agent_mcp-2.7.0.dist-info/entry_points.txt,sha256=qhOUwR-ORVf9GO7emhhl7Lgd6MISgqbZr8bEuSH_VdA,70
49
- media_agent_mcp-2.7.0.dist-info/top_level.txt,sha256=WEa0YfchpTxZgiKn8gdxYgs-dir5HepJaTOrxAGx9nY,16
50
- media_agent_mcp-2.7.0.dist-info/RECORD,,
49
+ media_agent_mcp-2.7.2.dist-info/METADATA,sha256=ZaP4F-KrJm6eOAnNxnUcO2tzx3dnEibwavZNr0J-nqs,11337
50
+ media_agent_mcp-2.7.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
51
+ media_agent_mcp-2.7.2.dist-info/entry_points.txt,sha256=qhOUwR-ORVf9GO7emhhl7Lgd6MISgqbZr8bEuSH_VdA,70
52
+ media_agent_mcp-2.7.2.dist-info/top_level.txt,sha256=WEa0YfchpTxZgiKn8gdxYgs-dir5HepJaTOrxAGx9nY,16
53
+ media_agent_mcp-2.7.2.dist-info/RECORD,,