aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-node/src/meshbay_node/media_probe.py
diff options
context:
space:
mode:
Diffstat (limited to 'packages/meshbay-node/src/meshbay_node/media_probe.py')
-rw-r--r--packages/meshbay-node/src/meshbay_node/media_probe.py17
1 files changed, 16 insertions, 1 deletions
diff --git a/packages/meshbay-node/src/meshbay_node/media_probe.py b/packages/meshbay-node/src/meshbay_node/media_probe.py
index a6267d9..e9090ea 100644
--- a/packages/meshbay-node/src/meshbay_node/media_probe.py
+++ b/packages/meshbay-node/src/meshbay_node/media_probe.py
@@ -10,6 +10,12 @@ import asyncio
import json
from dataclasses import dataclass, field
+# How long ffprobe may take over one file's headers. The file is a member's
+# upload as often as the operator's own: one that keeps ffprobe busy must not
+# keep the stream request, the subtitle request or the enrichment slot that
+# asked for it — the same bound the index-time enrichment already put around it.
+FFPROBE_TIMEOUT_SECS = 30
+
_H264_PROFILES = {"Baseline": "42", "Main": "4d", "High": "64", "High 10": "6e"}
# Source video codecs whose MSE codec string is real but which no mainstream
@@ -158,7 +164,16 @@ async def probe_video(path: str) -> VideoProbe:
"-of", "json", path,
stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE,
)
- stdout, _ = await proc.communicate()
+ try:
+ stdout, _ = await asyncio.wait_for(proc.communicate(), FFPROBE_TIMEOUT_SECS)
+ except (TimeoutError, asyncio.CancelledError) as e:
+ # Killed, not abandoned: a cancelled wait leaves the process running,
+ # and a caller's own timeout (enrich.py) cancels exactly this wait.
+ proc.kill()
+ await proc.wait()
+ if isinstance(e, asyncio.CancelledError):
+ raise
+ raise RuntimeError(f"ffprobe timed out after {FFPROBE_TIMEOUT_SECS}s") from None
info = json.loads(stdout)
duration = float(info.get("format", {}).get("duration", 0))