feat: 集成 XTC 听歌识曲模块(/xtc/* 路由,无侵入)
Browse files新增 src/router/xtc_recognize/ 模块:
- POST /xtc/recognize 上传录音(base64) -> 返回识别结果列表
- GET /xtc/song/url 获取试听 URL(30s 试听片段)
- GET /xtc/song/detail 获取歌曲详情
- GET /xtc/health 健康检查
实现:
- Python subprocess 调用 Node.js 运行 afp.wasm 生成音频指纹
- ffmpeg 转 8kHz 单声道 PCM
- 直连网易云 interface.music.163.com 识曲接口
Dockerfile 加装 nodejs + ffmpeg
web.py 用 try/except 包裹注册,失败不影响主服务
- Dockerfile +6 -2
- src/router/xtc_recognize/__init__.py +7 -0
- src/router/xtc_recognize/afp/afp.js +1626 -0
- src/router/xtc_recognize/afp/afp.wasm.js +0 -0
- src/router/xtc_recognize/afp/afp_runner.js +47 -0
- src/router/xtc_recognize/amr_decoder.py +58 -0
- src/router/xtc_recognize/fingerprint.py +48 -0
- src/router/xtc_recognize/ncm_proxy.py +117 -0
- src/router/xtc_recognize/recognize.py +74 -0
- src/router/xtc_recognize/router.py +181 -0
- web.py +8 -0
Dockerfile
CHANGED
|
@@ -9,9 +9,13 @@ ENV PYTHONUNBUFFERED=1 \
|
|
| 9 |
TZ=Asia/Shanghai
|
| 10 |
|
| 11 |
# Install tzdata and set timezone
|
|
|
|
| 12 |
RUN apt-get update && \
|
| 13 |
-
apt-get install -y --no-install-recommends
|
| 14 |
-
|
|
|
|
|
|
|
|
|
|
| 15 |
echo "Asia/Shanghai" > /etc/timezone && \
|
| 16 |
apt-get clean && \
|
| 17 |
rm -rf /var/lib/apt/lists/*
|
|
|
|
| 9 |
TZ=Asia/Shanghai
|
| 10 |
|
| 11 |
# Install tzdata and set timezone
|
| 12 |
+
# Added: nodejs (for afp.wasm 音频指纹生成), ffmpeg (音频转码)
|
| 13 |
RUN apt-get update && \
|
| 14 |
+
apt-get install -y --no-install-recommends \
|
| 15 |
+
tzdata \
|
| 16 |
+
nodejs \
|
| 17 |
+
ffmpeg \
|
| 18 |
+
&& ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \
|
| 19 |
echo "Asia/Shanghai" > /etc/timezone && \
|
| 20 |
apt-get clean && \
|
| 21 |
rm -rf /var/lib/apt/lists/*
|
src/router/xtc_recognize/__init__.py
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""XTC 听歌识曲模块 - 无侵入集成到 gcli2api。
|
| 2 |
+
|
| 3 |
+
提供路由:/xtc/recognize, /xtc/song/url, /xtc/song/detail, /xtc/health
|
| 4 |
+
"""
|
| 5 |
+
from .router import router
|
| 6 |
+
|
| 7 |
+
__all__ = ["router"]
|
src/router/xtc_recognize/afp/afp.js
ADDED
|
@@ -0,0 +1,1626 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
'use strict'
|
| 2 |
+
const WASM_BINARY_PLACEHOLDER = 'WASM_BINARY_PLACEHOLDER';
|
| 3 |
+
// See https://github.com/Distributive-Network/PythonMonkey/issues/266
|
| 4 |
+
if (typeof globalThis.setInterval != 'function'){
|
| 5 |
+
globalThis.setInterval = function pm$$setInterval(fn, timeout) {
|
| 6 |
+
const timerHnd = { cancel: false };
|
| 7 |
+
function fnWrapper()
|
| 8 |
+
{
|
| 9 |
+
if (timerHnd.cancel)
|
| 10 |
+
return;
|
| 11 |
+
setTimeout(fnWrapper, timeout);
|
| 12 |
+
fn();
|
| 13 |
+
}
|
| 14 |
+
timerHnd.id = setTimeout(fnWrapper, timeout);
|
| 15 |
+
return timerHnd;
|
| 16 |
+
}
|
| 17 |
+
globalThis.clearInterval = function pm$$clearInterval(timerHnd) {
|
| 18 |
+
timerHnd.clear = true;
|
| 19 |
+
clearTimeout(timerHnd.id);
|
| 20 |
+
}
|
| 21 |
+
}
|
| 22 |
+
globalThis.b64decode = function b64decode(data) {
|
| 23 |
+
return Uint8Array.from(atob(data), c => c.charCodeAt(0));
|
| 24 |
+
}
|
| 25 |
+
globalThis.b64encode = function b64encode(data) {
|
| 26 |
+
return btoa(String.fromCharCode(...data));
|
| 27 |
+
}
|
| 28 |
+
// https://fn.music.163.com/g/chrome-extension-home-page-beta/
|
| 29 |
+
let AudioFingerprintRuntime = (() => {
|
| 30 |
+
var n, o = void 0 !== o ? o : {},i = {};
|
| 31 |
+
for (n in o)
|
| 32 |
+
o.hasOwnProperty(n) && (i[n] = o[n]);
|
| 33 |
+
var read_, readSync, readBinary, c, f, l = [],
|
| 34 |
+
p = "./this.program",
|
| 35 |
+
readSync = function(t, e, r) {
|
| 36 |
+
switch (t) {
|
| 37 |
+
case WASM_BINARY_PLACEHOLDER:
|
| 38 |
+
if (typeof WASM_BINARY == 'undefined') {
|
| 39 |
+
const { WASM_BINARY } = require('./afp.wasm.js');
|
| 40 |
+
e(globalThis.b64decode(WASM_BINARY));
|
| 41 |
+
} else {
|
| 42 |
+
e(globalThis.b64decode(WASM_BINARY));
|
| 43 |
+
}
|
| 44 |
+
default:
|
| 45 |
+
throw "Reading " + t + " is not supported";
|
| 46 |
+
break;
|
| 47 |
+
}
|
| 48 |
+
}
|
| 49 |
+
var v = o.print || console.log.bind(console),
|
| 50 |
+
g = o.printErr || console.warn.bind(console);
|
| 51 |
+
for (n in i)
|
| 52 |
+
i.hasOwnProperty(n) && (o[n] = i[n]);
|
| 53 |
+
i = null,
|
| 54 |
+
o.arguments && (l = o.arguments),
|
| 55 |
+
o.thisProgram && (p = o.thisProgram),
|
| 56 |
+
o.quit && o.quit;
|
| 57 |
+
var w;
|
| 58 |
+
o.wasmBinary && (w = o.wasmBinary);
|
| 59 |
+
var b, _ = o.noExitRuntime || !0;
|
| 60 |
+
"object" != typeof WebAssembly && abort("no native wasm support detected");
|
| 61 |
+
var C = !1;
|
| 62 |
+
|
| 63 |
+
function T(t, e) {
|
| 64 |
+
t || abort("Assertion failed: " + e)
|
| 65 |
+
}
|
| 66 |
+
var UTF8Decoder = "undefined" != typeof TextDecoder ? new TextDecoder("utf8") : void 0;
|
| 67 |
+
|
| 68 |
+
function UTF8ArrayToString(heap, idx, maxBytesToRead) {
|
| 69 |
+
var endIdx = idx + maxBytesToRead;
|
| 70 |
+
var endPtr = idx;
|
| 71 |
+
while (heap[endPtr] && !(endPtr >= endIdx)) ++endPtr;
|
| 72 |
+
if (endPtr - idx > 16 && heap.subarray && UTF8Decoder) {
|
| 73 |
+
return UTF8Decoder.decode(heap.subarray(idx, endPtr))
|
| 74 |
+
} else {
|
| 75 |
+
var str = "";
|
| 76 |
+
while (idx < endPtr) {
|
| 77 |
+
var u0 = heap[idx++];
|
| 78 |
+
if (!(u0 & 128)) {
|
| 79 |
+
str += String.fromCharCode(u0);
|
| 80 |
+
continue
|
| 81 |
+
}
|
| 82 |
+
var u1 = heap[idx++] & 63;
|
| 83 |
+
if ((u0 & 224) == 192) {
|
| 84 |
+
str += String.fromCharCode((u0 & 31) << 6 | u1);
|
| 85 |
+
continue
|
| 86 |
+
}
|
| 87 |
+
var u2 = heap[idx++] & 63;
|
| 88 |
+
if ((u0 & 240) == 224) {
|
| 89 |
+
u0 = (u0 & 15) << 12 | u1 << 6 | u2
|
| 90 |
+
} else {
|
| 91 |
+
u0 = (u0 & 7) << 18 | u1 << 12 | u2 << 6 | heap[idx++] & 63
|
| 92 |
+
}
|
| 93 |
+
if (u0 < 65536) {
|
| 94 |
+
str += String.fromCharCode(u0)
|
| 95 |
+
} else {
|
| 96 |
+
var ch = u0 - 65536;
|
| 97 |
+
str += String.fromCharCode(55296 | ch >> 10, 56320 | ch & 1023)
|
| 98 |
+
}
|
| 99 |
+
}
|
| 100 |
+
}
|
| 101 |
+
return str
|
| 102 |
+
}
|
| 103 |
+
|
| 104 |
+
function UTF8ToString(ptr, maxBytesToRead) {
|
| 105 |
+
return ptr ? UTF8ArrayToString(HEAPU8, ptr, maxBytesToRead) : ""
|
| 106 |
+
}
|
| 107 |
+
function UTF8ToString(t, e) {
|
| 108 |
+
return t ? UTF8ArrayToString(O, t, e) : ""
|
| 109 |
+
}
|
| 110 |
+
|
| 111 |
+
|
| 112 |
+
function stringToUTF8Array(str, heap, outIdx, maxBytesToWrite) {
|
| 113 |
+
if (!(maxBytesToWrite > 0)) return 0;
|
| 114 |
+
var startIdx = outIdx;
|
| 115 |
+
var endIdx = outIdx + maxBytesToWrite - 1;
|
| 116 |
+
for (var i = 0; i < str.length; ++i) {
|
| 117 |
+
var u = str.charCodeAt(i);
|
| 118 |
+
if (u >= 55296 && u <= 57343) {
|
| 119 |
+
var u1 = str.charCodeAt(++i);
|
| 120 |
+
u = 65536 + ((u & 1023) << 10) | u1 & 1023
|
| 121 |
+
}
|
| 122 |
+
if (u <= 127) {
|
| 123 |
+
if (outIdx >= endIdx) break;
|
| 124 |
+
heap[outIdx++] = u
|
| 125 |
+
} else if (u <= 2047) {
|
| 126 |
+
if (outIdx + 1 >= endIdx) break;
|
| 127 |
+
heap[outIdx++] = 192 | u >> 6;
|
| 128 |
+
heap[outIdx++] = 128 | u & 63
|
| 129 |
+
} else if (u <= 65535) {
|
| 130 |
+
if (outIdx + 2 >= endIdx) break;
|
| 131 |
+
heap[outIdx++] = 224 | u >> 12;
|
| 132 |
+
heap[outIdx++] = 128 | u >> 6 & 63;
|
| 133 |
+
heap[outIdx++] = 128 | u & 63
|
| 134 |
+
} else {
|
| 135 |
+
if (outIdx + 3 >= endIdx) break;
|
| 136 |
+
heap[outIdx++] = 240 | u >> 18;
|
| 137 |
+
heap[outIdx++] = 128 | u >> 12 & 63;
|
| 138 |
+
heap[outIdx++] = 128 | u >> 6 & 63;
|
| 139 |
+
heap[outIdx++] = 128 | u & 63
|
| 140 |
+
}
|
| 141 |
+
}
|
| 142 |
+
heap[outIdx] = 0;
|
| 143 |
+
return outIdx - startIdx
|
| 144 |
+
}
|
| 145 |
+
|
| 146 |
+
|
| 147 |
+
function UTF8CharCount(t) {
|
| 148 |
+
for (var e = 0, r = 0; r < t.length; ++r) {
|
| 149 |
+
var n = t.charCodeAt(r);
|
| 150 |
+
n >= 55296 && n <= 57343 && (n = 65536 + ((1023 & n) << 10) | 1023 & t.charCodeAt(++r)),
|
| 151 |
+
n <= 127 ? ++e : e += n <= 2047 ? 2 : n <= 65535 ? 3 : 4
|
| 152 |
+
}
|
| 153 |
+
return e
|
| 154 |
+
}
|
| 155 |
+
var E, S, O, k, W, j, R, M, I, UTF16Decoder = "undefined" != typeof TextDecoder ? new TextDecoder("utf-16le") : void 0;
|
| 156 |
+
|
| 157 |
+
function UTF16ArrayToString(t, e) {
|
| 158 |
+
for (var r = t, n = r >> 1, o = n + e / 2; !(n >= o) && W[n];)
|
| 159 |
+
++n;
|
| 160 |
+
if ((r = n << 1) - t > 32 && UTF16Decoder)
|
| 161 |
+
return UTF16Decoder.decode(O.subarray(t, r));
|
| 162 |
+
for (var i = "", a = 0; !(a >= e / 2); ++a) {
|
| 163 |
+
var u = k[t + 2 * a >> 1];
|
| 164 |
+
if (0 == u)
|
| 165 |
+
break;
|
| 166 |
+
i += String.fromCharCode(u)
|
| 167 |
+
}
|
| 168 |
+
return i
|
| 169 |
+
}
|
| 170 |
+
|
| 171 |
+
function H(t, e, r) {
|
| 172 |
+
if (void 0 === r && (r = 2147483647),
|
| 173 |
+
r < 2)
|
| 174 |
+
return 0;
|
| 175 |
+
for (var n = e, o = (r -= 2) < 2 * t.length ? r / 2 : t.length, i = 0; i < o; ++i) {
|
| 176 |
+
var a = t.charCodeAt(i);
|
| 177 |
+
k[e >> 1] = a,
|
| 178 |
+
e += 2
|
| 179 |
+
}
|
| 180 |
+
return k[e >> 1] = 0,
|
| 181 |
+
e - n
|
| 182 |
+
}
|
| 183 |
+
|
| 184 |
+
function Y(t) {
|
| 185 |
+
return 2 * t.length
|
| 186 |
+
}
|
| 187 |
+
|
| 188 |
+
function V(t, e) {
|
| 189 |
+
for (var r = 0, n = ""; !(r >= e / 4);) {
|
| 190 |
+
var o = j[t + 4 * r >> 2];
|
| 191 |
+
if (0 == o)
|
| 192 |
+
break;
|
| 193 |
+
if (++r,
|
| 194 |
+
o >= 65536) {
|
| 195 |
+
var i = o - 65536;
|
| 196 |
+
n += String.fromCharCode(55296 | i >> 10, 56320 | 1023 & i)
|
| 197 |
+
} else
|
| 198 |
+
n += String.fromCharCode(o)
|
| 199 |
+
}
|
| 200 |
+
return n
|
| 201 |
+
}
|
| 202 |
+
|
| 203 |
+
function z(t, e, r) {
|
| 204 |
+
if (void 0 === r && (r = 2147483647),
|
| 205 |
+
r < 4)
|
| 206 |
+
return 0;
|
| 207 |
+
for (var n = e, o = n + r - 4, i = 0; i < t.length; ++i) {
|
| 208 |
+
var a = t.charCodeAt(i);
|
| 209 |
+
if (a >= 55296 && a <= 57343)
|
| 210 |
+
a = 65536 + ((1023 & a) << 10) | 1023 & t.charCodeAt(++i);
|
| 211 |
+
if (j[e >> 2] = a,
|
| 212 |
+
(e += 4) + 4 > o)
|
| 213 |
+
break
|
| 214 |
+
}
|
| 215 |
+
return j[e >> 2] = 0,
|
| 216 |
+
e - n
|
| 217 |
+
}
|
| 218 |
+
|
| 219 |
+
function B(t) {
|
| 220 |
+
for (var e = 0, r = 0; r < t.length; ++r) {
|
| 221 |
+
var n = t.charCodeAt(r);
|
| 222 |
+
n >= 55296 && n <= 57343 && ++r,
|
| 223 |
+
e += 4
|
| 224 |
+
}
|
| 225 |
+
return e
|
| 226 |
+
}
|
| 227 |
+
o.INITIAL_MEMORY;
|
| 228 |
+
var L, G = [],
|
| 229 |
+
N = [],
|
| 230 |
+
q = [];
|
| 231 |
+
var J = 0,
|
| 232 |
+
X = null,
|
| 233 |
+
Z = null;
|
| 234 |
+
|
| 235 |
+
function abort(what) {
|
| 236 |
+
throw o.onAbort && o.onAbort(what),
|
| 237 |
+
g(what = "Aborted(" + what + ")"),
|
| 238 |
+
C = !0,
|
| 239 |
+
1,
|
| 240 |
+
what += ". Build with -s ASSERTIONS=1 for more info.",
|
| 241 |
+
new WebAssembly.RuntimeError(what)
|
| 242 |
+
}
|
| 243 |
+
o.preloadedImages = {},
|
| 244 |
+
o.preloadedAudios = {};
|
| 245 |
+
var Q;
|
| 246 |
+
|
| 247 |
+
function isDataURI(t) {
|
| 248 |
+
return t.startsWith("data:application/octet-stream;base64,")
|
| 249 |
+
}
|
| 250 |
+
|
| 251 |
+
function isFileURI(t) {
|
| 252 |
+
return t.startsWith("file://")
|
| 253 |
+
}
|
| 254 |
+
|
| 255 |
+
function getBinary(file) {
|
| 256 |
+
try {
|
| 257 |
+
if (file == Q && w)
|
| 258 |
+
return new Uint8Array(w);
|
| 259 |
+
if (readBinary)
|
| 260 |
+
return readBinary(file);
|
| 261 |
+
throw "both async and sync fetching of the wasm failed"
|
| 262 |
+
} catch (t) {
|
| 263 |
+
abort(t)
|
| 264 |
+
}
|
| 265 |
+
}
|
| 266 |
+
|
| 267 |
+
function callRuntimeCallbacks(cb) {
|
| 268 |
+
for (; cb.length > 0;) {
|
| 269 |
+
var func = cb.shift();
|
| 270 |
+
if ("function" != typeof func) {
|
| 271 |
+
var r = func.func;
|
| 272 |
+
"number" == typeof r ? void 0 === func.arg ? it(r)() : it(r)(func.arg) : r(void 0 === func.arg ? null : func.arg)
|
| 273 |
+
} else
|
| 274 |
+
func(o)
|
| 275 |
+
}
|
| 276 |
+
}
|
| 277 |
+
var wasmBinaryFile = WASM_BINARY_PLACEHOLDER;
|
| 278 |
+
isDataURI(wasmBinaryFile) || (Q = function(t) {
|
| 279 |
+
return wasmBinaryFile
|
| 280 |
+
}(Q));
|
| 281 |
+
var ot = [];
|
| 282 |
+
|
| 283 |
+
function it(t) {
|
| 284 |
+
var e = ot[t];
|
| 285 |
+
return e || (t >= ot.length && (ot.length = t + 1),
|
| 286 |
+
ot[t] = e = L.get(t)),
|
| 287 |
+
e
|
| 288 |
+
}
|
| 289 |
+
|
| 290 |
+
function ExceptionInfo(excPtr) {
|
| 291 |
+
this.excPtr = excPtr,
|
| 292 |
+
this.ptr = excPtr - 16,
|
| 293 |
+
this.set_type = function(t) {
|
| 294 |
+
j[this.ptr + 4 >> 2] = t
|
| 295 |
+
},
|
| 296 |
+
this.get_type = function() {
|
| 297 |
+
return j[this.ptr + 4 >> 2]
|
| 298 |
+
},
|
| 299 |
+
this.set_destructor = function(t) {
|
| 300 |
+
j[this.ptr + 8 >> 2] = t
|
| 301 |
+
},
|
| 302 |
+
this.get_destructor = function() {
|
| 303 |
+
return j[this.ptr + 8 >> 2]
|
| 304 |
+
},
|
| 305 |
+
this.set_refcount = function(t) {
|
| 306 |
+
j[this.ptr >> 2] = t
|
| 307 |
+
},
|
| 308 |
+
this.set_caught = function(t) {
|
| 309 |
+
t = t ? 1 : 0,
|
| 310 |
+
S[this.ptr + 12 >> 0] = t
|
| 311 |
+
},
|
| 312 |
+
this.get_caught = function() {
|
| 313 |
+
return 0 != S[this.ptr + 12 >> 0]
|
| 314 |
+
},
|
| 315 |
+
this.set_rethrown = function(t) {
|
| 316 |
+
t = t ? 1 : 0,
|
| 317 |
+
S[this.ptr + 13 >> 0] = t
|
| 318 |
+
},
|
| 319 |
+
this.get_rethrown = function() {
|
| 320 |
+
return 0 != S[this.ptr + 13 >> 0]
|
| 321 |
+
},
|
| 322 |
+
this.init = function(t, e) {
|
| 323 |
+
this.set_type(t),
|
| 324 |
+
this.set_destructor(e),
|
| 325 |
+
this.set_refcount(0),
|
| 326 |
+
this.set_caught(!1),
|
| 327 |
+
this.set_rethrown(!1)
|
| 328 |
+
},
|
| 329 |
+
this.add_ref = function() {
|
| 330 |
+
var t = j[this.ptr >> 2];
|
| 331 |
+
j[this.ptr >> 2] = t + 1
|
| 332 |
+
},
|
| 333 |
+
this.release_ref = function() {
|
| 334 |
+
var t = j[this.ptr >> 2];
|
| 335 |
+
return j[this.ptr >> 2] = t - 1,
|
| 336 |
+
1 === t
|
| 337 |
+
}
|
| 338 |
+
}
|
| 339 |
+
|
| 340 |
+
function ut(t) {
|
| 341 |
+
switch (t) {
|
| 342 |
+
case 1:
|
| 343 |
+
return 0;
|
| 344 |
+
case 2:
|
| 345 |
+
return 1;
|
| 346 |
+
case 4:
|
| 347 |
+
return 2;
|
| 348 |
+
case 8:
|
| 349 |
+
return 3;
|
| 350 |
+
default:
|
| 351 |
+
throw new TypeError("Unknown type size: " + t)
|
| 352 |
+
}
|
| 353 |
+
}
|
| 354 |
+
var st = void 0;
|
| 355 |
+
|
| 356 |
+
function ct(t) {
|
| 357 |
+
for (var e = "", r = t; O[r];)
|
| 358 |
+
e += st[O[r++]];
|
| 359 |
+
return e
|
| 360 |
+
}
|
| 361 |
+
var ft = {},
|
| 362 |
+
lt = {},
|
| 363 |
+
pt = {};
|
| 364 |
+
|
| 365 |
+
function dt(t) {
|
| 366 |
+
if (void 0 === t)
|
| 367 |
+
return "_unknown";
|
| 368 |
+
var e = (t = t.replace(/[^a-zA-Z0-9_]/g, "$")).charCodeAt(0);
|
| 369 |
+
return e >= 48 && e <= 57 ? "_" + t : t
|
| 370 |
+
}
|
| 371 |
+
|
| 372 |
+
function ht(t, e) {
|
| 373 |
+
return t = dt(t),
|
| 374 |
+
new Function("body", "return function " + t + '() {\n "use strict"; return body.apply(this, arguments);\n};\n')(e)
|
| 375 |
+
}
|
| 376 |
+
|
| 377 |
+
function yt(t, e) {
|
| 378 |
+
var r = ht(e, (function(t) {
|
| 379 |
+
this.name = e,
|
| 380 |
+
this.message = t;
|
| 381 |
+
var r = new Error(t).stack;
|
| 382 |
+
void 0 !== r && (this.stack = this.toString() + "\n" + r.replace(/^Error(:[^\n]*)?\n/, ""))
|
| 383 |
+
}));
|
| 384 |
+
return r.prototype = Object.create(t.prototype),
|
| 385 |
+
r.prototype.constructor = r,
|
| 386 |
+
r.prototype.toString = function() {
|
| 387 |
+
return void 0 === this.message ? this.name : this.name + ": " + this.message
|
| 388 |
+
},
|
| 389 |
+
r
|
| 390 |
+
}
|
| 391 |
+
var mt = void 0;
|
| 392 |
+
|
| 393 |
+
function vt(t) {
|
| 394 |
+
throw new mt(t)
|
| 395 |
+
}
|
| 396 |
+
var gt = void 0;
|
| 397 |
+
|
| 398 |
+
function wt(t) {
|
| 399 |
+
throw new gt(t)
|
| 400 |
+
}
|
| 401 |
+
|
| 402 |
+
function bt(t, e, r) {
|
| 403 |
+
function n(e) {
|
| 404 |
+
var n = r(e);
|
| 405 |
+
n.length !== t.length && wt("Mismatched type converter count");
|
| 406 |
+
for (var o = 0; o < t.length; ++o)
|
| 407 |
+
_t(t[o], n[o])
|
| 408 |
+
}
|
| 409 |
+
t.forEach((function(t) {
|
| 410 |
+
pt[t] = e
|
| 411 |
+
}));
|
| 412 |
+
var o = new Array(e.length),
|
| 413 |
+
i = [],
|
| 414 |
+
a = 0;
|
| 415 |
+
e.forEach((function(t, e) {
|
| 416 |
+
lt.hasOwnProperty(t) ? o[e] = lt[t] : (i.push(t),
|
| 417 |
+
ft.hasOwnProperty(t) || (ft[t] = []),
|
| 418 |
+
ft[t].push((function() {
|
| 419 |
+
o[e] = lt[t],
|
| 420 |
+
++a === i.length && n(o)
|
| 421 |
+
})))
|
| 422 |
+
})),
|
| 423 |
+
0 === i.length && n(o)
|
| 424 |
+
}
|
| 425 |
+
|
| 426 |
+
function _t(t, e, r) {
|
| 427 |
+
if (r = r || {},
|
| 428 |
+
!("argPackAdvance" in e))
|
| 429 |
+
throw new TypeError("registerType registeredInstance requires argPackAdvance");
|
| 430 |
+
var n = e.name;
|
| 431 |
+
if (t || vt('type "' + n + '" must have a positive integer typeid pointer'),
|
| 432 |
+
lt.hasOwnProperty(t)) {
|
| 433 |
+
if (r.ignoreDuplicateRegistrations)
|
| 434 |
+
return;
|
| 435 |
+
vt("Cannot register type '" + n + "' twice")
|
| 436 |
+
}
|
| 437 |
+
if (lt[t] = e,
|
| 438 |
+
delete pt[t],
|
| 439 |
+
ft.hasOwnProperty(t)) {
|
| 440 |
+
var o = ft[t];
|
| 441 |
+
delete ft[t],
|
| 442 |
+
o.forEach((function(t) {
|
| 443 |
+
t()
|
| 444 |
+
}))
|
| 445 |
+
}
|
| 446 |
+
}
|
| 447 |
+
|
| 448 |
+
function Ct(t) {
|
| 449 |
+
if (!(this instanceof Rt))
|
| 450 |
+
return !1;
|
| 451 |
+
if (!(t instanceof Rt))
|
| 452 |
+
return !1;
|
| 453 |
+
for (var e = this.$$.ptrType.registeredClass, r = this.$$.ptr, n = t.$$.ptrType.registeredClass, o = t.$$.ptr; e.baseClass;)
|
| 454 |
+
r = e.upcast(r),
|
| 455 |
+
e = e.baseClass;
|
| 456 |
+
for (; n.baseClass;)
|
| 457 |
+
o = n.upcast(o),
|
| 458 |
+
n = n.baseClass;
|
| 459 |
+
return e === n && r === o
|
| 460 |
+
}
|
| 461 |
+
|
| 462 |
+
function Tt(t) {
|
| 463 |
+
vt(t.$$.ptrType.registeredClass.name + " instance already deleted")
|
| 464 |
+
}
|
| 465 |
+
var $t = !1;
|
| 466 |
+
|
| 467 |
+
function Pt(t) {}
|
| 468 |
+
|
| 469 |
+
function At(t) {
|
| 470 |
+
t.count.value -= 1,
|
| 471 |
+
0 === t.count.value && function(t) {
|
| 472 |
+
t.smartPtr ? t.smartPtrType.rawDestructor(t.smartPtr) : t.ptrType.registeredClass.rawDestructor(t.ptr)
|
| 473 |
+
}(t)
|
| 474 |
+
}
|
| 475 |
+
|
| 476 |
+
function Dt(t) {
|
| 477 |
+
return "undefined" == typeof FinalizationRegistry ? (Dt = function(t) {
|
| 478 |
+
return t
|
| 479 |
+
},
|
| 480 |
+
t) : ($t = new FinalizationRegistry((function(t) {
|
| 481 |
+
At(t.$$)
|
| 482 |
+
})),
|
| 483 |
+
Dt = function(t) {
|
| 484 |
+
var e = {
|
| 485 |
+
$$: t.$$
|
| 486 |
+
};
|
| 487 |
+
return $t.register(t, e, t),
|
| 488 |
+
t
|
| 489 |
+
},
|
| 490 |
+
Pt = function(t) {
|
| 491 |
+
$t.unregister(t)
|
| 492 |
+
},
|
| 493 |
+
Dt(t))
|
| 494 |
+
}
|
| 495 |
+
|
| 496 |
+
function Ft() {
|
| 497 |
+
if (this.$$.ptr || Tt(this),
|
| 498 |
+
this.$$.preservePointerOnDelete)
|
| 499 |
+
return this.$$.count.value += 1,
|
| 500 |
+
this;
|
| 501 |
+
var t, e = Dt(Object.create(Object.getPrototypeOf(this), {
|
| 502 |
+
$$: {
|
| 503 |
+
value: (t = this.$$, {
|
| 504 |
+
count: t.count,
|
| 505 |
+
deleteScheduled: t.deleteScheduled,
|
| 506 |
+
preservePointerOnDelete: t.preservePointerOnDelete,
|
| 507 |
+
ptr: t.ptr,
|
| 508 |
+
ptrType: t.ptrType,
|
| 509 |
+
smartPtr: t.smartPtr,
|
| 510 |
+
smartPtrType: t.smartPtrType
|
| 511 |
+
})
|
| 512 |
+
}
|
| 513 |
+
}));
|
| 514 |
+
return e.$$.count.value += 1,
|
| 515 |
+
e.$$.deleteScheduled = !1,
|
| 516 |
+
e
|
| 517 |
+
}
|
| 518 |
+
|
| 519 |
+
function Et() {
|
| 520 |
+
this.$$.ptr || Tt(this),
|
| 521 |
+
this.$$.deleteScheduled && !this.$$.preservePointerOnDelete && vt("Object already scheduled for deletion"),
|
| 522 |
+
Pt(this),
|
| 523 |
+
At(this.$$),
|
| 524 |
+
this.$$.preservePointerOnDelete || (this.$$.smartPtr = void 0,
|
| 525 |
+
this.$$.ptr = void 0)
|
| 526 |
+
}
|
| 527 |
+
|
| 528 |
+
function St() {
|
| 529 |
+
return !this.$$.ptr
|
| 530 |
+
}
|
| 531 |
+
var Ot = void 0,
|
| 532 |
+
kt = [];
|
| 533 |
+
|
| 534 |
+
function Wt() {
|
| 535 |
+
for (; kt.length;) {
|
| 536 |
+
var t = kt.pop();
|
| 537 |
+
t.$$.deleteScheduled = !1,
|
| 538 |
+
t.delete()
|
| 539 |
+
}
|
| 540 |
+
}
|
| 541 |
+
|
| 542 |
+
function jt() {
|
| 543 |
+
return this.$$.ptr || Tt(this),
|
| 544 |
+
this.$$.deleteScheduled && !this.$$.preservePointerOnDelete && vt("Object already scheduled for deletion"),
|
| 545 |
+
kt.push(this),
|
| 546 |
+
1 === kt.length && Ot && Ot(Wt),
|
| 547 |
+
this.$$.deleteScheduled = !0,
|
| 548 |
+
this
|
| 549 |
+
}
|
| 550 |
+
|
| 551 |
+
function Rt() {}
|
| 552 |
+
var Mt = {};
|
| 553 |
+
|
| 554 |
+
function It(t, e, r) {
|
| 555 |
+
if (void 0 === t[e].overloadTable) {
|
| 556 |
+
var n = t[e];
|
| 557 |
+
t[e] = function() {
|
| 558 |
+
return t[e].overloadTable.hasOwnProperty(arguments.length) || vt("Function '" + r + "' called with an invalid number of arguments (" + arguments.length + ") - expects one of (" + t[e].overloadTable + ")!"),
|
| 559 |
+
t[e].overloadTable[arguments.length].apply(this, arguments)
|
| 560 |
+
},
|
| 561 |
+
t[e].overloadTable = [],
|
| 562 |
+
t[e].overloadTable[n.argCount] = n
|
| 563 |
+
}
|
| 564 |
+
}
|
| 565 |
+
|
| 566 |
+
function xt(t, e, r) {
|
| 567 |
+
o.hasOwnProperty(t) ? ((void 0 === r || void 0 !== o[t].overloadTable && void 0 !== o[t].overloadTable[r]) && vt("Cannot register public name '" + t + "' twice"),
|
| 568 |
+
It(o, t, t),
|
| 569 |
+
o.hasOwnProperty(r) && vt("Cannot register multiple overloads of a function with the same number of arguments (" + r + ")!"),
|
| 570 |
+
o[t].overloadTable[r] = e) : (o[t] = e,
|
| 571 |
+
void 0 !== r && (o[t].numArguments = r))
|
| 572 |
+
}
|
| 573 |
+
|
| 574 |
+
function Ut(t, e, r, n, o, i, a, u) {
|
| 575 |
+
this.name = t,
|
| 576 |
+
this.constructor = e,
|
| 577 |
+
this.instancePrototype = r,
|
| 578 |
+
this.rawDestructor = n,
|
| 579 |
+
this.baseClass = o,
|
| 580 |
+
this.getActualType = i,
|
| 581 |
+
this.upcast = a,
|
| 582 |
+
this.downcast = u,
|
| 583 |
+
this.pureVirtualFunctions = []
|
| 584 |
+
}
|
| 585 |
+
|
| 586 |
+
function Ht(t, e, r) {
|
| 587 |
+
for (; e !== r;)
|
| 588 |
+
e.upcast || vt("Expected null or instance of " + r.name + ", got an instance of " + e.name),
|
| 589 |
+
t = e.upcast(t),
|
| 590 |
+
e = e.baseClass;
|
| 591 |
+
return t
|
| 592 |
+
}
|
| 593 |
+
|
| 594 |
+
function Yt(t, e) {
|
| 595 |
+
if (null === e)
|
| 596 |
+
return this.isReference && vt("null is not a valid " + this.name),
|
| 597 |
+
0;
|
| 598 |
+
e.$$ || vt('Cannot pass "' + ge(e) + '" as a ' + this.name),
|
| 599 |
+
e.$$.ptr || vt("Cannot pass deleted object as a pointer of type " + this.name);
|
| 600 |
+
var r = e.$$.ptrType.registeredClass;
|
| 601 |
+
return Ht(e.$$.ptr, r, this.registeredClass)
|
| 602 |
+
}
|
| 603 |
+
|
| 604 |
+
function Vt(t, e) {
|
| 605 |
+
var r;
|
| 606 |
+
if (null === e)
|
| 607 |
+
return this.isReference && vt("null is not a valid " + this.name),
|
| 608 |
+
this.isSmartPointer ? (r = this.rawConstructor(),
|
| 609 |
+
null !== t && t.push(this.rawDestructor, r),
|
| 610 |
+
r) : 0;
|
| 611 |
+
e.$$ || vt('Cannot pass "' + ge(e) + '" as a ' + this.name),
|
| 612 |
+
e.$$.ptr || vt("Cannot pass deleted object as a pointer of type " + this.name),
|
| 613 |
+
!this.isConst && e.$$.ptrType.isConst && vt("Cannot convert argument of type " + (e.$$.smartPtrType ? e.$$.smartPtrType.name : e.$$.ptrType.name) + " to parameter type " + this.name);
|
| 614 |
+
var n = e.$$.ptrType.registeredClass;
|
| 615 |
+
if (r = Ht(e.$$.ptr, n, this.registeredClass),
|
| 616 |
+
this.isSmartPointer)
|
| 617 |
+
switch (void 0 === e.$$.smartPtr && vt("Passing raw pointer to smart pointer is illegal"),
|
| 618 |
+
this.sharingPolicy) {
|
| 619 |
+
case 0:
|
| 620 |
+
e.$$.smartPtrType === this ? r = e.$$.smartPtr : vt("Cannot convert argument of type " + (e.$$.smartPtrType ? e.$$.smartPtrType.name : e.$$.ptrType.name) + " to parameter type " + this.name);
|
| 621 |
+
break;
|
| 622 |
+
case 1:
|
| 623 |
+
r = e.$$.smartPtr;
|
| 624 |
+
break;
|
| 625 |
+
case 2:
|
| 626 |
+
if (e.$$.smartPtrType === this)
|
| 627 |
+
r = e.$$.smartPtr;
|
| 628 |
+
else {
|
| 629 |
+
var o = e.clone();
|
| 630 |
+
r = this.rawShare(r, ve.toHandle((function() {
|
| 631 |
+
o.delete()
|
| 632 |
+
}))),
|
| 633 |
+
null !== t && t.push(this.rawDestructor, r)
|
| 634 |
+
}
|
| 635 |
+
break;
|
| 636 |
+
default:
|
| 637 |
+
vt("Unsupporting sharing policy")
|
| 638 |
+
}
|
| 639 |
+
return r
|
| 640 |
+
}
|
| 641 |
+
|
| 642 |
+
function zt(t, e) {
|
| 643 |
+
if (null === e)
|
| 644 |
+
return this.isReference && vt("null is not a valid " + this.name),
|
| 645 |
+
0;
|
| 646 |
+
e.$$ || vt('Cannot pass "' + ge(e) + '" as a ' + this.name),
|
| 647 |
+
e.$$.ptr || vt("Cannot pass deleted object as a pointer of type " + this.name),
|
| 648 |
+
e.$$.ptrType.isConst && vt("Cannot convert argument of type " + e.$$.ptrType.name + " to parameter type " + this.name);
|
| 649 |
+
var r = e.$$.ptrType.registeredClass;
|
| 650 |
+
return Ht(e.$$.ptr, r, this.registeredClass)
|
| 651 |
+
}
|
| 652 |
+
|
| 653 |
+
function Bt(t) {
|
| 654 |
+
return this.fromWireType(R[t >> 2])
|
| 655 |
+
}
|
| 656 |
+
|
| 657 |
+
function Lt(t) {
|
| 658 |
+
return this.rawGetPointee && (t = this.rawGetPointee(t)),
|
| 659 |
+
t
|
| 660 |
+
}
|
| 661 |
+
|
| 662 |
+
function Gt(t) {
|
| 663 |
+
this.rawDestructor && this.rawDestructor(t)
|
| 664 |
+
}
|
| 665 |
+
|
| 666 |
+
function Nt(t) {
|
| 667 |
+
null !== t && t.delete()
|
| 668 |
+
}
|
| 669 |
+
|
| 670 |
+
function qt(t, e, r) {
|
| 671 |
+
if (e === r)
|
| 672 |
+
return t;
|
| 673 |
+
if (void 0 === r.baseClass)
|
| 674 |
+
return null;
|
| 675 |
+
var n = qt(t, e, r.baseClass);
|
| 676 |
+
return null === n ? null : r.downcast(n)
|
| 677 |
+
}
|
| 678 |
+
|
| 679 |
+
function Jt() {
|
| 680 |
+
return Object.keys(Kt).length
|
| 681 |
+
}
|
| 682 |
+
|
| 683 |
+
function Xt() {
|
| 684 |
+
var t = [];
|
| 685 |
+
for (var e in Kt)
|
| 686 |
+
Kt.hasOwnProperty(e) && t.push(Kt[e]);
|
| 687 |
+
return t
|
| 688 |
+
}
|
| 689 |
+
|
| 690 |
+
function Zt(t) {
|
| 691 |
+
Ot = t,
|
| 692 |
+
kt.length && Ot && Ot(Wt)
|
| 693 |
+
}
|
| 694 |
+
var Kt = {};
|
| 695 |
+
|
| 696 |
+
function Qt(t, e) {
|
| 697 |
+
return e = function(t, e) {
|
| 698 |
+
for (void 0 === e && vt("ptr should not be undefined"); t.baseClass;)
|
| 699 |
+
e = t.upcast(e),
|
| 700 |
+
t = t.baseClass;
|
| 701 |
+
return e
|
| 702 |
+
}(t, e),
|
| 703 |
+
Kt[e]
|
| 704 |
+
}
|
| 705 |
+
|
| 706 |
+
function te(t, e) {
|
| 707 |
+
return e.ptrType && e.ptr || wt("makeClassHandle requires ptr and ptrType"),
|
| 708 |
+
!!e.smartPtrType !== !!e.smartPtr && wt("Both smartPtrType and smartPtr must be specified"),
|
| 709 |
+
e.count = {
|
| 710 |
+
value: 1
|
| 711 |
+
},
|
| 712 |
+
Dt(Object.create(t, {
|
| 713 |
+
$$: {
|
| 714 |
+
value: e
|
| 715 |
+
}
|
| 716 |
+
}))
|
| 717 |
+
}
|
| 718 |
+
|
| 719 |
+
function ee(t) {
|
| 720 |
+
var e = this.getPointee(t);
|
| 721 |
+
if (!e)
|
| 722 |
+
return this.destructor(t),
|
| 723 |
+
null;
|
| 724 |
+
var r = Qt(this.registeredClass, e);
|
| 725 |
+
if (void 0 !== r) {
|
| 726 |
+
if (0 === r.$$.count.value)
|
| 727 |
+
return r.$$.ptr = e,
|
| 728 |
+
r.$$.smartPtr = t,
|
| 729 |
+
r.clone();
|
| 730 |
+
var n = r.clone();
|
| 731 |
+
return this.destructor(t),
|
| 732 |
+
n
|
| 733 |
+
}
|
| 734 |
+
|
| 735 |
+
function o() {
|
| 736 |
+
return this.isSmartPointer ? te(this.registeredClass.instancePrototype, {
|
| 737 |
+
ptrType: this.pointeeType,
|
| 738 |
+
ptr: e,
|
| 739 |
+
smartPtrType: this,
|
| 740 |
+
smartPtr: t
|
| 741 |
+
}) : te(this.registeredClass.instancePrototype, {
|
| 742 |
+
ptrType: this,
|
| 743 |
+
ptr: t
|
| 744 |
+
})
|
| 745 |
+
}
|
| 746 |
+
var i, a = this.registeredClass.getActualType(e),
|
| 747 |
+
u = Mt[a];
|
| 748 |
+
if (!u)
|
| 749 |
+
return o.call(this);
|
| 750 |
+
i = this.isConst ? u.constPointerType : u.pointerType;
|
| 751 |
+
var s = qt(e, this.registeredClass, i.registeredClass);
|
| 752 |
+
return null === s ? o.call(this) : this.isSmartPointer ? te(i.registeredClass.instancePrototype, {
|
| 753 |
+
ptrType: i,
|
| 754 |
+
ptr: s,
|
| 755 |
+
smartPtrType: this,
|
| 756 |
+
smartPtr: t
|
| 757 |
+
}) : te(i.registeredClass.instancePrototype, {
|
| 758 |
+
ptrType: i,
|
| 759 |
+
ptr: s
|
| 760 |
+
})
|
| 761 |
+
}
|
| 762 |
+
|
| 763 |
+
function re(t, e, r, n, o, i, a, u, s, c, f) {
|
| 764 |
+
this.name = t,
|
| 765 |
+
this.registeredClass = e,
|
| 766 |
+
this.isReference = r,
|
| 767 |
+
this.isConst = n,
|
| 768 |
+
this.isSmartPointer = o,
|
| 769 |
+
this.pointeeType = i,
|
| 770 |
+
this.sharingPolicy = a,
|
| 771 |
+
this.rawGetPointee = u,
|
| 772 |
+
this.rawConstructor = s,
|
| 773 |
+
this.rawShare = c,
|
| 774 |
+
this.rawDestructor = f,
|
| 775 |
+
o || void 0 !== e.baseClass ? this.toWireType = Vt : n ? (this.toWireType = Yt,
|
| 776 |
+
this.destructorFunction = null) : (this.toWireType = zt,
|
| 777 |
+
this.destructorFunction = null)
|
| 778 |
+
}
|
| 779 |
+
|
| 780 |
+
function ne(t, e, r) {
|
| 781 |
+
o.hasOwnProperty(t) || wt("Replacing nonexistant public symbol"),
|
| 782 |
+
void 0 !== o[t].overloadTable && void 0 !== r ? o[t].overloadTable[r] = e : (o[t] = e,
|
| 783 |
+
o[t].argCount = r)
|
| 784 |
+
}
|
| 785 |
+
|
| 786 |
+
function oe(t, e, r) {
|
| 787 |
+
return t.includes("j") ? function(t, e, r) {
|
| 788 |
+
var n = o["dynCall_" + t];
|
| 789 |
+
return r && r.length ? n.apply(null, [e].concat(r)) : n.call(null, e)
|
| 790 |
+
}(t, e, r) : it(e).apply(null, r)
|
| 791 |
+
}
|
| 792 |
+
|
| 793 |
+
function ie(t, e) {
|
| 794 |
+
var r, n, o, i = (t = ct(t)).includes("j") ? (r = t,
|
| 795 |
+
n = e,
|
| 796 |
+
o = [],
|
| 797 |
+
function() {
|
| 798 |
+
o.length = arguments.length;
|
| 799 |
+
for (var t = 0; t < arguments.length; t++)
|
| 800 |
+
o[t] = arguments[t];
|
| 801 |
+
return oe(r, n, o)
|
| 802 |
+
}
|
| 803 |
+
) : it(e);
|
| 804 |
+
return "function" != typeof i && vt("unknown function pointer with signature " + t + ": " + e),
|
| 805 |
+
i
|
| 806 |
+
}
|
| 807 |
+
var ae = void 0;
|
| 808 |
+
|
| 809 |
+
function ue(t) {
|
| 810 |
+
var e = je(t),
|
| 811 |
+
r = ct(e);
|
| 812 |
+
return We(e),
|
| 813 |
+
r
|
| 814 |
+
}
|
| 815 |
+
|
| 816 |
+
function se(t, e) {
|
| 817 |
+
var r = [],
|
| 818 |
+
n = {};
|
| 819 |
+
throw e.forEach((function t(e) {
|
| 820 |
+
n[e] || lt[e] || (pt[e] ? pt[e].forEach(t) : (r.push(e),
|
| 821 |
+
n[e] = !0))
|
| 822 |
+
})),
|
| 823 |
+
new ae(t + ": " + r.map(ue).join([", "]))
|
| 824 |
+
}
|
| 825 |
+
|
| 826 |
+
function ce(t, e) {
|
| 827 |
+
for (var r = [], n = 0; n < t; n++)
|
| 828 |
+
r.push(j[(e >> 2) + n]);
|
| 829 |
+
return r
|
| 830 |
+
}
|
| 831 |
+
|
| 832 |
+
function fe(t) {
|
| 833 |
+
for (; t.length;) {
|
| 834 |
+
var e = t.pop();
|
| 835 |
+
t.pop()(e)
|
| 836 |
+
}
|
| 837 |
+
}
|
| 838 |
+
|
| 839 |
+
function le(t, e, r, n, o) {
|
| 840 |
+
var i = e.length;
|
| 841 |
+
i < 2 && vt("argTypes array size mismatch! Must at least get return value and 'this' types!");
|
| 842 |
+
for (var a = null !== e[1] && null !== r, u = !1, s = 1; s < e.length; ++s)
|
| 843 |
+
if (null !== e[s] && void 0 === e[s].destructorFunction) {
|
| 844 |
+
u = !0;
|
| 845 |
+
break
|
| 846 |
+
}
|
| 847 |
+
var c = "void" !== e[0].name,
|
| 848 |
+
f = "",
|
| 849 |
+
l = "";
|
| 850 |
+
for (s = 0; s < i - 2; ++s)
|
| 851 |
+
f += (0 !== s ? ", " : "") + "arg" + s,
|
| 852 |
+
l += (0 !== s ? ", " : "") + "arg" + s + "Wired";
|
| 853 |
+
var p = "return function " + dt(t) + "(" + f + ") {\nif (arguments.length !== " + (i - 2) + ") {\nthrowBindingError('function " + t + " called with ' + arguments.length + ' arguments, expected " + (i - 2) + " args!');\n}\n";
|
| 854 |
+
u && (p += "var destructors = [];\n");
|
| 855 |
+
var d = u ? "destructors" : "null",
|
| 856 |
+
h = ["throwBindingError", "invoker", "fn", "runDestructors", "retType", "classParam"],
|
| 857 |
+
y = [vt, n, o, fe, e[0], e[1]];
|
| 858 |
+
a && (p += "var thisWired = classParam.toWireType(" + d + ", this);\n");
|
| 859 |
+
for (s = 0; s < i - 2; ++s)
|
| 860 |
+
p += "var arg" + s + "Wired = argType" + s + ".toWireType(" + d + ", arg" + s + "); // " + e[s + 2].name + "\n",
|
| 861 |
+
h.push("argType" + s),
|
| 862 |
+
y.push(e[s + 2]);
|
| 863 |
+
if (a && (l = "thisWired" + (l.length > 0 ? ", " : "") + l),
|
| 864 |
+
p += (c ? "var rv = " : "") + "invoker(fn" + (l.length > 0 ? ", " : "") + l + ");\n",
|
| 865 |
+
u)
|
| 866 |
+
p += "runDestructors(destructors);\n";
|
| 867 |
+
else
|
| 868 |
+
for (s = a ? 1 : 2; s < e.length; ++s) {
|
| 869 |
+
var m = 1 === s ? "thisWired" : "arg" + (s - 2) + "Wired";
|
| 870 |
+
null !== e[s].destructorFunction && (p += m + "_dtor(" + m + "); // " + e[s].name + "\n",
|
| 871 |
+
h.push(m + "_dtor"),
|
| 872 |
+
y.push(e[s].destructorFunction))
|
| 873 |
+
}
|
| 874 |
+
return c && (p += "var ret = retType.fromWireType(rv);\nreturn ret;\n"),
|
| 875 |
+
p += "}\n",
|
| 876 |
+
h.push(p),
|
| 877 |
+
function(t, e) {
|
| 878 |
+
if (!(t instanceof Function))
|
| 879 |
+
throw new TypeError("new_ called with constructor type " + typeof t + " which is not a function");
|
| 880 |
+
var r = ht(t.name || "unknownFunctionName", (function() {}));
|
| 881 |
+
r.prototype = t.prototype;
|
| 882 |
+
var n = new r,
|
| 883 |
+
o = t.apply(n, e);
|
| 884 |
+
return o instanceof Object ? o : n
|
| 885 |
+
}(Function, h).apply(null, y)
|
| 886 |
+
}
|
| 887 |
+
var pe = [],
|
| 888 |
+
de = [{}, {
|
| 889 |
+
value: void 0
|
| 890 |
+
}, {
|
| 891 |
+
value: null
|
| 892 |
+
}, {
|
| 893 |
+
value: !0
|
| 894 |
+
}, {
|
| 895 |
+
value: !1
|
| 896 |
+
}];
|
| 897 |
+
|
| 898 |
+
function he(t) {
|
| 899 |
+
t > 4 && 0 == --de[t].refcount && (de[t] = void 0,
|
| 900 |
+
pe.push(t))
|
| 901 |
+
}
|
| 902 |
+
|
| 903 |
+
function ye() {
|
| 904 |
+
for (var t = 0, e = 5; e < de.length; ++e)
|
| 905 |
+
void 0 !== de[e] && ++t;
|
| 906 |
+
return t
|
| 907 |
+
}
|
| 908 |
+
|
| 909 |
+
function me() {
|
| 910 |
+
for (var t = 5; t < de.length; ++t)
|
| 911 |
+
if (void 0 !== de[t])
|
| 912 |
+
return de[t];
|
| 913 |
+
return null
|
| 914 |
+
}
|
| 915 |
+
var ve = {
|
| 916 |
+
toValue: function(t) {
|
| 917 |
+
return t || vt("Cannot use deleted val. handle = " + t),
|
| 918 |
+
de[t].value
|
| 919 |
+
},
|
| 920 |
+
toHandle: function(t) {
|
| 921 |
+
switch (t) {
|
| 922 |
+
case void 0:
|
| 923 |
+
return 1;
|
| 924 |
+
case null:
|
| 925 |
+
return 2;
|
| 926 |
+
case !0:
|
| 927 |
+
return 3;
|
| 928 |
+
case !1:
|
| 929 |
+
return 4;
|
| 930 |
+
default:
|
| 931 |
+
var e = pe.length ? pe.pop() : de.length;
|
| 932 |
+
return de[e] = {
|
| 933 |
+
refcount: 1,
|
| 934 |
+
value: t
|
| 935 |
+
},
|
| 936 |
+
e
|
| 937 |
+
}
|
| 938 |
+
}
|
| 939 |
+
};
|
| 940 |
+
|
| 941 |
+
function ge(t) {
|
| 942 |
+
if (null === t)
|
| 943 |
+
return "null";
|
| 944 |
+
var e = typeof t;
|
| 945 |
+
return "object" === e || "array" === e || "function" === e ? t.toString() : "" + t
|
| 946 |
+
}
|
| 947 |
+
|
| 948 |
+
function we(t, e) {
|
| 949 |
+
switch (e) {
|
| 950 |
+
case 2:
|
| 951 |
+
return function(t) {
|
| 952 |
+
return this.fromWireType(M[t >> 2])
|
| 953 |
+
};
|
| 954 |
+
case 3:
|
| 955 |
+
return function(t) {
|
| 956 |
+
return this.fromWireType(I[t >> 3])
|
| 957 |
+
};
|
| 958 |
+
default:
|
| 959 |
+
throw new TypeError("Unknown float type: " + t)
|
| 960 |
+
}
|
| 961 |
+
}
|
| 962 |
+
|
| 963 |
+
function be(t, e, r) {
|
| 964 |
+
switch (e) {
|
| 965 |
+
case 0:
|
| 966 |
+
return r ? function(t) {
|
| 967 |
+
return S[t]
|
| 968 |
+
} :
|
| 969 |
+
function(t) {
|
| 970 |
+
return O[t]
|
| 971 |
+
};
|
| 972 |
+
case 1:
|
| 973 |
+
return r ? function(t) {
|
| 974 |
+
return k[t >> 1]
|
| 975 |
+
} :
|
| 976 |
+
function(t) {
|
| 977 |
+
return W[t >> 1]
|
| 978 |
+
};
|
| 979 |
+
case 2:
|
| 980 |
+
return r ? function(t) {
|
| 981 |
+
return j[t >> 2]
|
| 982 |
+
} :
|
| 983 |
+
function(t) {
|
| 984 |
+
return R[t >> 2]
|
| 985 |
+
};
|
| 986 |
+
default:
|
| 987 |
+
throw new TypeError("Unknown integer type: " + t)
|
| 988 |
+
}
|
| 989 |
+
}
|
| 990 |
+
var Te = {
|
| 991 |
+
mappings: {},
|
| 992 |
+
buffers: [null, [],
|
| 993 |
+
[]
|
| 994 |
+
],
|
| 995 |
+
printChar: function(t, e) {
|
| 996 |
+
var r = Te.buffers[t];
|
| 997 |
+
0 === e || 10 === e ? ((1 === t ? v : g)(UTF8ArrayToString(r, 0)),
|
| 998 |
+
r.length = 0) : r.push(e)
|
| 999 |
+
},
|
| 1000 |
+
varargs: void 0,
|
| 1001 |
+
get: function() {
|
| 1002 |
+
return Te.varargs += 4,
|
| 1003 |
+
j[Te.varargs - 4 >> 2]
|
| 1004 |
+
},
|
| 1005 |
+
getStr: function(t) {
|
| 1006 |
+
return UTF8ToString(t)
|
| 1007 |
+
},
|
| 1008 |
+
get64: function(t, e) {
|
| 1009 |
+
return t
|
| 1010 |
+
}
|
| 1011 |
+
};
|
| 1012 |
+
|
| 1013 |
+
function $e(t) {
|
| 1014 |
+
return t % 4 == 0 && (t % 100 != 0 || t % 400 == 0)
|
| 1015 |
+
}
|
| 1016 |
+
|
| 1017 |
+
function Pe(t, e) {
|
| 1018 |
+
for (var r = 0, n = 0; n <= e; r += t[n++])
|
| 1019 |
+
;
|
| 1020 |
+
return r
|
| 1021 |
+
}
|
| 1022 |
+
var Ae = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
|
| 1023 |
+
De = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
|
| 1024 |
+
|
| 1025 |
+
function Fe(t, e) {
|
| 1026 |
+
for (var r = new Date(t.getTime()); e > 0;) {
|
| 1027 |
+
var n = $e(r.getFullYear()),
|
| 1028 |
+
o = r.getMonth(),
|
| 1029 |
+
i = (n ? Ae : De)[o];
|
| 1030 |
+
if (!(e > i - r.getDate()))
|
| 1031 |
+
return r.setDate(r.getDate() + e),
|
| 1032 |
+
r;
|
| 1033 |
+
e -= i - r.getDate() + 1,
|
| 1034 |
+
r.setDate(1),
|
| 1035 |
+
o < 11 ? r.setMonth(o + 1) : (r.setMonth(0),
|
| 1036 |
+
r.setFullYear(r.getFullYear() + 1))
|
| 1037 |
+
}
|
| 1038 |
+
return r
|
| 1039 |
+
}
|
| 1040 |
+
|
| 1041 |
+
for (var t = new Array(256), e = 0; e < 256; ++e)
|
| 1042 |
+
t[e] = String.fromCharCode(e);
|
| 1043 |
+
st = t
|
| 1044 |
+
|
| 1045 |
+
mt = o.BindingError = yt(Error, "BindingError"),
|
| 1046 |
+
gt = o.InternalError = yt(Error, "InternalError"),
|
| 1047 |
+
Rt.prototype.isAliasOf = Ct,
|
| 1048 |
+
Rt.prototype.clone = Ft,
|
| 1049 |
+
Rt.prototype.delete = Et,
|
| 1050 |
+
Rt.prototype.isDeleted = St,
|
| 1051 |
+
Rt.prototype.deleteLater = jt,
|
| 1052 |
+
re.prototype.getPointee = Lt,
|
| 1053 |
+
re.prototype.destructor = Gt,
|
| 1054 |
+
re.prototype.argPackAdvance = 8,
|
| 1055 |
+
re.prototype.readValueFromPointer = Bt,
|
| 1056 |
+
re.prototype.deleteObject = Nt,
|
| 1057 |
+
re.prototype.fromWireType = ee,
|
| 1058 |
+
o.getInheritedInstanceCount = Jt,
|
| 1059 |
+
o.getLiveInheritedInstances = Xt,
|
| 1060 |
+
o.flushPendingDeletes = Wt,
|
| 1061 |
+
o.setDelayFunction = Zt,
|
| 1062 |
+
ae = o.UnboundTypeError = yt(Error, "UnboundTypeError"),
|
| 1063 |
+
o.count_emval_handles = ye,
|
| 1064 |
+
o.get_first_emval = me;
|
| 1065 |
+
var Se, import_table_impl = {
|
| 1066 |
+
d: function(t, e, r, n) {
|
| 1067 |
+
abort("Assertion failed: " + UTF8ToString(t) + ", at: " + [e ? UTF8ToString(e) : "unknown filename", r, n ? UTF8ToString(n) : "unknown function"])
|
| 1068 |
+
},
|
| 1069 |
+
g: function(t) {
|
| 1070 |
+
return ke(t + 16) + 16
|
| 1071 |
+
},
|
| 1072 |
+
f: function(t, e, r) {
|
| 1073 |
+
throw new ExceptionInfo(t).init(e, r),t,t
|
| 1074 |
+
},
|
| 1075 |
+
p: function(t, e, r, n, o) {},
|
| 1076 |
+
y: function(t, e, r, n, o) {
|
| 1077 |
+
var i = ut(r);
|
| 1078 |
+
_t(t, {
|
| 1079 |
+
name: e = ct(e),
|
| 1080 |
+
fromWireType: function(t) {
|
| 1081 |
+
return !!t
|
| 1082 |
+
},
|
| 1083 |
+
toWireType: function(t, e) {
|
| 1084 |
+
return e ? n : o
|
| 1085 |
+
},
|
| 1086 |
+
argPackAdvance: 8,
|
| 1087 |
+
readValueFromPointer: function(t) {
|
| 1088 |
+
var n;
|
| 1089 |
+
if (1 === r)
|
| 1090 |
+
n = S;
|
| 1091 |
+
else if (2 === r)
|
| 1092 |
+
n = k;
|
| 1093 |
+
else {
|
| 1094 |
+
if (4 !== r)
|
| 1095 |
+
throw new TypeError("Unknown boolean type size: " + e);
|
| 1096 |
+
n = j
|
| 1097 |
+
}
|
| 1098 |
+
return this.fromWireType(n[t >> i])
|
| 1099 |
+
},
|
| 1100 |
+
destructorFunction: null
|
| 1101 |
+
})
|
| 1102 |
+
},
|
| 1103 |
+
A: function(t, e, r, n, o, i, a, u, s, c, f, l, p) {
|
| 1104 |
+
f = ct(f),
|
| 1105 |
+
i = ie(o, i),
|
| 1106 |
+
u && (u = ie(a, u)),
|
| 1107 |
+
c && (c = ie(s, c)),
|
| 1108 |
+
p = ie(l, p);
|
| 1109 |
+
var d = dt(f);
|
| 1110 |
+
xt(d, (function() {
|
| 1111 |
+
se("Cannot construct " + f + " due to unbound types", [n])
|
| 1112 |
+
})),
|
| 1113 |
+
bt([t, e, r], n ? [n] : [], (function(e) {
|
| 1114 |
+
var r, o;
|
| 1115 |
+
e = e[0],
|
| 1116 |
+
o = n ? (r = e.registeredClass).instancePrototype : Rt.prototype;
|
| 1117 |
+
var a = ht(d, (function() {
|
| 1118 |
+
if (Object.getPrototypeOf(this) !== s)
|
| 1119 |
+
throw new mt("Use 'new' to construct " + f);
|
| 1120 |
+
if (void 0 === l.constructor_body)
|
| 1121 |
+
throw new mt(f + " has no accessible constructor");
|
| 1122 |
+
var t = l.constructor_body[arguments.length];
|
| 1123 |
+
if (void 0 === t)
|
| 1124 |
+
throw new mt("Tried to invoke ctor of " + f + " with invalid number of parameters (" + arguments.length + ") - expected (" + Object.keys(l.constructor_body).toString() + ") parameters instead!");
|
| 1125 |
+
return t.apply(this, arguments)
|
| 1126 |
+
})),
|
| 1127 |
+
s = Object.create(o, {
|
| 1128 |
+
constructor: {
|
| 1129 |
+
value: a
|
| 1130 |
+
}
|
| 1131 |
+
});
|
| 1132 |
+
a.prototype = s;
|
| 1133 |
+
var l = new Ut(f, a, s, p, r, i, u, c),
|
| 1134 |
+
h = new re(f, l, !0, !1, !1),
|
| 1135 |
+
y = new re(f + "*", l, !1, !1, !1),
|
| 1136 |
+
m = new re(f + " const*", l, !1, !0, !1);
|
| 1137 |
+
return Mt[t] = {
|
| 1138 |
+
pointerType: y,
|
| 1139 |
+
constPointerType: m
|
| 1140 |
+
},
|
| 1141 |
+
ne(d, a),
|
| 1142 |
+
[h, y, m]
|
| 1143 |
+
}))
|
| 1144 |
+
},
|
| 1145 |
+
w: function(t, e, r, n, o, i) {
|
| 1146 |
+
T(e > 0);
|
| 1147 |
+
var a = ce(e, r);
|
| 1148 |
+
o = ie(n, o),
|
| 1149 |
+
bt([], [t], (function(t) {
|
| 1150 |
+
var r = "constructor " + (t = t[0]).name;
|
| 1151 |
+
if (void 0 === t.registeredClass.constructor_body && (t.registeredClass.constructor_body = []),
|
| 1152 |
+
void 0 !== t.registeredClass.constructor_body[e - 1])
|
| 1153 |
+
throw new mt("Cannot register multiple constructors with identical number of parameters (" + (e - 1) + ") for class '" + t.name + "'! Overload resolution is currently only performed using the parameter count, not actual type info!");
|
| 1154 |
+
return t.registeredClass.constructor_body[e - 1] = function() {
|
| 1155 |
+
se("Cannot construct " + t.name + " due to unbound types", a)
|
| 1156 |
+
},
|
| 1157 |
+
bt([], a, (function(n) {
|
| 1158 |
+
return n.splice(1, 0, null),
|
| 1159 |
+
t.registeredClass.constructor_body[e - 1] = le(r, n, null, o, i),
|
| 1160 |
+
[]
|
| 1161 |
+
})),
|
| 1162 |
+
[]
|
| 1163 |
+
}))
|
| 1164 |
+
},
|
| 1165 |
+
c: function(t, e, r, n, o, i, a, u) {
|
| 1166 |
+
var s = ce(r, n);
|
| 1167 |
+
e = ct(e),
|
| 1168 |
+
i = ie(o, i),
|
| 1169 |
+
bt([], [t], (function(t) {
|
| 1170 |
+
var n = (t = t[0]).name + "." + e;
|
| 1171 |
+
|
| 1172 |
+
function o() {
|
| 1173 |
+
se("Cannot call " + n + " due to unbound types", s)
|
| 1174 |
+
}
|
| 1175 |
+
e.startsWith("@@") && (e = Symbol[e.substring(2)]),
|
| 1176 |
+
u && t.registeredClass.pureVirtualFunctions.push(e);
|
| 1177 |
+
var c = t.registeredClass.instancePrototype,
|
| 1178 |
+
f = c[e];
|
| 1179 |
+
return void 0 === f || void 0 === f.overloadTable && f.className !== t.name && f.argCount === r - 2 ? (o.argCount = r - 2,
|
| 1180 |
+
o.className = t.name,
|
| 1181 |
+
c[e] = o) : (It(c, e, n),
|
| 1182 |
+
c[e].overloadTable[r - 2] = o),
|
| 1183 |
+
bt([], s, (function(o) {
|
| 1184 |
+
var u = le(n, o, t, i, a);
|
| 1185 |
+
return void 0 === c[e].overloadTable ? (u.argCount = r - 2,
|
| 1186 |
+
c[e] = u) : c[e].overloadTable[r - 2] = u,
|
| 1187 |
+
[]
|
| 1188 |
+
})),
|
| 1189 |
+
[]
|
| 1190 |
+
}))
|
| 1191 |
+
},
|
| 1192 |
+
x: function(t, e) {
|
| 1193 |
+
_t(t, {
|
| 1194 |
+
name: e = ct(e),
|
| 1195 |
+
fromWireType: function(t) {
|
| 1196 |
+
var e = ve.toValue(t);
|
| 1197 |
+
return he(t),
|
| 1198 |
+
e
|
| 1199 |
+
},
|
| 1200 |
+
toWireType: function(t, e) {
|
| 1201 |
+
return ve.toHandle(e)
|
| 1202 |
+
},
|
| 1203 |
+
argPackAdvance: 8,
|
| 1204 |
+
readValueFromPointer: Bt,
|
| 1205 |
+
destructorFunction: null
|
| 1206 |
+
})
|
| 1207 |
+
},
|
| 1208 |
+
j: function(t, e, r) {
|
| 1209 |
+
var n = ut(r);
|
| 1210 |
+
_t(t, {
|
| 1211 |
+
name: e = ct(e),
|
| 1212 |
+
fromWireType: function(t) {
|
| 1213 |
+
return t
|
| 1214 |
+
},
|
| 1215 |
+
toWireType: function(t, e) {
|
| 1216 |
+
if ("number" != typeof e && "boolean" != typeof e)
|
| 1217 |
+
throw new TypeError('Cannot convert "' + ge(e) + '" to ' + this.name);
|
| 1218 |
+
return e
|
| 1219 |
+
},
|
| 1220 |
+
argPackAdvance: 8,
|
| 1221 |
+
readValueFromPointer: we(e, n),
|
| 1222 |
+
destructorFunction: null
|
| 1223 |
+
})
|
| 1224 |
+
},
|
| 1225 |
+
l: function(t, e, r, n, o, i) {
|
| 1226 |
+
// Registering functions from constructor (;204;)
|
| 1227 |
+
var a = ce(e, r);
|
| 1228 |
+
t = ct(t),
|
| 1229 |
+
o = ie(n, o),
|
| 1230 |
+
xt(t, (function() {
|
| 1231 |
+
se("Cannot call " + t + " due to unbound types", a)
|
| 1232 |
+
}), e - 1),
|
| 1233 |
+
bt([], a, (function(r) {
|
| 1234 |
+
var n = [r[0], null].concat(r.slice(1));
|
| 1235 |
+
return ne(t, le(t, n, null, o, i), e - 1),
|
| 1236 |
+
[]
|
| 1237 |
+
}))
|
| 1238 |
+
},
|
| 1239 |
+
b: function(t, e, r, n, o) {
|
| 1240 |
+
e = ct(e),
|
| 1241 |
+
-1 === o && (o = 4294967295);
|
| 1242 |
+
var i = ut(r),
|
| 1243 |
+
a = function(t) {
|
| 1244 |
+
return t
|
| 1245 |
+
};
|
| 1246 |
+
if (0 === n) {
|
| 1247 |
+
var u = 32 - 8 * r;
|
| 1248 |
+
a = function(t) {
|
| 1249 |
+
return t << u >>> u
|
| 1250 |
+
}
|
| 1251 |
+
}
|
| 1252 |
+
var s = e.includes("unsigned");
|
| 1253 |
+
_t(t, {
|
| 1254 |
+
name: e,
|
| 1255 |
+
fromWireType: a,
|
| 1256 |
+
toWireType: function(t, r) {
|
| 1257 |
+
if ("number" != typeof r && "boolean" != typeof r)
|
| 1258 |
+
throw new TypeError('Cannot convert "' + ge(r) + '" to ' + this.name);
|
| 1259 |
+
if (r < n || r > o)
|
| 1260 |
+
throw new TypeError('Passing a number "' + ge(r) + '" from JS side to C/C++ side to an argument of type "' + e + '", which is outside the valid range [' + n + ", " + o + "]!");
|
| 1261 |
+
return s ? r >>> 0 : 0 | r
|
| 1262 |
+
},
|
| 1263 |
+
argPackAdvance: 8,
|
| 1264 |
+
readValueFromPointer: be(e, i, 0 !== n),
|
| 1265 |
+
destructorFunction: null
|
| 1266 |
+
})
|
| 1267 |
+
},
|
| 1268 |
+
a: function(t, e, r) {
|
| 1269 |
+
var n = [Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array][e];
|
| 1270 |
+
|
| 1271 |
+
function o(t) {
|
| 1272 |
+
var e = R,
|
| 1273 |
+
r = e[t >>= 2],
|
| 1274 |
+
o = e[t + 1];
|
| 1275 |
+
return new n(E, o, r)
|
| 1276 |
+
}
|
| 1277 |
+
_t(t, {
|
| 1278 |
+
name: r = ct(r),
|
| 1279 |
+
fromWireType: o,
|
| 1280 |
+
argPackAdvance: 8,
|
| 1281 |
+
readValueFromPointer: o
|
| 1282 |
+
}, {
|
| 1283 |
+
ignoreDuplicateRegistrations: !0
|
| 1284 |
+
})
|
| 1285 |
+
},
|
| 1286 |
+
k: function(t, e) {
|
| 1287 |
+
var r = "std::string" === (e = ct(e));
|
| 1288 |
+
_t(t, {
|
| 1289 |
+
name: e,
|
| 1290 |
+
fromWireType: function(t) {
|
| 1291 |
+
var e, n = R[t >> 2];
|
| 1292 |
+
if (r)
|
| 1293 |
+
for (var o = t + 4, i = 0; i <= n; ++i) {
|
| 1294 |
+
var a = t + 4 + i;
|
| 1295 |
+
if (i == n || 0 == O[a]) {
|
| 1296 |
+
var u = UTF8ToString(o, a - o);
|
| 1297 |
+
void 0 === e ? e = u : (e += String.fromCharCode(0),
|
| 1298 |
+
e += u),
|
| 1299 |
+
o = a + 1
|
| 1300 |
+
}
|
| 1301 |
+
}
|
| 1302 |
+
else {
|
| 1303 |
+
var s = new Array(n);
|
| 1304 |
+
for (i = 0; i < n; ++i)
|
| 1305 |
+
s[i] = String.fromCharCode(O[t + 4 + i]);
|
| 1306 |
+
e = s.join("")
|
| 1307 |
+
}
|
| 1308 |
+
return We(t),
|
| 1309 |
+
e
|
| 1310 |
+
},
|
| 1311 |
+
toWireType: function(t, e) {
|
| 1312 |
+
e instanceof ArrayBuffer && (e = new Uint8Array(e));
|
| 1313 |
+
var n = "string" == typeof e;
|
| 1314 |
+
n || e instanceof Uint8Array || e instanceof Uint8ClampedArray || e instanceof Int8Array || vt("Cannot pass non-string to std::string");
|
| 1315 |
+
var o = (r && n ? function() {
|
| 1316 |
+
return UTF8CharCount(e)
|
| 1317 |
+
} :
|
| 1318 |
+
function() {
|
| 1319 |
+
return e.length
|
| 1320 |
+
}
|
| 1321 |
+
)(),
|
| 1322 |
+
i = ke(4 + o + 1);
|
| 1323 |
+
if (R[i >> 2] = o,
|
| 1324 |
+
r && n)
|
| 1325 |
+
stringToUTF8Array(e, O, i + 4, o + 1);
|
| 1326 |
+
else if (n)
|
| 1327 |
+
for (var a = 0; a < o; ++a) {
|
| 1328 |
+
var u = e.charCodeAt(a);
|
| 1329 |
+
u > 255 && (We(i),
|
| 1330 |
+
vt("String has UTF-16 code units that do not fit in 8 bits")),
|
| 1331 |
+
O[i + 4 + a] = u
|
| 1332 |
+
}
|
| 1333 |
+
else
|
| 1334 |
+
for (a = 0; a < o; ++a)
|
| 1335 |
+
O[i + 4 + a] = e[a];
|
| 1336 |
+
return null !== t && t.push(We, i),
|
| 1337 |
+
i
|
| 1338 |
+
},
|
| 1339 |
+
argPackAdvance: 8,
|
| 1340 |
+
readValueFromPointer: Bt,
|
| 1341 |
+
destructorFunction: function(t) {
|
| 1342 |
+
We(t)
|
| 1343 |
+
}
|
| 1344 |
+
})
|
| 1345 |
+
},
|
| 1346 |
+
e: function(t, e, r) {
|
| 1347 |
+
var n, o, i, a, u;
|
| 1348 |
+
r = ct(r),
|
| 1349 |
+
2 === e ? (n = UTF16ArrayToString,
|
| 1350 |
+
o = H,
|
| 1351 |
+
a = Y,
|
| 1352 |
+
i = function() {
|
| 1353 |
+
return W
|
| 1354 |
+
},
|
| 1355 |
+
u = 1) : 4 === e && (n = V,
|
| 1356 |
+
o = z,
|
| 1357 |
+
a = B,
|
| 1358 |
+
i = function() {
|
| 1359 |
+
return R
|
| 1360 |
+
},
|
| 1361 |
+
u = 2),
|
| 1362 |
+
_t(t, {
|
| 1363 |
+
name: r,
|
| 1364 |
+
fromWireType: function(t) {
|
| 1365 |
+
for (var r, o = R[t >> 2], a = i(), s = t + 4, c = 0; c <= o; ++c) {
|
| 1366 |
+
var f = t + 4 + c * e;
|
| 1367 |
+
if (c == o || 0 == a[f >> u]) {
|
| 1368 |
+
var l = n(s, f - s);
|
| 1369 |
+
void 0 === r ? r = l : (r += String.fromCharCode(0),
|
| 1370 |
+
r += l),
|
| 1371 |
+
s = f + e
|
| 1372 |
+
}
|
| 1373 |
+
}
|
| 1374 |
+
return We(t),
|
| 1375 |
+
r
|
| 1376 |
+
},
|
| 1377 |
+
toWireType: function(t, n) {
|
| 1378 |
+
"string" != typeof n && vt("Cannot pass non-string to C++ string type " + r);
|
| 1379 |
+
var i = a(n),
|
| 1380 |
+
s = ke(4 + i + e);
|
| 1381 |
+
return R[s >> 2] = i >> u,
|
| 1382 |
+
o(n, s + 4, i + e),
|
| 1383 |
+
null !== t && t.push(We, s),
|
| 1384 |
+
s
|
| 1385 |
+
},
|
| 1386 |
+
argPackAdvance: 8,
|
| 1387 |
+
readValueFromPointer: Bt,
|
| 1388 |
+
destructorFunction: function(t) {
|
| 1389 |
+
We(t)
|
| 1390 |
+
}
|
| 1391 |
+
})
|
| 1392 |
+
},
|
| 1393 |
+
z: function(t, e) {
|
| 1394 |
+
_t(t, {
|
| 1395 |
+
isVoid: !0,
|
| 1396 |
+
name: e = ct(e),
|
| 1397 |
+
argPackAdvance: 0,
|
| 1398 |
+
fromWireType: function() {},
|
| 1399 |
+
toWireType: function(t, e) {}
|
| 1400 |
+
})
|
| 1401 |
+
},
|
| 1402 |
+
m: he,
|
| 1403 |
+
n: function(t) {
|
| 1404 |
+
t > 4 && (de[t].refcount += 1)
|
| 1405 |
+
},
|
| 1406 |
+
o: function(t, e) {
|
| 1407 |
+
var r, n, o;
|
| 1408 |
+
n = "_emval_take_value",
|
| 1409 |
+
void 0 === (o = lt[r = t]) && vt(n + " has unknown type " + ue(r));
|
| 1410 |
+
var i = (t = o).readValueFromPointer(e);
|
| 1411 |
+
return ve.toHandle(i)
|
| 1412 |
+
},
|
| 1413 |
+
h: function() {
|
| 1414 |
+
abort("")
|
| 1415 |
+
},
|
| 1416 |
+
r: function(t, e, r) {
|
| 1417 |
+
O.copyWithin(t, e, e + r)
|
| 1418 |
+
},
|
| 1419 |
+
s: function(t) {
|
| 1420 |
+
O.length,
|
| 1421 |
+
abort("OOM")
|
| 1422 |
+
},
|
| 1423 |
+
u: function(t, e) {},
|
| 1424 |
+
v: function(t, e) {},
|
| 1425 |
+
i: function(t, e, r, n) {
|
| 1426 |
+
for (var o = 0, i = 0; i < r; i++) {
|
| 1427 |
+
var a = j[e >> 2],
|
| 1428 |
+
u = j[e + 4 >> 2];
|
| 1429 |
+
e += 8;
|
| 1430 |
+
for (var s = 0; s < u; s++)
|
| 1431 |
+
Te.printChar(t, O[a + s]);
|
| 1432 |
+
o += u
|
| 1433 |
+
}
|
| 1434 |
+
return j[n >> 2] = o,
|
| 1435 |
+
0
|
| 1436 |
+
},
|
| 1437 |
+
q: function(t) {
|
| 1438 |
+
t
|
| 1439 |
+
},
|
| 1440 |
+
t: function(t, e, r, n) {
|
| 1441 |
+
return Ee(t, e, r, n)
|
| 1442 |
+
}
|
| 1443 |
+
},
|
| 1444 |
+
ke = (function() {
|
| 1445 |
+
var import_table = {
|
| 1446 |
+
a: import_table_impl
|
| 1447 |
+
};
|
| 1448 |
+
|
| 1449 |
+
function updateGlobalBufferAndViews(t, e) {
|
| 1450 |
+
var r, n, exports = t.exports;
|
| 1451 |
+
o.asm = exports;
|
| 1452 |
+
b = o.asm.B; // Mem
|
| 1453 |
+
r = b.buffer
|
| 1454 |
+
E = r
|
| 1455 |
+
o.HEAP8 = S = new Int8Array(r),
|
| 1456 |
+
o.HEAP16 = k = new Int16Array(r),
|
| 1457 |
+
o.HEAP32 = j = new Int32Array(r),
|
| 1458 |
+
o.HEAPU8 = O = new Uint8Array(r),
|
| 1459 |
+
o.HEAPU16 = W = new Uint16Array(r),
|
| 1460 |
+
o.HEAPU32 = R = new Uint32Array(r),
|
| 1461 |
+
o.HEAPF32 = M = new Float32Array(r),
|
| 1462 |
+
o.HEAPF64 = I = new Float64Array(r),
|
| 1463 |
+
L = o.asm.D // Table
|
| 1464 |
+
n = o.asm.C // ctor
|
| 1465 |
+
N.unshift(n),
|
| 1466 |
+
function(t) {
|
| 1467 |
+
if (J--,
|
| 1468 |
+
o.monitorRunDependencies && o.monitorRunDependencies(J),
|
| 1469 |
+
0 == J && (null !== X && (clearInterval(X),
|
| 1470 |
+
X = null),
|
| 1471 |
+
Z)) {
|
| 1472 |
+
var e = Z;
|
| 1473 |
+
Z = null,
|
| 1474 |
+
e()
|
| 1475 |
+
}
|
| 1476 |
+
}()
|
| 1477 |
+
}
|
| 1478 |
+
|
| 1479 |
+
function load_wasm(t) {
|
| 1480 |
+
updateGlobalBufferAndViews(t.instance)
|
| 1481 |
+
}
|
| 1482 |
+
|
| 1483 |
+
function getBinaryPromise(e) {
|
| 1484 |
+
return function() {
|
| 1485 |
+
if (!w) {
|
| 1486 |
+
if (readSync)
|
| 1487 |
+
return new Promise((function(t, e) {
|
| 1488 |
+
readSync(Q, (function(e) {
|
| 1489 |
+
t(new Uint8Array(e))
|
| 1490 |
+
}), e)
|
| 1491 |
+
}))
|
| 1492 |
+
}
|
| 1493 |
+
return Promise.resolve().then((function() {
|
| 1494 |
+
return getBinary(Q)
|
| 1495 |
+
}))
|
| 1496 |
+
}().then((function(e) {
|
| 1497 |
+
return WebAssembly.instantiate(e, import_table)
|
| 1498 |
+
})).then((function(t) {
|
| 1499 |
+
return t
|
| 1500 |
+
})).then(e, (function(t) {
|
| 1501 |
+
g("failed to asynchronously prepare wasm: " + t,Q),
|
| 1502 |
+
abort(t)
|
| 1503 |
+
}))
|
| 1504 |
+
}
|
| 1505 |
+
if (J++,
|
| 1506 |
+
o.monitorRunDependencies && o.monitorRunDependencies(J),
|
| 1507 |
+
o.instantiateWasm)
|
| 1508 |
+
try {
|
| 1509 |
+
return o.instantiateWasm(import_table, updateGlobalBufferAndViews)
|
| 1510 |
+
} catch (t) {
|
| 1511 |
+
return g("Module.instantiateWasm callback failed with error: " + t),
|
| 1512 |
+
!1
|
| 1513 |
+
}
|
| 1514 |
+
w || "function" != typeof WebAssembly.instantiate || isDataURI(Q) || isFileURI(Q) || getBinaryPromise(load_wasm)
|
| 1515 |
+
}(),
|
| 1516 |
+
o.___wasm_call_ctors = function() {
|
| 1517 |
+
return (o.___wasm_call_ctors = o.asm.C).apply(null, arguments)
|
| 1518 |
+
},
|
| 1519 |
+
o._malloc = function() {
|
| 1520 |
+
return (ke = o._malloc = o.asm.E).apply(null, arguments)
|
| 1521 |
+
}
|
| 1522 |
+
),
|
| 1523 |
+
We = o._free = function() {
|
| 1524 |
+
return (We = o._free = o.asm.F).apply(null, arguments)
|
| 1525 |
+
},
|
| 1526 |
+
je = o.___getTypeName = function() {
|
| 1527 |
+
return (je = o.___getTypeName = o.asm.G).apply(null, arguments)
|
| 1528 |
+
};
|
| 1529 |
+
o.___embind_register_native_and_builtin_types = function() {
|
| 1530 |
+
return (o.___embind_register_native_and_builtin_types = o.asm.H).apply(null, arguments)
|
| 1531 |
+
},
|
| 1532 |
+
o.dynCall_jiji = function() {
|
| 1533 |
+
return (o.dynCall_jiji = o.asm.I).apply(null, arguments)
|
| 1534 |
+
},
|
| 1535 |
+
o.dynCall_iiiiij = function() {
|
| 1536 |
+
return (o.dynCall_iiiiij = o.asm.J).apply(null, arguments)
|
| 1537 |
+
},
|
| 1538 |
+
o.dynCall_iiiiijj = function() {
|
| 1539 |
+
return (o.dynCall_iiiiijj = o.asm.K).apply(null, arguments)
|
| 1540 |
+
},
|
| 1541 |
+
o.dynCall_iiiiiijj = function() {
|
| 1542 |
+
return (o.dynCall_iiiiiijj = o.asm.L).apply(null, arguments)
|
| 1543 |
+
},
|
| 1544 |
+
o.dynCall_viijii = function() {
|
| 1545 |
+
return (o.dynCall_viijii = o.asm.M).apply(null, arguments)
|
| 1546 |
+
};
|
| 1547 |
+
|
| 1548 |
+
function ExitStatus(t) {
|
| 1549 |
+
this.name = "ExitStatus",
|
| 1550 |
+
this.message = "Program terminated with exit(" + t + ")",
|
| 1551 |
+
this.status = t
|
| 1552 |
+
}
|
| 1553 |
+
|
| 1554 |
+
function doRun(t) {
|
| 1555 |
+
function postRun() {
|
| 1556 |
+
Se || (Se = !0,o.calledRun = !0,C || (!0,
|
| 1557 |
+
callRuntimeCallbacks(N),
|
| 1558 |
+
o.onRuntimeInitialized && o.onRuntimeInitialized(),
|
| 1559 |
+
function() {
|
| 1560 |
+
if (o.postRun)
|
| 1561 |
+
for ("function" == typeof o.postRun && (o.postRun = [o.postRun]); o.postRun.length;)
|
| 1562 |
+
t = o.postRun.shift(),
|
| 1563 |
+
q.unshift(t);
|
| 1564 |
+
var t;
|
| 1565 |
+
callRuntimeCallbacks(q)
|
| 1566 |
+
}()))
|
| 1567 |
+
}
|
| 1568 |
+
t = t || l
|
| 1569 |
+
J > 0 || (! function preRun() {
|
| 1570 |
+
if (o.preRun)
|
| 1571 |
+
for ("function" == typeof o.preRun && (o.preRun = [o.preRun]); o.preRun.length;)
|
| 1572 |
+
t = o.preRun.shift(),
|
| 1573 |
+
G.unshift(t);
|
| 1574 |
+
var t;
|
| 1575 |
+
callRuntimeCallbacks(G)
|
| 1576 |
+
}(),
|
| 1577 |
+
J > 0 || (o.setStatus ? (o.setStatus("Running..."),
|
| 1578 |
+
setTimeout((function() {
|
| 1579 |
+
setTimeout((function() {
|
| 1580 |
+
o.setStatus("")
|
| 1581 |
+
}), 1),
|
| 1582 |
+
postRun()
|
| 1583 |
+
}), 1)) : postRun()))
|
| 1584 |
+
}
|
| 1585 |
+
if (Z = function t() {
|
| 1586 |
+
Se || doRun(),
|
| 1587 |
+
Se || (Z = t)
|
| 1588 |
+
},
|
| 1589 |
+
o.run = doRun,
|
| 1590 |
+
o.preInit)
|
| 1591 |
+
for ("function" == typeof o.preInit && (o.preInit = [o.preInit]); o.preInit.length > 0;)
|
| 1592 |
+
o.preInit.pop()();
|
| 1593 |
+
doRun();
|
| 1594 |
+
return o;
|
| 1595 |
+
})
|
| 1596 |
+
|
| 1597 |
+
// XXX: With PythonMonkey, the required module
|
| 1598 |
+
// is destructed(?) once the function is called
|
| 1599 |
+
// This is probably not what actaully happened, but
|
| 1600 |
+
// for now, everytime an FP is generated, the entire
|
| 1601 |
+
// WASM module is reloaded as a workaround
|
| 1602 |
+
function instantiateRuntime(){
|
| 1603 |
+
return new Promise((resolve, reject) => {
|
| 1604 |
+
var fpRuntime = AudioFingerprintRuntime()
|
| 1605 |
+
var monitor = setInterval(() => {
|
| 1606 |
+
if (typeof fpRuntime.ExtractQueryFP == "function")
|
| 1607 |
+
clearInterval(monitor) || resolve(fpRuntime)
|
| 1608 |
+
})
|
| 1609 |
+
})
|
| 1610 |
+
}
|
| 1611 |
+
|
| 1612 |
+
function GenerateFP(floatArray) {
|
| 1613 |
+
let PCMBuffer = Float32Array.from(floatArray)
|
| 1614 |
+
console.info('[afp] input samples n=', PCMBuffer.length)
|
| 1615 |
+
return instantiateRuntime().then((fpRuntime) => {
|
| 1616 |
+
console.info('[afp] begin fingerprinting')
|
| 1617 |
+
let fp_vector = fpRuntime.ExtractQueryFP(PCMBuffer.buffer)
|
| 1618 |
+
let result_buf = new Uint8Array(fp_vector.size());
|
| 1619 |
+
for (let t = 0; t < fp_vector.size(); t++)
|
| 1620 |
+
result_buf[t] = fp_vector.get(t);
|
| 1621 |
+
return globalThis.b64encode(result_buf)
|
| 1622 |
+
});
|
| 1623 |
+
}
|
| 1624 |
+
|
| 1625 |
+
if (typeof exports != 'undefined') /* Node, PythonMonkey */
|
| 1626 |
+
exports.GenerateFP = GenerateFP;
|
src/router/xtc_recognize/afp/afp.wasm.js
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
src/router/xtc_recognize/afp/afp_runner.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/**
|
| 2 |
+
* 指纹生成子进程脚本(被 Python 通过 subprocess 调用)
|
| 3 |
+
*
|
| 4 |
+
* 协议:
|
| 5 |
+
* stdin: base64 编码的 16-bit PCM s16le 数据(8kHz 单声道,3秒)
|
| 6 |
+
* stdout: base64 编码的指纹 FP 字符串
|
| 7 |
+
* stderr: 日志/错误
|
| 8 |
+
*
|
| 9 |
+
* 退出码:
|
| 10 |
+
* 0 成功;1 失败
|
| 11 |
+
*/
|
| 12 |
+
|
| 13 |
+
const path = require('path')
|
| 14 |
+
const { GenerateFP } = require(path.join(__dirname, 'afp.js'))
|
| 15 |
+
|
| 16 |
+
async function main() {
|
| 17 |
+
const chunks = []
|
| 18 |
+
process.stdin.on('data', (c) => chunks.push(c))
|
| 19 |
+
process.stdin.on('end', async () => {
|
| 20 |
+
try {
|
| 21 |
+
const b64 = Buffer.concat(chunks).toString('utf8').trim()
|
| 22 |
+
const pcmBytes = Buffer.from(b64, 'base64')
|
| 23 |
+
// s16le -> Float32Array
|
| 24 |
+
const samples = pcmBytes.length / 2
|
| 25 |
+
const pcm = new Float32Array(samples)
|
| 26 |
+
const dv = new DataView(pcmBytes.buffer, pcmBytes.byteOffset, pcmBytes.byteLength)
|
| 27 |
+
for (let i = 0; i < samples; i++) {
|
| 28 |
+
pcm[i] = dv.getInt16(i * 2, true) / 32768
|
| 29 |
+
}
|
| 30 |
+
const t0 = Date.now()
|
| 31 |
+
const fp = await GenerateFP(pcm)
|
| 32 |
+
const t1 = Date.now()
|
| 33 |
+
process.stderr.write(`[afp-runner] samples=${samples} fp_len=${fp.length} took=${t1 - t0}ms\n`)
|
| 34 |
+
process.stdout.write(fp)
|
| 35 |
+
process.exit(0)
|
| 36 |
+
} catch (e) {
|
| 37 |
+
process.stderr.write('[afp-runner] ERROR: ' + (e.stack || e.message) + '\n')
|
| 38 |
+
process.exit(1)
|
| 39 |
+
}
|
| 40 |
+
})
|
| 41 |
+
process.stdin.on('error', (e) => {
|
| 42 |
+
process.stderr.write('[afp-runner] stdin error: ' + e.message + '\n')
|
| 43 |
+
process.exit(1)
|
| 44 |
+
})
|
| 45 |
+
}
|
| 46 |
+
|
| 47 |
+
main()
|
src/router/xtc_recognize/amr_decoder.py
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""AMR/任意音频 -> 8kHz 单声道 PCM s16le 转码。
|
| 2 |
+
|
| 3 |
+
使用系统 ffmpeg(Dockerfile 已 apt-get install ffmpeg)。
|
| 4 |
+
"""
|
| 5 |
+
from __future__ import annotations
|
| 6 |
+
|
| 7 |
+
import asyncio
|
| 8 |
+
import logging
|
| 9 |
+
from typing import Optional
|
| 10 |
+
|
| 11 |
+
logger = logging.getLogger(__name__)
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
async def decode_to_pcm_s16le(
|
| 15 |
+
audio_bytes: bytes,
|
| 16 |
+
*,
|
| 17 |
+
duration_sec: int = 3,
|
| 18 |
+
sample_rate: int = 8000,
|
| 19 |
+
channels: int = 1,
|
| 20 |
+
) -> bytes:
|
| 21 |
+
"""把任意音频(amr/mp3/wav/m4a 等)转成 8kHz 单声道 PCM s16le。
|
| 22 |
+
|
| 23 |
+
Args:
|
| 24 |
+
audio_bytes: 输入音频字节
|
| 25 |
+
duration_sec: 截取时长(秒),默认 3
|
| 26 |
+
sample_rate: 采样率,默认 8000(网易云 shazam_v2 要求)
|
| 27 |
+
channels: 声道数,默认 1
|
| 28 |
+
|
| 29 |
+
Returns:
|
| 30 |
+
PCM s16le 字节流
|
| 31 |
+
|
| 32 |
+
Raises:
|
| 33 |
+
RuntimeError: ffmpeg 失败
|
| 34 |
+
"""
|
| 35 |
+
args = [
|
| 36 |
+
"ffmpeg",
|
| 37 |
+
"-i", "pipe:0",
|
| 38 |
+
"-f", "s16le",
|
| 39 |
+
"-acodec", "pcm_s16le",
|
| 40 |
+
"-ar", str(sample_rate),
|
| 41 |
+
"-ac", str(channels),
|
| 42 |
+
"-t", str(duration_sec),
|
| 43 |
+
"pipe:1",
|
| 44 |
+
]
|
| 45 |
+
logger.info("[amr-decoder] ffmpeg start, input=%d bytes", len(audio_bytes))
|
| 46 |
+
|
| 47 |
+
proc = await asyncio.create_subprocess_exec(
|
| 48 |
+
*args,
|
| 49 |
+
stdin=asyncio.subprocess.PIPE,
|
| 50 |
+
stdout=asyncio.subprocess.PIPE,
|
| 51 |
+
stderr=asyncio.subprocess.PIPE,
|
| 52 |
+
)
|
| 53 |
+
stdout, stderr = await proc.communicate(input=audio_bytes)
|
| 54 |
+
if proc.returncode != 0:
|
| 55 |
+
err = stderr.decode("utf-8", errors="replace")[-500:]
|
| 56 |
+
raise RuntimeError(f"ffmpeg exit {proc.returncode}: {err}")
|
| 57 |
+
logger.info("[amr-decoder] ffmpeg ok, pcm=%d bytes", len(stdout))
|
| 58 |
+
return stdout
|
src/router/xtc_recognize/fingerprint.py
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""调用 Node.js 子进程运行 afp.js 生成音频指纹。
|
| 2 |
+
|
| 3 |
+
通过 stdin/stdout 通信,避免 Node 进程频繁启动开销(每次仍会重启 wasm,
|
| 4 |
+
但实测 93ms 足够快)。
|
| 5 |
+
"""
|
| 6 |
+
from __future__ import annotations
|
| 7 |
+
|
| 8 |
+
import asyncio
|
| 9 |
+
import base64
|
| 10 |
+
import logging
|
| 11 |
+
import os
|
| 12 |
+
from typing import Optional
|
| 13 |
+
|
| 14 |
+
logger = logging.getLogger(__name__)
|
| 15 |
+
|
| 16 |
+
# afp_runner.js 路径(相对于本文件)
|
| 17 |
+
_RUNNER_PATH = os.path.join(os.path.dirname(__file__), "afp", "afp_runner.js")
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
async def generate_fp(pcm_s16le: bytes) -> str:
|
| 21 |
+
"""生成音频指纹。
|
| 22 |
+
|
| 23 |
+
Args:
|
| 24 |
+
pcm_s16le: 8kHz 单声道 PCM s16le 字节
|
| 25 |
+
|
| 26 |
+
Returns:
|
| 27 |
+
base64 编码的指纹字符串
|
| 28 |
+
"""
|
| 29 |
+
b64_input = base64.b64encode(pcm_s16le).decode("ascii")
|
| 30 |
+
|
| 31 |
+
logger.info("[fp] spawn node, input b64=%d chars", len(b64_input))
|
| 32 |
+
proc = await asyncio.create_subprocess_exec(
|
| 33 |
+
"node", _RUNNER_PATH,
|
| 34 |
+
stdin=asyncio.subprocess.PIPE,
|
| 35 |
+
stdout=asyncio.subprocess.PIPE,
|
| 36 |
+
stderr=asyncio.subprocess.PIPE,
|
| 37 |
+
)
|
| 38 |
+
stdout, stderr = await proc.communicate(input=b64_input.encode("ascii"))
|
| 39 |
+
if proc.returncode != 0:
|
| 40 |
+
err = stderr.decode("utf-8", errors="replace")[-500:]
|
| 41 |
+
raise RuntimeError(f"afp_runner exit {proc.returncode}: {err}")
|
| 42 |
+
# 把 stderr 日志输出到 logger
|
| 43 |
+
if stderr:
|
| 44 |
+
for line in stderr.decode("utf-8", errors="replace").splitlines():
|
| 45 |
+
logger.info("[fp] %s", line)
|
| 46 |
+
fp = stdout.decode("ascii").strip()
|
| 47 |
+
logger.info("[fp] generated, len=%d", len(fp))
|
| 48 |
+
return fp
|
src/router/xtc_recognize/ncm_proxy.py
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""网易云 API 代理:识曲 / 歌曲 URL / 歌曲详情。
|
| 2 |
+
|
| 3 |
+
所有调用直连网易云公开接口,不依赖 api-enhanced 子服务。
|
| 4 |
+
"""
|
| 5 |
+
from __future__ import annotations
|
| 6 |
+
|
| 7 |
+
import logging
|
| 8 |
+
from typing import Any, Optional
|
| 9 |
+
|
| 10 |
+
import httpx
|
| 11 |
+
|
| 12 |
+
logger = logging.getLogger(__name__)
|
| 13 |
+
|
| 14 |
+
_NCM_HEADERS = {
|
| 15 |
+
"User-Agent": "Mozilla/5.0 (Linux; Android 10) AppleWebKit/537.36",
|
| 16 |
+
"Referer": "https://music.163.com",
|
| 17 |
+
}
|
| 18 |
+
_TIMEOUT = 10.0
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
async def audio_match(audio_fp: str, duration: int = 3) -> dict:
|
| 22 |
+
"""调用网易云听歌识曲接口。
|
| 23 |
+
|
| 24 |
+
Args:
|
| 25 |
+
audio_fp: base64 编码的音频指纹
|
| 26 |
+
duration: 录音时长(秒)
|
| 27 |
+
|
| 28 |
+
Returns:
|
| 29 |
+
网易云原始响应 dict,结构 {data: {result: [{song, startTime, score}]}}
|
| 30 |
+
"""
|
| 31 |
+
url = "https://interface.music.163.com/api/music/audio/match"
|
| 32 |
+
params = {
|
| 33 |
+
"sessionId": "0123456789abcdef",
|
| 34 |
+
"algorithmCode": "shazam_v2",
|
| 35 |
+
"duration": str(duration),
|
| 36 |
+
"rawdata": audio_fp,
|
| 37 |
+
"times": "1",
|
| 38 |
+
"decrypt": "1",
|
| 39 |
+
}
|
| 40 |
+
async with httpx.AsyncClient(timeout=_TIMEOUT) as client:
|
| 41 |
+
resp = await client.get(url, params=params, headers=_NCM_HEADERS)
|
| 42 |
+
resp.raise_for_status()
|
| 43 |
+
return resp.json()
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
async def get_song_url(song_id: str, *, unblock: bool = False, level: str = "standard") -> dict:
|
| 47 |
+
"""获取试听 URL。
|
| 48 |
+
|
| 49 |
+
优先调用网易云公开接口(无 cookie,仅试听片段)。
|
| 50 |
+
若需要完整播放,由前端开关 unblock=true 触发,但本代理不实现 unblock
|
| 51 |
+
(需要 api-enhanced 的 unblockmusic-utils,这里仅返回公开接口结果)。
|
| 52 |
+
|
| 53 |
+
Returns:
|
| 54 |
+
{id, url, type, freeTrialInfo, fee}
|
| 55 |
+
"""
|
| 56 |
+
url = "https://music.163.com/api/song/enhance/player/url"
|
| 57 |
+
params = {"ids": f"[{song_id}]", "br": "128000"}
|
| 58 |
+
async with httpx.AsyncClient(timeout=_TIMEOUT) as client:
|
| 59 |
+
resp = await client.get(url, params=params, headers=_NCM_HEADERS)
|
| 60 |
+
resp.raise_for_status()
|
| 61 |
+
data = resp.json()
|
| 62 |
+
item = (data.get("data") or [{}])[0]
|
| 63 |
+
return {
|
| 64 |
+
"id": item.get("id"),
|
| 65 |
+
"url": item.get("url"),
|
| 66 |
+
"type": item.get("type"),
|
| 67 |
+
"freeTrialInfo": None,
|
| 68 |
+
"fee": item.get("fee"),
|
| 69 |
+
"unblock_supported": False, # 标记给前端:本后端不支持 unblock
|
| 70 |
+
}
|
| 71 |
+
|
| 72 |
+
|
| 73 |
+
async def get_song_detail(song_id: str) -> Optional[dict]:
|
| 74 |
+
"""获取歌曲详情(封面、歌手等)。"""
|
| 75 |
+
url = "https://music.163.com/api/v1/song/detail"
|
| 76 |
+
params = {"ids": f"[{song_id}]"}
|
| 77 |
+
async with httpx.AsyncClient(timeout=_TIMEOUT) as client:
|
| 78 |
+
resp = await client.get(url, params=params, headers=_NCM_HEADERS)
|
| 79 |
+
resp.raise_for_status()
|
| 80 |
+
data = resp.json()
|
| 81 |
+
songs = data.get("songs") or []
|
| 82 |
+
if not songs:
|
| 83 |
+
return None
|
| 84 |
+
s = songs[0]
|
| 85 |
+
return {
|
| 86 |
+
"id": s.get("id"),
|
| 87 |
+
"name": s.get("name"),
|
| 88 |
+
"artists": "/".join(a.get("name", "") for a in s.get("artists", [])),
|
| 89 |
+
"album": (s.get("album") or {}).get("name"),
|
| 90 |
+
"cover": (s.get("album") or {}).get("picUrl"),
|
| 91 |
+
"duration": s.get("duration"),
|
| 92 |
+
}
|
| 93 |
+
|
| 94 |
+
|
| 95 |
+
async def get_song_detail_batch(song_ids: list[str]) -> list[Optional[dict]]:
|
| 96 |
+
"""批量获取歌曲详情。公开接口不支持批量,逐个调用。"""
|
| 97 |
+
# 网易云公开接口 ids 参数实际是 JSON 数组字符串,可以传多个
|
| 98 |
+
# 但为稳定性考虑,单个查询更可靠
|
| 99 |
+
url = "https://music.163.com/api/v1/song/detail"
|
| 100 |
+
ids_json = "[" + ",".join(str(i) for i in song_ids) + "]"
|
| 101 |
+
params = {"ids": ids_json}
|
| 102 |
+
async with httpx.AsyncClient(timeout=_TIMEOUT) as client:
|
| 103 |
+
resp = await client.get(url, params=params, headers=_NCM_HEADERS)
|
| 104 |
+
resp.raise_for_status()
|
| 105 |
+
data = resp.json()
|
| 106 |
+
songs = data.get("songs") or []
|
| 107 |
+
by_id = {}
|
| 108 |
+
for s in songs:
|
| 109 |
+
by_id[str(s.get("id"))] = {
|
| 110 |
+
"id": s.get("id"),
|
| 111 |
+
"name": s.get("name"),
|
| 112 |
+
"artists": "/".join(a.get("name", "") for a in s.get("artists", [])),
|
| 113 |
+
"album": (s.get("album") or {}).get("name"),
|
| 114 |
+
"cover": (s.get("album") or {}).get("picUrl"),
|
| 115 |
+
"duration": s.get("duration"),
|
| 116 |
+
}
|
| 117 |
+
return [by_id.get(str(sid)) for sid in song_ids]
|
src/router/xtc_recognize/recognize.py
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""识曲主流程:buffer -> PCM -> FP -> 网易云识曲 -> 批量补详情。"""
|
| 2 |
+
from __future__ import annotations
|
| 3 |
+
|
| 4 |
+
import logging
|
| 5 |
+
from typing import Any
|
| 6 |
+
|
| 7 |
+
from .amr_decoder import decode_to_pcm_s16le
|
| 8 |
+
from .fingerprint import generate_fp
|
| 9 |
+
from .ncm_proxy import audio_match, get_song_detail_batch
|
| 10 |
+
|
| 11 |
+
logger = logging.getLogger(__name__)
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
async def recognize_from_audio(
|
| 15 |
+
audio_bytes: bytes,
|
| 16 |
+
*,
|
| 17 |
+
duration: int = 3,
|
| 18 |
+
mime: str = "",
|
| 19 |
+
) -> dict:
|
| 20 |
+
"""主入口。
|
| 21 |
+
|
| 22 |
+
Args:
|
| 23 |
+
audio_bytes: 原始音频字节(amr/wav/mp3/m4a 等 ffmpeg 支持的格式)
|
| 24 |
+
duration: 采样时长(秒)
|
| 25 |
+
mime: MIME 类型(仅用于日志)
|
| 26 |
+
|
| 27 |
+
Returns:
|
| 28 |
+
{fp: str, list: [...], raw_match: dict}
|
| 29 |
+
"""
|
| 30 |
+
logger.info(
|
| 31 |
+
"[recognize] start, input=%d bytes mime=%s duration=%d",
|
| 32 |
+
len(audio_bytes), mime, duration,
|
| 33 |
+
)
|
| 34 |
+
|
| 35 |
+
# 1) 转码
|
| 36 |
+
pcm = await decode_to_pcm_s16le(audio_bytes, duration_sec=duration)
|
| 37 |
+
|
| 38 |
+
# 2) 指纹
|
| 39 |
+
fp = await generate_fp(pcm)
|
| 40 |
+
|
| 41 |
+
# 3) 识曲
|
| 42 |
+
match_res = await audio_match(fp, duration)
|
| 43 |
+
candidates = ((match_res.get("data") or {}).get("result")) or []
|
| 44 |
+
logger.info("[recognize] match candidates=%d", len(candidates))
|
| 45 |
+
|
| 46 |
+
if not candidates:
|
| 47 |
+
return {"fp": fp, "list": [], "raw_match": match_res}
|
| 48 |
+
|
| 49 |
+
# 4) 批量补全详情
|
| 50 |
+
ids = [str(c.get("song", {}).get("id")) for c in candidates if c.get("song", {}).get("id")]
|
| 51 |
+
details = await get_song_detail_batch(ids) if ids else []
|
| 52 |
+
detail_map = {str(d["id"]): d for d in details if d}
|
| 53 |
+
|
| 54 |
+
# 5) 组装结果,按 score 排序
|
| 55 |
+
out_list = []
|
| 56 |
+
for c in candidates:
|
| 57 |
+
song = c.get("song") or {}
|
| 58 |
+
sid = str(song.get("id")) if song.get("id") else ""
|
| 59 |
+
d = detail_map.get(sid, {})
|
| 60 |
+
out_list.append({
|
| 61 |
+
"id": song.get("id"),
|
| 62 |
+
"name": d.get("name") or song.get("name"),
|
| 63 |
+
"artists": d.get("artists") or "/".join(
|
| 64 |
+
a.get("name", "") for a in song.get("artists", [])
|
| 65 |
+
),
|
| 66 |
+
"album": d.get("album") or (song.get("album") or {}).get("name"),
|
| 67 |
+
"cover": d.get("cover") or (song.get("album") or {}).get("picUrl"),
|
| 68 |
+
"duration": d.get("duration"),
|
| 69 |
+
"matchScore": c.get("score"),
|
| 70 |
+
"startTime": c.get("startTime"),
|
| 71 |
+
})
|
| 72 |
+
out_list.sort(key=lambda x: (x.get("matchScore") or 0), reverse=True)
|
| 73 |
+
|
| 74 |
+
return {"fp": fp, "list": out_list, "raw_match": match_res}
|
src/router/xtc_recognize/router.py
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""XTC 听歌识曲 FastAPI 路由。
|
| 2 |
+
|
| 3 |
+
无侵入集成到 gcli2api:所有路径前缀 /xtc,不与现有路由冲突。
|
| 4 |
+
"""
|
| 5 |
+
from __future__ import annotations
|
| 6 |
+
|
| 7 |
+
import base64
|
| 8 |
+
import logging
|
| 9 |
+
from typing import Any, Optional
|
| 10 |
+
|
| 11 |
+
from fastapi import APIRouter, Query, Request
|
| 12 |
+
from fastapi.responses import JSONResponse
|
| 13 |
+
|
| 14 |
+
from .ncm_proxy import get_song_detail, get_song_url
|
| 15 |
+
from .recognize import recognize_from_audio
|
| 16 |
+
|
| 17 |
+
logger = logging.getLogger(__name__)
|
| 18 |
+
|
| 19 |
+
router = APIRouter(prefix="/xtc", tags=["XTC Recognize"])
|
| 20 |
+
|
| 21 |
+
# 可选鉴权:环境变量 XTC_API_KEY 设置后,请求头 X-API-Key 需匹配
|
| 22 |
+
import os
|
| 23 |
+
_API_KEY = os.environ.get("XTC_API_KEY", "")
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
def _check_key(request: Request) -> Optional[JSONResponse]:
|
| 27 |
+
"""校验 X-API-Key。未配置则放行。"""
|
| 28 |
+
if not _API_KEY:
|
| 29 |
+
return None
|
| 30 |
+
provided = request.headers.get("X-API-Key", "")
|
| 31 |
+
if provided == _API_KEY:
|
| 32 |
+
return None
|
| 33 |
+
return JSONResponse(
|
| 34 |
+
status_code=401,
|
| 35 |
+
content={"code": 401, "msg": "unauthorized"},
|
| 36 |
+
)
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
def _ok(data: Any) -> JSONResponse:
|
| 40 |
+
return JSONResponse(
|
| 41 |
+
status_code=200,
|
| 42 |
+
content={"code": 200, "data": data},
|
| 43 |
+
headers={
|
| 44 |
+
"Access-Control-Allow-Origin": "*",
|
| 45 |
+
"Access-Control-Allow-Methods": "GET,POST,OPTIONS",
|
| 46 |
+
"Access-Control-Allow-Headers": "Content-Type, X-API-Key",
|
| 47 |
+
},
|
| 48 |
+
)
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
def _err(msg: str, status: int = 500) -> JSONResponse:
|
| 52 |
+
return JSONResponse(
|
| 53 |
+
status_code=status,
|
| 54 |
+
content={"code": status, "msg": msg},
|
| 55 |
+
headers={
|
| 56 |
+
"Access-Control-Allow-Origin": "*",
|
| 57 |
+
"Access-Control-Allow-Methods": "GET,POST,OPTIONS",
|
| 58 |
+
"Access-Control-Allow-Headers": "Content-Type, X-API-Key",
|
| 59 |
+
},
|
| 60 |
+
)
|
| 61 |
+
|
| 62 |
+
|
| 63 |
+
@router.options("/{path:path}")
|
| 64 |
+
async def cors_preflight(path: str) -> JSONResponse:
|
| 65 |
+
return JSONResponse(
|
| 66 |
+
status_code=204,
|
| 67 |
+
content=None,
|
| 68 |
+
headers={
|
| 69 |
+
"Access-Control-Allow-Origin": "*",
|
| 70 |
+
"Access-Control-Allow-Methods": "GET,POST,OPTIONS",
|
| 71 |
+
"Access-Control-Allow-Headers": "Content-Type, X-API-Key",
|
| 72 |
+
"Access-Control-Max-Age": "86400",
|
| 73 |
+
},
|
| 74 |
+
)
|
| 75 |
+
|
| 76 |
+
|
| 77 |
+
@router.get("/health")
|
| 78 |
+
async def health() -> JSONResponse:
|
| 79 |
+
"""健康检查。"""
|
| 80 |
+
import shutil
|
| 81 |
+
node_ok = shutil.which("node") is not None
|
| 82 |
+
ffmpeg_ok = shutil.which("ffmpeg") is not None
|
| 83 |
+
return _ok({
|
| 84 |
+
"service": "xtc-recognize",
|
| 85 |
+
"node": node_ok,
|
| 86 |
+
"ffmpeg": ffmpeg_ok,
|
| 87 |
+
"auth": bool(_API_KEY),
|
| 88 |
+
})
|
| 89 |
+
|
| 90 |
+
|
| 91 |
+
@router.post("/recognize")
|
| 92 |
+
async def recognize(request: Request) -> JSONResponse:
|
| 93 |
+
"""识曲接口。
|
| 94 |
+
|
| 95 |
+
Body: JSON
|
| 96 |
+
{
|
| 97 |
+
"audio": "<base64 编码的音频字节>",
|
| 98 |
+
"mime": "audio/amr", # 可选
|
| 99 |
+
"name": "record.amr", # 可选
|
| 100 |
+
"duration": 3 # 可选,默认 3
|
| 101 |
+
}
|
| 102 |
+
|
| 103 |
+
Returns:
|
| 104 |
+
{code:200, data:{fp, list:[{id,name,artists,album,cover,duration,matchScore,startTime}], raw_match}}
|
| 105 |
+
"""
|
| 106 |
+
auth_err = _check_key(request)
|
| 107 |
+
if auth_err:
|
| 108 |
+
return auth_err
|
| 109 |
+
|
| 110 |
+
try:
|
| 111 |
+
body = await request.json()
|
| 112 |
+
if not isinstance(body, dict):
|
| 113 |
+
return _err("invalid body", 400)
|
| 114 |
+
audio_b64 = body.get("audio")
|
| 115 |
+
if not audio_b64:
|
| 116 |
+
return _err("audio required", 400)
|
| 117 |
+
duration = int(body.get("duration") or 3)
|
| 118 |
+
if duration < 1 or duration > 10:
|
| 119 |
+
duration = 3
|
| 120 |
+
mime = str(body.get("mime") or "")
|
| 121 |
+
name = str(body.get("name") or "")
|
| 122 |
+
|
| 123 |
+
audio_bytes = base64.b64decode(audio_b64)
|
| 124 |
+
if len(audio_bytes) > 2 * 1024 * 1024:
|
| 125 |
+
return _err("audio too large (max 2MB)", 400)
|
| 126 |
+
|
| 127 |
+
result = await recognize_from_audio(
|
| 128 |
+
audio_bytes,
|
| 129 |
+
duration=duration,
|
| 130 |
+
mime=mime,
|
| 131 |
+
)
|
| 132 |
+
# raw_match 体积大且含敏感字段,对外只返回精简后的 list
|
| 133 |
+
return _ok({
|
| 134 |
+
"fp": result["fp"],
|
| 135 |
+
"list": result["list"],
|
| 136 |
+
"count": len(result["list"]),
|
| 137 |
+
})
|
| 138 |
+
except Exception as e:
|
| 139 |
+
logger.exception("[recognize] error")
|
| 140 |
+
return _err(str(e) or "recognize failed")
|
| 141 |
+
|
| 142 |
+
|
| 143 |
+
@router.get("/song/url")
|
| 144 |
+
async def song_url(
|
| 145 |
+
request: Request,
|
| 146 |
+
id: str = Query(...),
|
| 147 |
+
unblock: str = Query("false"),
|
| 148 |
+
level: str = Query("standard"),
|
| 149 |
+
) -> JSONResponse:
|
| 150 |
+
"""获取试听 URL。
|
| 151 |
+
|
| 152 |
+
公开接口仅返回 30s 试听片段(VIP 歌曲)。unblock 暂不支持。
|
| 153 |
+
"""
|
| 154 |
+
auth_err = _check_key(request)
|
| 155 |
+
if auth_err:
|
| 156 |
+
return auth_err
|
| 157 |
+
try:
|
| 158 |
+
data = await get_song_url(id, unblock=(unblock == "true"), level=level)
|
| 159 |
+
return _ok(data)
|
| 160 |
+
except Exception as e:
|
| 161 |
+
logger.exception("[song/url] error")
|
| 162 |
+
return _err(str(e) or "get url failed")
|
| 163 |
+
|
| 164 |
+
|
| 165 |
+
@router.get("/song/detail")
|
| 166 |
+
async def song_detail(
|
| 167 |
+
request: Request,
|
| 168 |
+
id: str = Query(...),
|
| 169 |
+
) -> JSONResponse:
|
| 170 |
+
"""获取歌曲详情。"""
|
| 171 |
+
auth_err = _check_key(request)
|
| 172 |
+
if auth_err:
|
| 173 |
+
return auth_err
|
| 174 |
+
try:
|
| 175 |
+
data = await get_song_detail(id)
|
| 176 |
+
if not data:
|
| 177 |
+
return _err("song not found", 404)
|
| 178 |
+
return _ok(data)
|
| 179 |
+
except Exception as e:
|
| 180 |
+
logger.exception("[song/detail] error")
|
| 181 |
+
return _err(str(e) or "get detail failed")
|
web.py
CHANGED
|
@@ -141,6 +141,14 @@ app.include_router(geminicli_anthropic_router, prefix="", tags=["Geminicli Anthr
|
|
| 141 |
# Panel路由 - 包含认证、凭证管理和控制面板功能
|
| 142 |
app.include_router(panel_router, prefix="", tags=["Panel Interface"])
|
| 143 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 144 |
# 静态文件路由 - 服务docs目录下的文件
|
| 145 |
app.mount("/docs", StaticFiles(directory="docs"), name="docs")
|
| 146 |
|
|
|
|
| 141 |
# Panel路由 - 包含认证、凭证管理和控制面板功能
|
| 142 |
app.include_router(panel_router, prefix="", tags=["Panel Interface"])
|
| 143 |
|
| 144 |
+
# ===== XTC 听歌识曲路由(无侵入集成,失败不影响主服务) =====
|
| 145 |
+
try:
|
| 146 |
+
from src.router.xtc_recognize import router as xtc_recognize_router
|
| 147 |
+
app.include_router(xtc_recognize_router, tags=["XTC Recognize"])
|
| 148 |
+
log.info("XTC 听歌识曲路由加载成功")
|
| 149 |
+
except Exception as e:
|
| 150 |
+
log.error(f"XTC 听歌识曲路由加载失败(不影响主服务): {e}")
|
| 151 |
+
|
| 152 |
# 静态文件路由 - 服务docs目录下的文件
|
| 153 |
app.mount("/docs", StaticFiles(directory="docs"), name="docs")
|
| 154 |
|