chore: atualização geral
This commit is contained in:
@@ -0,0 +1,133 @@
|
||||
"""Roles — tool schemas and handlers.
|
||||
|
||||
Extracted from server.py; see Engine/docs/03_SERVER_TOOLS.md for the tool catalog.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Sequence
|
||||
|
||||
from mcp.types import TextContent, Tool
|
||||
|
||||
from server_tools._shared import (
|
||||
_require_timeline,
|
||||
_setup_modifier,
|
||||
_text_result,
|
||||
format_duration,
|
||||
)
|
||||
|
||||
TOOLS = [
|
||||
Tool(
|
||||
name="assign_role",
|
||||
description="Set the audio or video role on a clip (dialogue, music, effects, titles, etc.)",
|
||||
inputSchema={
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"filepath": {"type": "string", "description": "Path to FCPXML file"},
|
||||
"clip_id": {"type": "string", "description": "Clip name or ID"},
|
||||
"audio_role": {"type": "string", "description": "Audio role (e.g., dialogue, music, effects)"},
|
||||
"video_role": {"type": "string", "description": "Video role (e.g., video, titles)"},
|
||||
"output_path": {"type": "string", "description": "Output path (default: adds _modified suffix)"}
|
||||
},
|
||||
"required": ["filepath", "clip_id"]
|
||||
}
|
||||
),
|
||||
Tool(
|
||||
name="filter_by_role",
|
||||
description="List all clips matching a specific audio or video role",
|
||||
inputSchema={
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"filepath": {"type": "string", "description": "Path to FCPXML file"},
|
||||
"role": {"type": "string", "description": "Role name to filter by"},
|
||||
"role_type": {"type": "string", "enum": ["audio", "video", "any"], "default": "any", "description": "Which role type to search"},
|
||||
},
|
||||
"required": ["filepath", "role"]
|
||||
}
|
||||
),
|
||||
Tool(
|
||||
name="export_role_stems",
|
||||
description="Export clip list grouped by role for audio mixing stem planning",
|
||||
inputSchema={
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"filepath": {"type": "string", "description": "Path to FCPXML file"},
|
||||
},
|
||||
"required": ["filepath"]
|
||||
}
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
async def handle_assign_role(arguments: dict) -> Sequence[TextContent]:
|
||||
filepath, output_path, modifier = _setup_modifier(arguments)
|
||||
modifier.assign_role(
|
||||
clip_id=arguments["clip_id"],
|
||||
audio_role=arguments.get("audio_role"),
|
||||
video_role=arguments.get("video_role"),
|
||||
)
|
||||
modifier.save(output_path)
|
||||
|
||||
roles_set = []
|
||||
if arguments.get("audio_role"):
|
||||
roles_set.append(f"audioRole={arguments['audio_role']}")
|
||||
if arguments.get("video_role"):
|
||||
roles_set.append(f"videoRole={arguments['video_role']}")
|
||||
|
||||
return _text_result((
|
||||
f"Set {', '.join(roles_set)} on '{arguments['clip_id']}'\n\n"
|
||||
f"Saved to: `{output_path}`"
|
||||
))
|
||||
|
||||
|
||||
async def handle_filter_by_role(arguments: dict) -> Sequence[TextContent]:
|
||||
project, tl = _require_timeline(arguments["filepath"])
|
||||
|
||||
role = arguments["role"].lower()
|
||||
role_type = arguments.get("role_type", "any")
|
||||
matches = []
|
||||
|
||||
for clip in tl.clips:
|
||||
if role_type in ("audio", "any") and clip.audio_role.lower() == role:
|
||||
matches.append((clip.name, "audio", clip.audio_role, format_duration(clip.duration_seconds)))
|
||||
if role_type in ("video", "any") and clip.video_role.lower() == role:
|
||||
matches.append((clip.name, "video", clip.video_role, format_duration(clip.duration_seconds)))
|
||||
|
||||
if not matches:
|
||||
return _text_result(f"No clips found with role '{role}'.")
|
||||
|
||||
result = f"# Clips with role '{role}'\n\n"
|
||||
result += "| Clip | Type | Role | Duration |\n|------|------|------|----------|\n"
|
||||
for name, rtype, rval, dur in matches:
|
||||
result += f"| {name} | {rtype} | {rval} | {dur} |\n"
|
||||
return _text_result(result)
|
||||
|
||||
|
||||
async def handle_export_role_stems(arguments: dict) -> Sequence[TextContent]:
|
||||
project, tl = _require_timeline(arguments["filepath"])
|
||||
|
||||
stems: dict[str, list] = {}
|
||||
for clip in tl.clips:
|
||||
role = clip.audio_role or "unassigned"
|
||||
stems.setdefault(role, []).append(clip)
|
||||
|
||||
for cc in tl.connected_clips:
|
||||
role = cc.role or "unassigned"
|
||||
stems.setdefault(role, []).append(cc)
|
||||
|
||||
result = f"# Audio Stem Plan for {tl.name}\n\n"
|
||||
for role, clips in sorted(stems.items()):
|
||||
total_dur = sum(c.duration_seconds for c in clips)
|
||||
result += f"## {role.title()} ({len(clips)} clips, {format_duration(total_dur)})\n\n"
|
||||
for c in clips:
|
||||
result += f"- {c.name} ({format_duration(c.duration_seconds)})\n"
|
||||
result += "\n"
|
||||
|
||||
return _text_result(result)
|
||||
|
||||
|
||||
HANDLERS = {
|
||||
"assign_role": handle_assign_role,
|
||||
"filter_by_role": handle_filter_by_role,
|
||||
"export_role_stems": handle_export_role_stems,
|
||||
}
|
||||
Reference in New Issue
Block a user