Files
managed-portal/managed/people_flow_project/scripts/docker-entrypoint.sh

139 lines
3.6 KiB
Bash
Executable File

#!/usr/bin/env sh
set -eu
PROJECT_DIR="/opt/people-flow"
CONFIG_TEMPLATE="${PROJECT_DIR}/config/config.example.yaml"
CONFIG_PATH="${CONFIG_PATH:-${PROJECT_DIR}/config/local.yaml}"
OUTPUT_DIR="${OUTPUT_DIR:-${PROJECT_DIR}/outputs}"
RTSP_URL="${RTSP_URL:-}"
API_HOST="${API_HOST:-0.0.0.0}"
API_PORT="${API_PORT:-18082}"
RTSP_STALL_TIMEOUT_SECONDS="${RTSP_STALL_TIMEOUT_SECONDS:-180}"
DEEPFACE_CACHE_DIR="/root/.deepface/weights"
DEEPFACE_SOURCE_DIR="${PROJECT_DIR}/weights/deepface"
mkdir -p "${OUTPUT_DIR}" "$(dirname "${CONFIG_PATH}")" "${DEEPFACE_CACHE_DIR}"
if [ -d "${DEEPFACE_SOURCE_DIR}" ]; then
find "${DEEPFACE_SOURCE_DIR}" -maxdepth 1 -name '*.h5' -exec cp {} "${DEEPFACE_CACHE_DIR}/" \;
fi
if [ ! -f "${CONFIG_PATH}" ]; then
cp "${CONFIG_TEMPLATE}" "${CONFIG_PATH}"
fi
python - "$CONFIG_PATH" "$RTSP_URL" "$OUTPUT_DIR" <<'PY'
from pathlib import Path
import sys
import yaml
config_path = Path(sys.argv[1])
rtsp_url = sys.argv[2]
output_dir = sys.argv[3]
raw = yaml.safe_load(config_path.read_text(encoding="utf-8")) or {}
runtime = raw.setdefault("runtime", {})
if rtsp_url:
runtime["rtsp_url"] = rtsp_url
runtime["output_dir"] = output_dir
yolo = raw.setdefault("yolo", {})
yolo.setdefault("model_path", "weights/yolo11n.pt")
config_path.write_text(
yaml.safe_dump(raw, allow_unicode=True, sort_keys=False),
encoding="utf-8",
)
PY
RTSP_OUTPUT_SUBDIR="$(python - "$CONFIG_PATH" <<'PY'
from pathlib import Path
import sys
import yaml
config_path = Path(sys.argv[1])
raw = yaml.safe_load(config_path.read_text(encoding="utf-8")) or {}
rtsp = raw.get("rtsp") or {}
print(rtsp.get("output_subdir", "rtsp_stream"))
PY
)"
RTSP_STATUS_PATH="${OUTPUT_DIR}/${RTSP_OUTPUT_SUBDIR}/worker_status.json"
exec python - "$CONFIG_PATH" "$API_HOST" "$API_PORT" "$RTSP_STATUS_PATH" "$RTSP_STALL_TIMEOUT_SECONDS" <<'PY'
from pathlib import Path
import signal
import subprocess
import sys
import time
from src.people_flow.worker_status import worker_status_stall_reason
config_path, api_host, api_port, status_path_raw, stall_timeout_raw = sys.argv[1:6]
status_path = Path(status_path_raw)
stall_timeout_seconds = max(float(stall_timeout_raw), 30.0)
commands = [
[sys.executable, "main.py", "--config", config_path, "rtsp"],
[
sys.executable,
"main.py",
"--config",
config_path,
"manage-api",
"--host",
api_host,
"--port",
api_port,
],
]
processes = [subprocess.Popen(command) for command in commands]
supervisor_started_at = time.time()
def stop_all(excluded_index=None):
for index, process in enumerate(processes):
if index == excluded_index or process.poll() is not None:
continue
process.terminate()
deadline = time.time() + 10
for index, process in enumerate(processes):
if index == excluded_index or process.poll() is not None:
continue
timeout = max(0, deadline - time.time())
try:
process.wait(timeout=timeout)
except subprocess.TimeoutExpired:
process.kill()
def stale_reason():
if processes[0].poll() is not None:
return None
return worker_status_stall_reason(
status_path,
started_at=supervisor_started_at,
max_age_seconds=stall_timeout_seconds,
)
def terminate_all(signum, _frame):
stop_all()
raise SystemExit(128 + signum)
for handled_signal in (signal.SIGINT, signal.SIGTERM):
signal.signal(handled_signal, terminate_all)
while True:
for index, process in enumerate(processes):
return_code = process.poll()
if return_code is None:
continue
stop_all(excluded_index=index)
raise SystemExit(return_code)
reason = stale_reason()
if reason is not None:
print(reason, flush=True)
stop_all()
raise SystemExit(1)
time.sleep(0.5)
PY