Files
teamspeak-music-bot/src/web/api/auth.ts
T
saopig1andClaude Opus 4.6 a6881403ef feat: add BiliBili audio source integration
Implement BiliBiliProvider for video-as-audio playback using direct
BiliBili API calls (search, video info, DASH audio URL extraction).
Add QR code login support and cookie persistence. Update FFmpeg to
send Referer header for BiliBili CDN URLs. Extend platform union type
to "netease" | "qq" | "bilibili" across all interfaces. Add -b flag
for chat commands and B站 badge in web UI.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 15:14:27 +08:00

134 lines
4.2 KiB
TypeScript

import { Router } from "express";
import type { MusicProvider } from "../../music/provider.js";
import type { CookieStore } from "../../music/auth.js";
import type { Logger } from "../../logger.js";
export function createAuthRouter(
neteaseProvider: MusicProvider,
qqProvider: MusicProvider,
bilibiliProvider: MusicProvider,
logger: Logger,
cookieStore?: CookieStore
): Router {
const router = Router();
function getProvider(platform?: string): MusicProvider {
if (platform === "bilibili") return bilibiliProvider;
return platform === "qq" ? qqProvider : neteaseProvider;
}
router.get("/status", async (req, res) => {
try {
const platform = req.query.platform as string;
const provider = getProvider(platform);
const status = await provider.getAuthStatus();
logger.debug({ platform, status }, "Auth status check");
res.json({ platform: provider.platform, ...status });
} catch (err) {
logger.error({ err }, "Auth status check failed");
res.status(500).json({ error: (err as Error).message });
}
});
router.post("/qrcode", async (req, res) => {
try {
const { platform } = req.body;
const provider = getProvider(platform);
const qr = await provider.getQrCode();
logger.info({ platform, key: qr.key }, "QR code generated");
res.json(qr);
} catch (err) {
logger.error({ err }, "QR code generation failed");
res.status(500).json({ error: (err as Error).message });
}
});
router.get("/qrcode/status", async (req, res) => {
try {
const { key, platform } = req.query;
if (!key) {
res.status(400).json({ error: "key is required" });
return;
}
const provider = getProvider(platform as string);
const status = await provider.checkQrCodeStatus(key as string);
logger.info({ platform, status, key }, "QR status check");
// When confirmed, persist cookie
if (status === "confirmed") {
const cookie = provider.getCookie();
const plat = (platform as string) === "bilibili" ? "bilibili" as const
: (platform as string) === "qq" ? "qq" as const : "netease" as const;
if (cookie && cookieStore) {
cookieStore.save(plat, cookie);
logger.info({ platform: plat }, "Cookie persisted to disk");
}
}
res.json({ status });
} catch (err) {
logger.error({ err }, "QR status check failed");
res.status(500).json({ error: (err as Error).message });
}
});
router.post("/sms/send", async (req, res) => {
try {
const { phone } = req.body;
if (!phone) {
res.status(400).json({ error: "phone is required" });
return;
}
if (!neteaseProvider.sendSmsCode) {
res
.status(400)
.json({ error: "SMS login not supported for this platform" });
return;
}
const success = await neteaseProvider.sendSmsCode(phone);
res.json({ success });
} catch (err) {
res.status(500).json({ error: (err as Error).message });
}
});
router.post("/sms/verify", async (req, res) => {
try {
const { phone, code } = req.body;
if (!phone || !code) {
res.status(400).json({ error: "phone and code are required" });
return;
}
if (!neteaseProvider.loginWithSms) {
res.status(400).json({ error: "SMS login not supported" });
return;
}
const success = await neteaseProvider.loginWithSms(phone, code);
if (success && cookieStore) {
cookieStore.save("netease", neteaseProvider.getCookie());
}
res.json({ success });
} catch (err) {
res.status(500).json({ error: (err as Error).message });
}
});
router.post("/cookie", (req, res) => {
const { platform, cookie } = req.body;
if (!cookie) {
res.status(400).json({ error: "cookie is required" });
return;
}
const provider = getProvider(platform);
provider.setCookie(cookie);
const plat = platform === "bilibili" ? "bilibili" as const
: platform === "qq" ? "qq" as const : "netease" as const;
if (cookieStore) {
cookieStore.save(plat, cookie);
}
res.json({ success: true });
});
return router;
}