File size: 1,532 Bytes
d731d8e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
"""调用 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