Merge pull request #26 from ZHANGTIANYAO1/claude/fix-docker-audio-playback-9bGfw

Prioritize system FFmpeg over bundled ffmpeg-static
This commit is contained in:
TIANYAO ZHANG authored and GitHub committed 2026-04-12 22:42:56 +08:00
commit 78380f27d4
5 files changed
+649 -394

No files matched your search

+618 -371
View File
File diff suppressed because it is too large. Load diff
+1 -1
View File
@@ -17,7 +17,7 @@
"@discordjs/opus": "^0.10.0", "@discordjs/opus": "^0.10.0",
"@honeybbq/teamspeak-client": "^0.2.1", "@honeybbq/teamspeak-client": "^0.2.1",
"@koa/router": "^15.4.0", "@koa/router": "^15.4.0",
"@sansenjian/qq-music-api": "file:../qq-music-api", "@sansenjian/qq-music-api": "^2.2.10",
"axios": "^1.14.0", "axios": "^1.14.0",
"better-sqlite3": "^12.8.0", "better-sqlite3": "^12.8.0",
"chalk": "^5.6.2", "chalk": "^5.6.2",
+10 -13
View File
@@ -25,30 +25,27 @@ RUN cd web && npm ci
COPY . . COPY . .
RUN npm run build RUN npm run build
# Install production dependencies (with native addons compiled) in the builder
# so we don't need build tools in the production image.
RUN rm -rf node_modules && npm ci --production && npm cache clean --force
# --- Stage 2: Production image --- # --- Stage 2: Production image ---
FROM node:20-slim FROM node:20-slim
# Install only runtime native build tools (needed for native module rebuild) # Install system FFmpeg — the ffmpeg-static npm package bundles a pre-compiled
# binary that can SIGSEGV inside Docker (incompatible glibc / missing libs).
# System-installed FFmpeg is always compatible with the container runtime.
RUN apt-get update && apt-get install -y --no-install-recommends \ RUN apt-get update && apt-get install -y --no-install-recommends \
python3 make g++ && \ ffmpeg && \
rm -rf /var/lib/apt/lists/* rm -rf /var/lib/apt/lists/*
WORKDIR /app WORKDIR /app
# Copy built output # Copy built output and pre-compiled production node_modules from builder
COPY --from=builder /app/dist ./dist COPY --from=builder /app/dist ./dist
COPY --from=builder /app/web/dist ./web/dist COPY --from=builder /app/web/dist ./web/dist
COPY --from=builder /app/package*.json ./ COPY --from=builder /app/package*.json ./
COPY --from=builder /app/node_modules ./node_modules
# Install production dependencies (includes ffmpeg-static, opus, sqlite3)
# These need to compile native addons inside the container
RUN npm ci --production && npm cache clean --force
# Remove build tools to reduce image size
RUN apt-get purge -y python3 make g++ && apt-get autoremove -y && \
rm -rf /var/lib/apt/lists/*
# FFmpeg is bundled via ffmpeg-static — no system ffmpeg needed
# Data directory for database, cookies, logs # Data directory for database, cookies, logs
RUN mkdir -p /app/data RUN mkdir -p /app/data
+2 -2
View File
@@ -1,8 +1,8 @@
# TSMusicBot — Docker Compose # TSMusicBot — Docker Compose
# 一键部署:docker-compose up -d # 一键部署:docker-compose up -d
# #
# 所有依赖已内置(Node.js, FFmpeg, Opus 编码器) # 所有依赖已内置(Node.js, FFmpeg, Opus 编码器)无需安装任何额外软件
# 无需安装任何额外软件 # FFmpeg 使用系统包管理器安装,确保容器内兼容性
services: services:
tsmusicbot: tsmusicbot:
+18 -7
View File
@@ -35,28 +35,39 @@ function ffmpegWorks(bin: string): boolean {
} }
} }
/** Resolved once at module load — prefer bundled ffmpeg-static, fall back to system. */ /**
* Resolved once at module load.
*
* Priority: system FFmpeg → bundled ffmpeg-static.
*
* System-installed FFmpeg is always compatible with the running OS/container,
* while the pre-compiled binary from ffmpeg-static can SIGSEGV in Docker
* (passes `ffmpeg -version` but crashes during actual audio processing due to
* incompatible glibc or missing shared libraries).
*/
const resolvedFfmpeg: string = (() => { const resolvedFfmpeg: string = (() => {
// 1. Prefer system-installed FFmpeg (always compatible with the runtime)
if (ffmpegWorks("ffmpeg")) {
return "ffmpeg";
}
// 2. Fall back to bundled ffmpeg-static binary
// On Windows, ffmpeg-static may return a path with backslashes; on Linux/macOS // On Windows, ffmpeg-static may return a path with backslashes; on Linux/macOS
// it may return a Windows .exe path if node_modules was copied cross-platform. // it may return a Windows .exe path if node_modules was copied cross-platform.
const isWinPath = ffmpegPath ? /\\/.test(ffmpegPath) || ffmpegPath.endsWith(".exe") : false; const isWinPath = ffmpegPath ? /\\/.test(ffmpegPath) || ffmpegPath.endsWith(".exe") : false;
const onWindows = process.platform === "win32"; const onWindows = process.platform === "win32";
// Only try bundled binary if platform matches
if (ffmpegPath && (onWindows === isWinPath)) { if (ffmpegPath && (onWindows === isWinPath)) {
if (isExecutable(ffmpegPath) && ffmpegWorks(ffmpegPath)) { if (isExecutable(ffmpegPath) && ffmpegWorks(ffmpegPath)) {
return ffmpegPath; return ffmpegPath;
} }
} }
// Fall back to system ffmpeg
if (ffmpegWorks("ffmpeg")) {
return "ffmpeg";
}
// Last resort: always use "ffmpeg" so spawn error is clear, never use a cross-platform path // Last resort: always use "ffmpeg" so spawn error is clear, never use a cross-platform path
return "ffmpeg"; return "ffmpeg";
})(); })();
/** Resolve ffmpeg binary: prefer bundled ffmpeg-static, fall back to system PATH. */ /** Resolve ffmpeg binary: prefer system PATH, fall back to bundled ffmpeg-static. */
function getFfmpegCommand(): string { function getFfmpegCommand(): string {
return resolvedFfmpeg; return resolvedFfmpeg;
} }