From 220e4fab27b7f419b3e89e2f2d28ae789aac09d7 Mon Sep 17 00:00:00 2001
From: wangzhibo <wangzhibo@shsening.com>
Date: 星期四, 08 十月 2026 16:29:55 +0800
Subject: [PATCH] 5种场景模式家长端ok

---
 xiaozhi-esp32-server/main/xiaozhi-server/core/audio_archive.py |   64 ++++++++++++++++++++++++++++++-
 1 files changed, 61 insertions(+), 3 deletions(-)

diff --git a/xiaozhi-esp32-server/main/xiaozhi-server/core/audio_archive.py b/xiaozhi-esp32-server/main/xiaozhi-server/core/audio_archive.py
index 7e5e701..efeb8bb 100644
--- a/xiaozhi-esp32-server/main/xiaozhi-server/core/audio_archive.py
+++ b/xiaozhi-esp32-server/main/xiaozhi-server/core/audio_archive.py
@@ -2,6 +2,7 @@
 
 from __future__ import annotations
 
+import json
 import os
 import time
 from typing import Any, Optional
@@ -59,14 +60,63 @@
     return os.path.join(get_project_dir(), rel)
 
 
+def plain_transcript(text: Any) -> str:
+    """ASR/TTS 褰掓。鏂囨湰銆侳unASR 鍙兘鏄� {content, speaker, ...} JSON銆�"""
+    if text is None:
+        return ""
+    raw = str(text)
+    stripped = raw.strip()
+    if stripped.startswith("{") and stripped.endswith("}"):
+        try:
+            data = json.loads(stripped)
+            if isinstance(data, dict) and "content" in data:
+                return str(data.get("content") or "")
+        except Exception:
+            pass
+    return raw
+
+
+def write_sidecar_text(wav_path: str, text: Optional[str]) -> Optional[str]:
+    """Write UTF-8 .txt next to a wav. Same stem, suffix .txt. None skips."""
+    if text is None or not wav_path:
+        return None
+    sidecar = os.path.splitext(wav_path)[0] + ".txt"
+    try:
+        body = plain_transcript(text)
+        with open(sidecar, "w", encoding="utf-8") as handle:
+            handle.write(body)
+            if body and not body.endswith("\n"):
+                handle.write("\n")
+        return sidecar
+    except Exception as exc:
+        logger.bind(tag=TAG).warning(f"鏂囨湰褰掓。澶辫触: {exc}")
+        return None
+
+
+def write_sidecar_for_relative(conn: Any, relative: str, text: Optional[str]) -> Optional[str]:
+    """Update the .txt next to an already archived wav (relative audio_id)."""
+    if not relative or text is None:
+        return None
+    rel = str(relative).replace("\\", "/").lstrip("/")
+    if "/" in rel:
+        path = os.path.join(archive_root(conn), *rel.split("/"))
+    else:
+        path = os.path.join(get_project_dir(), "data", "task_sessions", "audio", rel)
+    return write_sidecar_text(path, text)
+
+
 def archive_wav(
     conn: Any,
     wav_bytes: bytes,
     *,
     kind: str = "asr",
     extra: str = "",
+    text: Optional[str] = None,
 ) -> Optional[str]:
-    """Write WAV under recordings/{device}/{mode}/{date}/{time}_{kind}_{extra}.wav."""
+    """Write WAV under recordings/{device}/{mode}/{date}/{time}_{kind}_{extra}.wav.
+
+    If text is not None, also write the same stem with .txt for later analysis.
+    """
     if not wav_bytes or not archive_enabled(conn):
         return None
     try:
@@ -86,6 +136,8 @@
         path = os.path.join(directory, filename)
         with open(path, "wb") as handle:
             handle.write(wav_bytes)
+        if text is not None:
+            write_sidecar_text(path, text)
         relative = os.path.join(device, mode, day, filename).replace("\\", "/")
         logger.bind(tag=TAG).info(f"闊抽褰掓。 {relative} bytes={len(wav_bytes)}")
         return relative
@@ -94,10 +146,16 @@
         return None
 
 
-def archive_pcm_snapshot(conn: Any, snapshot: Any, *, extra: str = "") -> Optional[str]:
+def archive_pcm_snapshot(
+    conn: Any,
+    snapshot: Any,
+    *,
+    extra: str = "",
+    text: Optional[str] = None,
+) -> Optional[str]:
     from core.app_runtime.assessment import pcm_snapshot_to_wav
 
     wav_bytes = pcm_snapshot_to_wav(snapshot)
     if not wav_bytes:
         return None
-    return archive_wav(conn, wav_bytes, kind="asr", extra=extra)
+    return archive_wav(conn, wav_bytes, kind="asr", extra=extra, text=text)

--
Gitblit v1.9.1