Commit 00e0032691b1a8e8dbb387880872a95148092607

Authored by yibo
1 parent aabda230

fix(phoneuse): 修复 send_voice_tts_via_hook 自复制 bug + 补全 PATH 检测

- 去掉 send_voice_tts_via_hook 里多余的 staging 步骤:
  生成 SILK 后直接把临时路径传给 send_voice_via_hook,
  由它统一做 cp → wechat_cache + broadcast(避免 cp A→A 报错)
- _tts_to_silk: ffmpeg/node 路径检测增加 Termux 全路径候选,
  root su shell 下 PATH 不含 Termux bin 时也能找到

验证:device 侧 Python 直调 send_voice_tts_via_hook → filehelper 5620ms 语音 OK

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Showing 1 changed file with 19 additions and 30 deletions
phoneuse/wechat_hook_send.py
... ... @@ -286,15 +286,26 @@ def _tts_to_silk(text: str, voice: str, tmp_dir: str) -&gt; tuple[str, int]:
286 286 asyncio.run(_synth())
287 287  
288 288 # 2. ffmpeg → PCM16LE 16kHz mono
289   - if not shutil.which("ffmpeg"):
  289 + # shutil.which misses Termux bin when running under root su; probe extra paths
  290 + _FFMPEG_CANDIDATES = ["ffmpeg",
  291 + "/data/data/com.termux/files/usr/bin/ffmpeg",
  292 + "/usr/bin/ffmpeg", "/usr/local/bin/ffmpeg"]
  293 + ffmpeg_bin = next((p for p in _FFMPEG_CANDIDATES if shutil.which(p) or
  294 + (os.path.isfile(p) and os.access(p, os.X_OK))), None)
  295 + if not ffmpeg_bin:
290 296 raise RuntimeError("ffmpeg not found: (Termux) pkg install ffmpeg")
291 297 subprocess.run(
292   - ["ffmpeg", "-y", "-i", mp3, "-ar", "16000", "-ac", "1", "-f", "s16le", pcm],
  298 + [ffmpeg_bin, "-y", "-i", mp3, "-ar", "16000", "-ac", "1", "-f", "s16le", pcm],
293 299 check=True, capture_output=True
294 300 )
295 301  
296 302 # 3. Node.js silk-wasm → SILK V3 (already includes \x02#!SILK_V3 prefix)
297   - if not shutil.which("node"):
  303 + _NODE_CANDIDATES = ["node",
  304 + "/data/data/com.termux/files/usr/bin/node",
  305 + "/usr/bin/node", "/usr/local/bin/node"]
  306 + node_bin = next((p for p in _NODE_CANDIDATES if shutil.which(p) or
  307 + (os.path.isfile(p) and os.access(p, os.X_OK))), None)
  308 + if not node_bin:
298 309 raise RuntimeError("node not found: (Termux) pkg install nodejs")
299 310  
300 311 # Locate silk-wasm: check global npm, then openclaw bundle
... ... @@ -315,7 +326,7 @@ silk.encode(fs.readFileSync(&#39;{pcm}&#39;), 16000).then(function(r) {{
315 326 process.stdout.write(String(r.duration));
316 327 }}).catch(function(e) {{ process.stderr.write(String(e)); process.exit(1); }});
317 328 """
318   - r = subprocess.run(["node", "-e", node_script], capture_output=True, text=True, timeout=60)
  329 + r = subprocess.run([node_bin, "-e", node_script], capture_output=True, text=True, timeout=60)
319 330 if r.returncode != 0:
320 331 raise RuntimeError(f"silk-wasm encode failed: {r.stderr or r.stdout}. "
321 332 "Install: npm install -g silk-wasm")
... ... @@ -357,32 +368,10 @@ def send_voice_tts_via_hook(
357 368 except Exception as e:
358 369 return {"status": "error", "error": f"TTS/SILK generation failed: {e}"}
359 370  
360   - wechat_cache = "/data/data/com.tencent.mm/cache/hook_voice_src.amr"
361   - if _ON_DEVICE:
362   - stage = _root_sh(
363   - f'cp "{silk_path}" "{wechat_cache}" && '
364   - f'chmod 644 "{wechat_cache}" && '
365   - f'chown u0_a330:u0_a330 "{wechat_cache}"'
366   - )
367   - else:
368   - adb = _adb_prefix()
369   - remote_tmp = "/sdcard/hook_voice_tts_tmp.amr"
370   - subprocess.run(adb + ["push", silk_path, remote_tmp],
371   - check=True, capture_output=True)
372   - stage = _root_sh(
373   - f'cp "{remote_tmp}" "{wechat_cache}" && '
374   - f'chmod 644 "{wechat_cache}" && '
375   - f'chown u0_a330:u0_a330 "{wechat_cache}" && '
376   - f'rm -f "{remote_tmp}"'
377   - )
378   -
379   - if stage.returncode != 0:
380   - return {"status": "error",
381   - "error": f"failed to stage SILK: {stage.stderr or stage.stdout}"}
382   -
383   - # TemporaryDirectory is cleaned up; wechat_cache is intact
384   - result = send_voice_via_hook(wxid=wxid, silk_path=wechat_cache,
385   - duration_ms=duration_ms)
  371 + # Delegate staging + broadcast to send_voice_via_hook (it handles both
  372 + # on-device and off-device paths). Must call while tmp dir is still alive.
  373 + result = send_voice_via_hook(wxid=wxid, silk_path=silk_path,
  374 + duration_ms=duration_ms)
386 375 result["tts_text"] = text[:100]
387 376 result["tts_voice"] = voice
388 377 return result
... ...