mp4保存

This commit is contained in:
2026-02-12 22:52:00 +09:00
parent eeb8400727
commit c15cd659e3
4 changed files with 242 additions and 108 deletions
+54 -48
View File
@@ -5,36 +5,36 @@ Manages the server process and handles HTTP communication
using standard library (avoiding requests dependency).
"""
import subprocess
import time
import json
import urllib.request
import urllib.error
import threading
import os
import signal
from typing import Optional, Dict, Any, Tuple
import subprocess
import threading
import time
import urllib.error
import urllib.request
from typing import Any, Dict, Optional, Tuple
class InferenceClient:
"""Client for the YOLO inference server."""
SERVER_URL = "http://127.0.0.1:8181"
def __init__(self):
self.server_process: Optional[subprocess.Popen] = None
self._server_lock = threading.Lock()
self.log_file = None
self.log_file_path = None
def start_server(self):
"""Start the inference server process."""
with self._server_lock:
if self.is_server_running():
return
print("[FaceMask] Starting inference server...")
# Find project root
# Assuming this file is in core/inference_client.py
root_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
@@ -46,24 +46,24 @@ class InferenceClient:
# Load environment variables from .env file if it exists
env_file = os.path.join(root_dir, ".env")
if os.path.exists(env_file):
with open(env_file, 'r') as f:
with open(env_file, "r") as f:
for line in f:
line = line.strip()
if line and not line.startswith('#') and '=' in line:
key, value = line.split('=', 1)
if line and not line.startswith("#") and "=" in line:
key, value = line.split("=", 1)
server_env[key] = value
print(f"[FaceMask] Loaded environment from: {env_file}")
# Clean PYTHONPATH to avoid conflicts with Nix Python packages
# Only include project root to allow local imports
server_env['PYTHONPATH'] = root_dir
server_env["PYTHONPATH"] = root_dir
# Remove Python-related environment variables that might cause conflicts
# These can cause venv to import packages from Nix instead of venv
env_vars_to_remove = [
'PYTHONUNBUFFERED',
'__PYVENV_LAUNCHER__', # macOS venv variable
'VIRTUAL_ENV', # Will be set by venv's Python automatically
"PYTHONUNBUFFERED",
"__PYVENV_LAUNCHER__", # macOS venv variable
"VIRTUAL_ENV", # Will be set by venv's Python automatically
]
for var in env_vars_to_remove:
server_env.pop(var, None)
@@ -73,25 +73,27 @@ class InferenceClient:
if os.path.isdir(venv_bin):
# Build a clean PATH with venv first, then essential system paths
# Filter out any Nix Python-specific paths to avoid version conflicts
current_path = server_env.get('PATH', '')
path_entries = current_path.split(':')
current_path = server_env.get("PATH", "")
path_entries = current_path.split(":")
# Filter out Nix Python 3.11 paths
filtered_paths = [
p for p in path_entries
if not ('/python3.11/' in p.lower() or '/python3-3.11' in p.lower())
p
for p in path_entries
if not ("/python3.11/" in p.lower() or "/python3-3.11" in p.lower())
]
# Reconstruct PATH with venv first
clean_path = ':'.join([venv_bin] + filtered_paths)
server_env['PATH'] = clean_path
clean_path = ":".join([venv_bin] + filtered_paths)
server_env["PATH"] = clean_path
print(f"[FaceMask] Using venv from: {venv_bin}")
# Prepare log file for server output
import tempfile
log_dir = tempfile.gettempdir()
self.log_file_path = os.path.join(log_dir, "facemask_server.log")
self.log_file = open(self.log_file_path, 'w', buffering=1) # Line buffered
self.log_file = open(self.log_file_path, "w", buffering=1) # Line buffered
print(f"[FaceMask] Server log: {self.log_file_path}")
# Start process with 'python' command (will use venv if PATH is set correctly)
@@ -120,12 +122,12 @@ class InferenceClient:
try:
if self.log_file:
self.log_file.close()
with open(self.log_file_path, 'r') as f:
with open(self.log_file_path, "r") as f:
log_content = f.read()
if log_content.strip():
print("[FaceMask] Server log:")
# Show last 50 lines
lines = log_content.strip().split('\n')
lines = log_content.strip().split("\n")
for line in lines[-50:]:
print(line)
except Exception as e:
@@ -143,18 +145,18 @@ class InferenceClient:
try:
if self.log_file:
self.log_file.close()
with open(self.log_file_path, 'r') as f:
with open(self.log_file_path, "r") as f:
log_content = f.read()
if log_content.strip():
print("[FaceMask] Server log (partial):")
lines = log_content.strip().split('\n')
lines = log_content.strip().split("\n")
for line in lines[-30:]:
print(line)
except Exception:
pass
raise RuntimeError("Server startup timed out")
def stop_server(self):
"""Stop the inference server."""
with self._server_lock:
@@ -175,15 +177,17 @@ class InferenceClient:
except Exception:
pass
self.log_file = None
def is_server_running(self) -> bool:
"""Check if server is responding."""
try:
with urllib.request.urlopen(f"{self.SERVER_URL}/status", timeout=1) as response:
with urllib.request.urlopen(
f"{self.SERVER_URL}/status", timeout=1
) as response:
return response.status == 200
except (urllib.error.URLError, ConnectionRefusedError, TimeoutError):
return False
def generate_mask(
self,
video_path: str,
@@ -196,13 +200,13 @@ class InferenceClient:
) -> str:
"""
Request mask generation.
Returns:
task_id (str)
"""
if not self.is_server_running():
self.start_server()
data = {
"video_path": video_path,
"output_dir": output_dir,
@@ -212,35 +216,36 @@ class InferenceClient:
"iou_threshold": iou_threshold,
"mask_scale": mask_scale,
}
req = urllib.request.Request(
f"{self.SERVER_URL}/generate",
data=json.dumps(data).encode('utf-8'),
headers={'Content-Type': 'application/json'},
method='POST'
data=json.dumps(data).encode("utf-8"),
headers={"Content-Type": "application/json"},
method="POST",
)
try:
with urllib.request.urlopen(req) as response:
result = json.loads(response.read().decode('utf-8'))
return result['id']
result = json.loads(response.read().decode("utf-8"))
return result["id"]
except urllib.error.HTTPError as e:
raise RuntimeError(f"Server error: {e.read().decode('utf-8')}")
def get_task_status(self, task_id: str) -> Dict[str, Any]:
"""Get status of a task."""
try:
with urllib.request.urlopen(f"{self.SERVER_URL}/tasks/{task_id}") as response:
return json.loads(response.read().decode('utf-8'))
with urllib.request.urlopen(
f"{self.SERVER_URL}/tasks/{task_id}"
) as response:
return json.loads(response.read().decode("utf-8"))
except urllib.error.HTTPError:
return {"status": "unknown"}
def cancel_task(self, task_id: str):
"""Cancel a task."""
try:
req = urllib.request.Request(
f"{self.SERVER_URL}/tasks/{task_id}/cancel",
method='POST'
f"{self.SERVER_URL}/tasks/{task_id}/cancel", method="POST"
)
with urllib.request.urlopen(req):
pass
@@ -251,6 +256,7 @@ class InferenceClient:
# Singleton
_client: Optional[InferenceClient] = None
def get_client() -> InferenceClient:
global _client
if _client is None: