gcli2api / src /router /xtc_recognize /fingerprint.py
a3216's picture
feat: 集成 XTC 听歌识曲模块(/xtc/* 路由,无侵入)
d731d8e
Raw
History Blame Contribute Delete
1.53 kB
"""调用 Node.js 子进程运行 afp.js 生成音频指纹。
通过 stdin/stdout 通信,避免 Node 进程频繁启动开销(每次仍会重启 wasm,
但实测 93ms 足够快)。
"""
from __future__ import annotations
import asyncio
import base64
import logging
import os
from typing import Optional
logger = logging.getLogger(__name__)
# afp_runner.js 路径(相对于本文件)
_RUNNER_PATH = os.path.join(os.path.dirname(__file__), "afp", "afp_runner.js")
async def generate_fp(pcm_s16le: bytes) -> str:
"""生成音频指纹。
Args:
pcm_s16le: 8kHz 单声道 PCM s16le 字节
Returns:
base64 编码的指纹字符串
"""
b64_input = base64.b64encode(pcm_s16le).decode("ascii")
logger.info("[fp] spawn node, input b64=%d chars", len(b64_input))
proc = await asyncio.create_subprocess_exec(
"node", _RUNNER_PATH,
stdin=asyncio.subprocess.PIPE,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE,
)
stdout, stderr = await proc.communicate(input=b64_input.encode("ascii"))
if proc.returncode != 0:
err = stderr.decode("utf-8", errors="replace")[-500:]
raise RuntimeError(f"afp_runner exit {proc.returncode}: {err}")
# 把 stderr 日志输出到 logger
if stderr:
for line in stderr.decode("utf-8", errors="replace").splitlines():
logger.info("[fp] %s", line)
fp = stdout.decode("ascii").strip()
logger.info("[fp] generated, len=%d", len(fp))
return fp