From dc9d1817832a9360b9b78314e076332909b852fc Mon Sep 17 00:00:00 2001 From: saopig1 Date: Mon, 30 Mar 2026 02:40:02 +0800 Subject: [PATCH] fix: check port availability before starting QQ Music API, update default port to 3200 Prevents EADDRINUSE crash that killed the entire app when port 3200 was still occupied from a previous run. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/data/config.ts | 2 +- src/music/api-server.ts | 38 ++++++++++++++++++++++++++++---------- 2 files changed, 29 insertions(+), 11 deletions(-) diff --git a/src/data/config.ts b/src/data/config.ts index ee08a29..2b26897 100644 --- a/src/data/config.ts +++ b/src/data/config.ts @@ -23,7 +23,7 @@ export function getDefaultConfig(): BotConfig { commandPrefix: "!", commandAliases: { p: "play", s: "skip", n: "next" }, neteaseApiPort: 3001, - qqMusicApiPort: 3002, + qqMusicApiPort: 3200, adminPassword: "", adminGroups: [], autoReturnDelay: 300, diff --git a/src/music/api-server.ts b/src/music/api-server.ts index ad6066e..8a1f5cc 100644 --- a/src/music/api-server.ts +++ b/src/music/api-server.ts @@ -1,3 +1,4 @@ +import net from "node:net"; import type { Logger } from "../logger.js"; import type { Server } from "node:http"; @@ -13,6 +14,17 @@ export interface ApiServerManager { getQQMusicBaseUrl(): string; } +function isPortFree(port: number): Promise { + return new Promise((resolve) => { + const server = net.createServer(); + server.once("error", () => resolve(false)); + server.once("listening", () => { + server.close(() => resolve(true)); + }); + server.listen(port, "127.0.0.1"); + }); +} + export function createApiServerManager( options: ApiServerOptions, logger: Logger @@ -28,11 +40,9 @@ export function createApiServerManager( // Start NetEase Cloud Music API try { - // NeteaseCloudMusicApi exports differ between CJS/ESM — use dynamic require const ncmModule = await import("NeteaseCloudMusicApi") as any; const serverObj = ncmModule.server ?? ncmModule.default?.server; const app = await serverObj.serveNcmApi({ port: options.neteasePort }); - // serveNcmApi returns the express app; the server is already listening neteaseServer = app; logger.info( { port: options.neteasePort }, @@ -42,17 +52,25 @@ export function createApiServerManager( logger.error({ err }, "Failed to start NetEase Cloud Music API"); } - // QQ Music API — auto-starts on port 3200 when imported + // Start QQ Music API (auto-starts on import) try { - await import("@sansenjian/qq-music-api"); - logger.info( - { port: options.qqMusicPort }, - "QQ Music API started" - ); + const portFree = await isPortFree(options.qqMusicPort); + if (!portFree) { + logger.info( + { port: options.qqMusicPort }, + "QQ Music API port already in use — reusing existing instance" + ); + } else { + await import("@sansenjian/qq-music-api"); + logger.info( + { port: options.qqMusicPort }, + "QQ Music API started" + ); + } } catch (err) { - logger.error( + logger.warn( { err }, - "Failed to start QQ Music API" + "QQ Music API not available — QQ Music features may be limited" ); } },