mp4保存
This commit is contained in:
+54
-48
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user