重构无人直播助手:内嵌 youLIVE、摄像头直推、推流诊断与 UI 焕新
将 README 改写为产品文档;移除微信/EasyTier/工作台等遗留模块;新增 youLIVE 目标端监控、摄像头推流、质量守卫与转播历史能力。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
105
web2_youtube_diagnostics.py
Normal file
105
web2_youtube_diagnostics.py
Normal file
@@ -0,0 +1,105 @@
|
||||
"""YouTube relay diagnostics: source + FFmpeg + target watch URL layers."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import time
|
||||
from typing import Any
|
||||
|
||||
from web2_quality_guard import build_youtube_quality_guard_report, parse_ffmpeg_metrics
|
||||
|
||||
|
||||
def _source_configured(url_lines: str) -> bool:
|
||||
for raw in str(url_lines or "").splitlines():
|
||||
t = raw.strip()
|
||||
if not t or t.startswith("#"):
|
||||
continue
|
||||
if "douyin" in t.lower() or "live.douyin.com" in t.lower():
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def build_youtube_diagnostics_report(
|
||||
rows: list[dict[str, Any]],
|
||||
*,
|
||||
config_by_process: dict[str, dict[str, str]] | None = None,
|
||||
) -> dict[str, Any]:
|
||||
config_by_process = config_by_process or {}
|
||||
guard = build_youtube_quality_guard_report(rows, config_by_process=config_by_process)
|
||||
lanes: list[dict[str, Any]] = []
|
||||
target_ok = 0
|
||||
source_ok = 0
|
||||
ffmpeg_ok = 0
|
||||
|
||||
for lane in guard.get("lanes") or []:
|
||||
proc = str(lane.get("process") or "")
|
||||
cfg = config_by_process.get(proc) or {}
|
||||
watch = str(cfg.get("watch_url") or "").strip()
|
||||
src_cfg = _source_configured(cfg.get("url_lines", ""))
|
||||
pushing = bool(lane.get("is_pushing"))
|
||||
metrics = lane.get("metrics") if isinstance(lane.get("metrics"), dict) else {}
|
||||
if not metrics and pushing:
|
||||
metrics = parse_ffmpeg_metrics(
|
||||
str((rows[0] if rows else {}).get("recent_log") or ""),
|
||||
)
|
||||
|
||||
issues = list(lane.get("issues") or [])
|
||||
score = int(lane.get("score") or 0)
|
||||
|
||||
if src_cfg:
|
||||
source_ok += 1
|
||||
else:
|
||||
issues.append({"level": "warning", "text": "源站地址未配置"})
|
||||
score = max(0, score - 12)
|
||||
|
||||
if watch:
|
||||
target_ok += 1
|
||||
else:
|
||||
issues.append({"level": "warning", "text": "未配置 YouTube 目标监听地址"})
|
||||
score = max(0, score - 8)
|
||||
|
||||
ffmpeg_active = bool(lane.get("ffmpeg_active"))
|
||||
if pushing and ffmpeg_active:
|
||||
ffmpeg_ok += 1
|
||||
elif pushing and not ffmpeg_active:
|
||||
issues.append({"level": "critical", "text": "推流中但未检测到 FFmpeg 进程"})
|
||||
|
||||
lanes.append(
|
||||
{
|
||||
**lane,
|
||||
"score": max(0, min(100, score)),
|
||||
"issues": issues[:8],
|
||||
"layers": {
|
||||
"source": {
|
||||
"ok": src_cfg,
|
||||
"hint": lane.get("source_url") or "",
|
||||
},
|
||||
"ffmpeg": {
|
||||
"ok": ffmpeg_active or not pushing,
|
||||
"pushing": pushing,
|
||||
"metrics": metrics,
|
||||
},
|
||||
"target": {
|
||||
"ok": bool(watch),
|
||||
"watch_url": watch,
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
lane_count = len(lanes)
|
||||
overall = int(sum(l["score"] for l in lanes) / lane_count) if lane_count else 0
|
||||
return {
|
||||
**guard,
|
||||
"score": max(0, min(100, overall)),
|
||||
"lanes": lanes,
|
||||
"diagnostics": {
|
||||
"generated_at": time.time(),
|
||||
"layers": ["source", "ffmpeg", "target"],
|
||||
"summary": {
|
||||
"lanes": lane_count,
|
||||
"source_configured": source_ok,
|
||||
"target_configured": target_ok,
|
||||
"ffmpeg_active": ffmpeg_ok,
|
||||
},
|
||||
},
|
||||
}
|
||||
Reference in New Issue
Block a user