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) <noreply@anthropic.com>
This commit is contained in:
saopig1andClaude Opus 4.6 committed 2026-03-30 02:40:02 +08:00
1 parent d84f23c2b2
commit dc9d181783
2 files changed
+29 -11

No files matched your search

+1 -1
View File
@@ -23,7 +23,7 @@ export function getDefaultConfig(): BotConfig {
commandPrefix: "!", commandPrefix: "!",
commandAliases: { p: "play", s: "skip", n: "next" }, commandAliases: { p: "play", s: "skip", n: "next" },
neteaseApiPort: 3001, neteaseApiPort: 3001,
qqMusicApiPort: 3002, qqMusicApiPort: 3200,
adminPassword: "", adminPassword: "",
adminGroups: [], adminGroups: [],
autoReturnDelay: 300, autoReturnDelay: 300,
+28 -10
View File
@@ -1,3 +1,4 @@
import net from "node:net";
import type { Logger } from "../logger.js"; import type { Logger } from "../logger.js";
import type { Server } from "node:http"; import type { Server } from "node:http";
@@ -13,6 +14,17 @@ export interface ApiServerManager {
getQQMusicBaseUrl(): string; getQQMusicBaseUrl(): string;
} }
function isPortFree(port: number): Promise<boolean> {
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( export function createApiServerManager(
options: ApiServerOptions, options: ApiServerOptions,
logger: Logger logger: Logger
@@ -28,11 +40,9 @@ export function createApiServerManager(
// Start NetEase Cloud Music API // Start NetEase Cloud Music API
try { try {
// NeteaseCloudMusicApi exports differ between CJS/ESM — use dynamic require
const ncmModule = await import("NeteaseCloudMusicApi") as any; const ncmModule = await import("NeteaseCloudMusicApi") as any;
const serverObj = ncmModule.server ?? ncmModule.default?.server; const serverObj = ncmModule.server ?? ncmModule.default?.server;
const app = await serverObj.serveNcmApi({ port: options.neteasePort }); const app = await serverObj.serveNcmApi({ port: options.neteasePort });
// serveNcmApi returns the express app; the server is already listening
neteaseServer = app; neteaseServer = app;
logger.info( logger.info(
{ port: options.neteasePort }, { port: options.neteasePort },
@@ -42,17 +52,25 @@ export function createApiServerManager(
logger.error({ err }, "Failed to start NetEase Cloud Music API"); 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 { try {
await import("@sansenjian/qq-music-api"); const portFree = await isPortFree(options.qqMusicPort);
logger.info( if (!portFree) {
{ port: options.qqMusicPort }, logger.info(
"QQ Music API started" { 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) { } catch (err) {
logger.error( logger.warn(
{ err }, { err },
"Failed to start QQ Music API" "QQ Music API not available — QQ Music features may be limited"
); );
} }
}, },