Merge origin/main into dev: sync bug fixes from main

Merges 4 PRs from main:
- PR#15: Fix ffmpeg spawn failure causing infinite retry loop
- PR#16: Fix Docker restart crash (respect autoStart flag, persist identity)
- PR#17: Add missing "play" script to package.json
- PR#18: Add detailed update/upgrade instructions to README

Conflict resolution in src/data/database.ts:
- Combined dev's serverProtocol/ts6ApiKey fields with main's identity field
- Extended migrateSchema() to also migrate serverProtocol and ts6ApiKey columns
- Updated SQL schema and upsert query to include all three new columns
This commit is contained in:
saopig1 committed 2026-04-08 15:48:40 +08:00
commit 419911b78b
8 files changed
+201 -20

No files matched your search

+31 -6
View File
@@ -28,6 +28,7 @@ export interface BotInstance {
serverProtocol: string;
/** API key for TS6 HTTP Query */
ts6ApiKey: string;
identity?: string;
}
export interface BotDatabase {
@@ -40,6 +41,20 @@ export interface BotDatabase {
close(): void;
}
function migrateSchema(db: Database.Database): void {
const columns = db.prepare("PRAGMA table_info(bot_instances)").all() as Array<{ name: string }>;
const names = columns.map((c) => c.name);
if (!names.includes("identity")) {
db.exec("ALTER TABLE bot_instances ADD COLUMN identity TEXT");
}
if (!names.includes("serverProtocol")) {
db.exec("ALTER TABLE bot_instances ADD COLUMN serverProtocol TEXT NOT NULL DEFAULT ''");
}
if (!names.includes("ts6ApiKey")) {
db.exec("ALTER TABLE bot_instances ADD COLUMN ts6ApiKey TEXT NOT NULL DEFAULT ''");
}
}
function initTables(db: Database.Database): void {
db.exec(`
CREATE TABLE IF NOT EXISTS play_history (
@@ -64,7 +79,8 @@ function initTables(db: Database.Database): void {
channelPassword TEXT NOT NULL,
autoStart INTEGER NOT NULL DEFAULT 0,
serverProtocol TEXT NOT NULL DEFAULT '',
ts6ApiKey TEXT NOT NULL DEFAULT ''
ts6ApiKey TEXT NOT NULL DEFAULT '',
identity TEXT
);
`);
}
@@ -73,6 +89,7 @@ export function createDatabase(dbPath: string): BotDatabase {
const db = new Database(dbPath);
db.pragma("journal_mode = WAL");
initTables(db);
migrateSchema(db);
const insertHistory = db.prepare(`
INSERT INTO play_history (botId, songId, songName, artist, album, platform, coverUrl)
@@ -84,8 +101,8 @@ export function createDatabase(dbPath: string): BotDatabase {
`);
const upsertInstance = db.prepare(`
INSERT INTO bot_instances (id, name, serverAddress, serverPort, nickname, defaultChannel, channelPassword, autoStart, serverProtocol, ts6ApiKey)
VALUES (@id, @name, @serverAddress, @serverPort, @nickname, @defaultChannel, @channelPassword, @autoStart, @serverProtocol, @ts6ApiKey)
INSERT INTO bot_instances (id, name, serverAddress, serverPort, nickname, defaultChannel, channelPassword, autoStart, serverProtocol, ts6ApiKey, identity)
VALUES (@id, @name, @serverAddress, @serverPort, @nickname, @defaultChannel, @channelPassword, @autoStart, @serverProtocol, @ts6ApiKey, @identity)
ON CONFLICT(id) DO UPDATE SET
name = excluded.name,
serverAddress = excluded.serverAddress,
@@ -95,7 +112,8 @@ export function createDatabase(dbPath: string): BotDatabase {
channelPassword = excluded.channelPassword,
autoStart = excluded.autoStart,
serverProtocol = excluded.serverProtocol,
ts6ApiKey = excluded.ts6ApiKey
ts6ApiKey = excluded.ts6ApiKey,
identity = excluded.identity
`);
const selectInstances = db.prepare(`SELECT * FROM bot_instances`);
@@ -117,14 +135,21 @@ export function createDatabase(dbPath: string): BotDatabase {
upsertInstance.run({
...instance,
autoStart: instance.autoStart ? 1 : 0,
identity: instance.identity ?? null,
});
},
getBotInstances() {
const rows = selectInstances.all() as Array<
Omit<BotInstance, "autoStart"> & { autoStart: number }
Omit<BotInstance, "autoStart" | "identity"> & { autoStart: number; identity: string | null }
>;
return rows.map((r) => ({ ...r, autoStart: r.autoStart === 1 }));
return rows.map((r) => ({
...r,
autoStart: r.autoStart === 1,
serverProtocol: r.serverProtocol ?? "",
ts6ApiKey: r.ts6ApiKey ?? "",
identity: r.identity ?? undefined,
}));
},
deleteBotInstance(id) {