Merge pull request #2 from ZHANGTIANYAO1/claude/start-music-api-servers-bL8Wo

Ensure ffmpeg binary has execute permissions on startup
This commit is contained in:
TIANYAO ZHANG authored and GitHub committed 2026-04-01 00:05:03 +08:00
commit 55c373d2bf
2 files changed
+29 -2

No files matched your search

+7
View File
@@ -292,6 +292,13 @@ A:可以。在设置页面创建多个实例,分别连接不同的 TS 服务
**Q:端口 3200 被占用?** **Q:端口 3200 被占用?**
A:QQ 音乐 API 启动时自动监听 3200 端口。如果之前的进程还在运行,程序会自动复用。如需重启可手动结束 `node` 进程。 A:QQ 音乐 API 启动时自动监听 3200 端口。如果之前的进程还在运行,程序会自动复用。如需重启可手动结束 `node` 进程。
**Q:播放歌曲时报 FFmpeg EACCES 错误?**
A:`ffmpeg-static` 内置的 FFmpeg 二进制文件缺少执行权限。程序已自动尝试修复,如果仍然失败,请手动执行:
```bash
chmod +x node_modules/ffmpeg-static/ffmpeg
```
或者确保系统已安装 FFmpeg(`apt install ffmpeg` / `brew install ffmpeg`),程序会自动回退使用系统版本。
**Q:Docker 构建失败?** **Q:Docker 构建失败?**
A:原生模块(opus、sqlite3)需要编译工具,Dockerfile 已包含。确保 Docker 有足够内存(建议 2GB+)。 A:原生模块(opus、sqlite3)需要编译工具,Dockerfile 已包含。确保 Docker 有足够内存(建议 2GB+)。
+22 -2
View File
@@ -1,6 +1,7 @@
import { spawn, type ChildProcess } from "node:child_process"; import { spawn, type ChildProcess } from "node:child_process";
import { EventEmitter } from "node:events"; import { EventEmitter } from "node:events";
import { createRequire } from "node:module"; import { createRequire } from "node:module";
import { accessSync, chmodSync, constants } from "node:fs";
import { createOpusEncoder, PCM_FRAME_BYTES, type Encoder } from "./encoder.js"; import { createOpusEncoder, PCM_FRAME_BYTES, type Encoder } from "./encoder.js";
import type { Logger } from "../logger.js"; import type { Logger } from "../logger.js";
@@ -8,10 +9,29 @@ import type { Logger } from "../logger.js";
const require = createRequire(import.meta.url); const require = createRequire(import.meta.url);
const ffmpegPath: string | null = require("ffmpeg-static"); const ffmpegPath: string | null = require("ffmpeg-static");
/** Ensure the given binary has execute permission. */
function isExecutable(binPath: string): boolean {
try {
accessSync(binPath, constants.X_OK);
return true;
} catch {
try {
chmodSync(binPath, 0o755);
accessSync(binPath, constants.X_OK);
return true;
} catch {
return false;
}
}
}
/** Resolved once at module load — no repeated fs checks per play(). */
const resolvedFfmpeg: string =
ffmpegPath && isExecutable(ffmpegPath) ? ffmpegPath : "ffmpeg";
/** Resolve ffmpeg binary: prefer bundled ffmpeg-static, fall back to system PATH. */ /** Resolve ffmpeg binary: prefer bundled ffmpeg-static, fall back to system PATH. */
function getFfmpegCommand(): string { function getFfmpegCommand(): string {
if (ffmpegPath) return ffmpegPath; return resolvedFfmpeg;
return "ffmpeg"; // fallback to system-installed ffmpeg
} }
export interface PlayerEvents { export interface PlayerEvents {