From 58caff3209b1ae5c7a9bf9d43f774f9bf8183e27 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 31 Mar 2026 15:58:54 +0000 Subject: [PATCH 1/3] fix: auto-fix ffmpeg binary execute permission on startup The bundled ffmpeg-static binary may lose execute permission after npm install on some platforms, causing EACCES errors during playback. Now checks and fixes the permission automatically before spawning ffmpeg. https://claude.ai/code/session_01CqfKgV8GuCmWNpfx86H62X --- src/audio/player.ts | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/audio/player.ts b/src/audio/player.ts index f637c48..f013782 100644 --- a/src/audio/player.ts +++ b/src/audio/player.ts @@ -1,6 +1,7 @@ import { spawn, type ChildProcess } from "node:child_process"; import { EventEmitter } from "node:events"; import { createRequire } from "node:module"; +import { accessSync, chmodSync, constants } from "node:fs"; import { createOpusEncoder, PCM_FRAME_BYTES, type Encoder } from "./encoder.js"; import type { Logger } from "../logger.js"; @@ -8,9 +9,24 @@ import type { Logger } from "../logger.js"; const require = createRequire(import.meta.url); const ffmpegPath: string | null = require("ffmpeg-static"); +/** Ensure the bundled ffmpeg binary has execute permission. */ +function ensureExecutable(binPath: string): boolean { + try { + accessSync(binPath, constants.X_OK); + return true; + } catch { + try { + chmodSync(binPath, 0o755); + return true; + } catch { + return false; + } + } +} + /** Resolve ffmpeg binary: prefer bundled ffmpeg-static, fall back to system PATH. */ function getFfmpegCommand(): string { - if (ffmpegPath) return ffmpegPath; + if (ffmpegPath && ensureExecutable(ffmpegPath)) return ffmpegPath; return "ffmpeg"; // fallback to system-installed ffmpeg } From 9e52b0bda99c33885af18ad8deb1a5898684e101 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 31 Mar 2026 16:02:00 +0000 Subject: [PATCH 2/3] refactor: cache resolved ffmpeg path at module load time Avoid repeated filesystem checks on every play() call by resolving the ffmpeg binary path once at module load. Also verify execute permission after chmod to handle noexec mounts. https://claude.ai/code/session_01CqfKgV8GuCmWNpfx86H62X --- src/audio/player.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/audio/player.ts b/src/audio/player.ts index f013782..3b37273 100644 --- a/src/audio/player.ts +++ b/src/audio/player.ts @@ -9,14 +9,15 @@ import type { Logger } from "../logger.js"; const require = createRequire(import.meta.url); const ffmpegPath: string | null = require("ffmpeg-static"); -/** Ensure the bundled ffmpeg binary has execute permission. */ -function ensureExecutable(binPath: string): boolean { +/** 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; @@ -24,10 +25,13 @@ function ensureExecutable(binPath: string): boolean { } } +/** 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. */ function getFfmpegCommand(): string { - if (ffmpegPath && ensureExecutable(ffmpegPath)) return ffmpegPath; - return "ffmpeg"; // fallback to system-installed ffmpeg + return resolvedFfmpeg; } export interface PlayerEvents { From 89407d96943798f36e75487f08fdc4aaecba50e6 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 31 Mar 2026 16:04:39 +0000 Subject: [PATCH 3/3] docs: add FFmpeg EACCES troubleshooting to README https://claude.ai/code/session_01CqfKgV8GuCmWNpfx86H62X --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index 7d2dd3f..7b0eb60 100644 --- a/README.md +++ b/README.md @@ -292,6 +292,13 @@ A:可以。在设置页面创建多个实例,分别连接不同的 TS 服务 **Q:端口 3200 被占用?** 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 构建失败?** A:原生模块(opus、sqlite3)需要编译工具,Dockerfile 已包含。确保 Docker 有足够内存(建议 2GB+)。