forked from MoonHugo/ComfyUI-FFmpeg
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathapi_routes.py
More file actions
114 lines (87 loc) · 3.59 KB
/
api_routes.py
File metadata and controls
114 lines (87 loc) · 3.59 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
from server import PromptServer
from aiohttp import web
import json
import os
routes = PromptServer.instance.routes
def validate_path(path):
# Security disabled allowed for all local paths (power-user mode)
return os.path.exists(path)
@routes.post("/comfyui-ffmpeg/metadata")
async def get_video_metadata(request):
try:
data = await request.json()
video_path = data.get("path")
if not video_path:
return web.json_response({"error": "No path provided"}, status=400)
if not os.path.exists(video_path):
return web.json_response({"error": "File not found"}, status=404)
if not validate_path(video_path):
return web.json_response(
{"error": "Security violation: Path not allowed"}, status=403
)
# Run ffprobe
cmd = [
"ffprobe",
"-v",
"error",
"-select_streams",
"v:0",
"-show_entries",
"stream=duration,r_frame_rate",
"-show_entries",
"packet=pts_time,flags",
"-of",
"json",
video_path,
]
# Run in a separate thread to avoid blocking the event loop
# Since we are in an async function, subprocess.run would block.
# But for simplicity in this initial port, we can use synchronous run if it's fast enough,
# or better: use asyncio.create_subprocess_exec (but that requires parsing stdout async).
# For now, let's stick to the existing logic but wrap it potentially?
# Actually, let's keep it simple. ffprobe is usually fast for metadata,
# BUT for keyframes on large files it might take time.
# Ideally: await asyncio.to_thread(...)
try:
import asyncio
process = await asyncio.create_subprocess_exec(
*cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE
)
stdout, stderr = await process.communicate()
if process.returncode != 0:
return web.json_response(
{"error": f"ffprobe failed: {stderr.decode()}"}, status=500
)
data = json.loads(stdout.decode())
except Exception as e:
return web.json_response(
{"error": f"Execution error: {str(e)}"}, status=500
)
keyframes = []
if "packets" in data:
for packet in data.get("packets", []):
if "K" in packet.get("flags", ""):
keyframes.append(float(packet["pts_time"]))
if not data.get("streams"):
return web.json_response({"error": "No video stream found"}, status=500)
fps_str = data["streams"][0]["r_frame_rate"]
num, den = map(int, fps_str.split("/"))
fps = num / den if den != 0 else 0
result = {
"duration": float(data["streams"][0].get("duration", 0)),
"fps": fps,
"keyframes": keyframes,
}
return web.json_response(result)
except Exception as e:
return web.json_response({"error": str(e)}, status=500)
@routes.get("/comfyui-ffmpeg/stream")
async def stream_video(request):
video_path = request.query.get("path")
if not video_path:
return web.Response(status=400, text="Missing path")
if not os.path.exists(video_path):
return web.Response(status=404, text="File not found")
if not validate_path(video_path):
return web.Response(status=403, text="Security violation: Path not allowed")
return web.FileResponse(video_path)