Deploy to new ubuntu machine
This commit is contained in:
@@ -15,6 +15,7 @@ from app.db import (
|
||||
load_watch_processed,
|
||||
save_health_snapshot,
|
||||
)
|
||||
from app.logging_config import new_run_id, stage
|
||||
from app.services import action as action_svc
|
||||
from app.services.measure import transcode_src_to_h264_dst
|
||||
from app.settings import Settings
|
||||
@@ -70,24 +71,21 @@ def _publish_slice_video(
|
||||
"""
|
||||
tmp = dst.with_name(dst.stem + "_tmp.mp4")
|
||||
tmp.unlink(missing_ok=True)
|
||||
log = logger.bind(pipeline="action_watch", source=src.name)
|
||||
try:
|
||||
ok = transcode_src_to_h264_dst(src, tmp)
|
||||
if ok and tmp.is_file() and tmp.stat().st_size > 0:
|
||||
tmp.replace(dst)
|
||||
logger.info(
|
||||
"[action-watch] published H.264: {} -> {}", src.name, dst.name,
|
||||
)
|
||||
log.info("[行为监控] 切片已发布为 H.264:{} -> {}", src.name, dst.name)
|
||||
else:
|
||||
tmp.unlink(missing_ok=True)
|
||||
shutil.copy2(src, dst)
|
||||
logger.warning(
|
||||
"[action-watch] transcode failed, copied raw: {} -> {}",
|
||||
src.name,
|
||||
dst.name,
|
||||
log.warning(
|
||||
"[行为监控] 转码失败,已直接拷贝原文件:{} -> {}", src.name, dst.name,
|
||||
)
|
||||
return _public_media_url(settings, dst.name)
|
||||
except Exception:
|
||||
logger.exception("[action-watch] publish failed for {}", src.name)
|
||||
log.exception("[行为监控] 切片发布异常 | {}", src.name)
|
||||
tmp.unlink(missing_ok=True)
|
||||
if dst.is_file():
|
||||
return _public_media_url(settings, dst.name)
|
||||
@@ -103,53 +101,71 @@ async def _run_inference_and_state(
|
||||
key = str(mp4.resolve())
|
||||
if key in processed:
|
||||
return
|
||||
logger.info("[action-watch] inference: {}", mp4)
|
||||
|
||||
rid = new_run_id("action_watch")
|
||||
log = logger.bind(pipeline="action_watch", run_id=rid, source=mp4.name)
|
||||
try:
|
||||
size_mb = mp4.stat().st_size / (1024 * 1024)
|
||||
except OSError:
|
||||
size_mb = 0.0
|
||||
log.info(
|
||||
"[行为监控] 触发行为识别 | mp4={} | 大小={:.1f} MB", mp4, size_mb,
|
||||
)
|
||||
|
||||
async with app_state.action_lock:
|
||||
app_state.action_status = "running"
|
||||
try:
|
||||
slice_files, duration = await to_thread(
|
||||
action_svc.prepare_action_slices, mp4, settings
|
||||
)
|
||||
|
||||
settings.media_root.mkdir(parents=True, exist_ok=True)
|
||||
base_name = (
|
||||
settings.biomass_water_video_media_name or "biomass_water_surface.mp4"
|
||||
)
|
||||
|
||||
saved_count = 0
|
||||
first_snap = None
|
||||
total_slices = len(slice_files)
|
||||
|
||||
for i, slice_file in enumerate(slice_files):
|
||||
snap = await to_thread(
|
||||
action_svc.run_single_slice_inference,
|
||||
slice_file, i, total_slices, duration, mp4.name, settings,
|
||||
with stage(
|
||||
f"行为识别 watch 全流程({mp4.name})",
|
||||
pipeline="action_watch",
|
||||
step="file_total",
|
||||
run_id=rid,
|
||||
source=mp4.name,
|
||||
metrics={"size_mb": round(size_mb, 2)},
|
||||
):
|
||||
slice_files, duration = await to_thread(
|
||||
action_svc.prepare_action_slices, mp4, settings, run_id=rid,
|
||||
)
|
||||
|
||||
if first_snap is None:
|
||||
first_snap = snap
|
||||
|
||||
if not health_snapshot_deliverable(snap):
|
||||
continue
|
||||
|
||||
video_url = ""
|
||||
media_basename = _slice_media_basename(base_name, i)
|
||||
dst = settings.media_root / media_basename
|
||||
video_url = await to_thread(
|
||||
_publish_slice_video, slice_file, dst, settings,
|
||||
settings.media_root.mkdir(parents=True, exist_ok=True)
|
||||
base_name = (
|
||||
settings.biomass_water_video_media_name or "biomass_water_surface.mp4"
|
||||
)
|
||||
|
||||
slice_key = f"{key}#slice{i}"
|
||||
save_health_snapshot(
|
||||
settings, snap, source_path=slice_key, video_url=video_url,
|
||||
)
|
||||
saved_count += 1
|
||||
saved_count = 0
|
||||
first_snap = None
|
||||
total_slices = len(slice_files)
|
||||
|
||||
if saved_count == 0:
|
||||
logger.warning(
|
||||
"[action-watch] no deliverable health snapshot for {}, skip SQLite",
|
||||
mp4.name,
|
||||
)
|
||||
for i, slice_file in enumerate(slice_files):
|
||||
snap = await to_thread(
|
||||
action_svc.run_single_slice_inference,
|
||||
slice_file, i, total_slices, duration, mp4.name, settings,
|
||||
run_id=rid,
|
||||
)
|
||||
|
||||
if first_snap is None:
|
||||
first_snap = snap
|
||||
|
||||
if not health_snapshot_deliverable(snap):
|
||||
continue
|
||||
|
||||
video_url = ""
|
||||
media_basename = _slice_media_basename(base_name, i)
|
||||
dst = settings.media_root / media_basename
|
||||
video_url = await to_thread(
|
||||
_publish_slice_video, slice_file, dst, settings,
|
||||
)
|
||||
|
||||
slice_key = f"{key}#slice{i}"
|
||||
save_health_snapshot(
|
||||
settings, snap, source_path=slice_key, video_url=video_url,
|
||||
)
|
||||
saved_count += 1
|
||||
|
||||
if saved_count == 0:
|
||||
log.warning(
|
||||
"[行为监控] 无可投递结果,跳过写库 | mp4={}", mp4.name,
|
||||
)
|
||||
|
||||
app_state.action_status = "idle"
|
||||
processed.add(key)
|
||||
@@ -157,14 +173,14 @@ async def _run_inference_and_state(
|
||||
add_watch_processed(settings, key, "action")
|
||||
|
||||
pred = (first_snap.raw_class_en or "").strip() if first_snap else ""
|
||||
logger.info(
|
||||
"[action-watch] done: {} -> {} (saved {} slices)",
|
||||
mp4.name,
|
||||
pred,
|
||||
saved_count,
|
||||
zh = first_snap.behavior_result if first_snap else ""
|
||||
health = first_snap.health_result if first_snap else ""
|
||||
log.info(
|
||||
"[行为监控] 完成 | mp4={} | 类别={} ({}) | 健康={} | 投递切片={}",
|
||||
mp4.name, zh or "-", pred or "-", health or "-", saved_count,
|
||||
)
|
||||
except Exception as e:
|
||||
logger.exception("[action-watch] error on {}: {}", mp4, e)
|
||||
log.exception("[行为监控] 处理异常 | mp4={} | {}", mp4, e)
|
||||
app_state.action_status = "idle"
|
||||
processed.add(key)
|
||||
if settings.action_watch_use_state_file:
|
||||
@@ -216,7 +232,9 @@ async def run_action_watch_loop(settings: Settings) -> None:
|
||||
assert settings.action_watch_dir is not None
|
||||
wd = settings.action_watch_dir
|
||||
if not wd.is_dir():
|
||||
logger.warning("[action-watch] skip: not a directory: {}", wd)
|
||||
logger.bind(pipeline="action_watch").warning(
|
||||
"[行为监控] 启动跳过:路径不是目录 {}", wd
|
||||
)
|
||||
return
|
||||
|
||||
state_file = _state_path(settings)
|
||||
@@ -227,8 +245,8 @@ async def run_action_watch_loop(settings: Settings) -> None:
|
||||
)
|
||||
stability = {} # type: Dict[str, Tuple[int, int]]
|
||||
|
||||
logger.info(
|
||||
"[action-watch] watching {} (poll={}s, stable_polls={}, state={} {})",
|
||||
logger.bind(pipeline="action_watch").info(
|
||||
"[行为监控] 监控目录已启动 | dir={} | poll={}s | stable_polls={} | state={} {}",
|
||||
wd,
|
||||
settings.action_watch_poll_interval,
|
||||
settings.action_watch_stable_polls,
|
||||
|
||||
Reference in New Issue
Block a user