mirror of
https://github.com/ZHANGTIANYAO1/teamspeak-music-bot.git
synced 2026-10-01 20:42:50 +08:00
fix: corner-case audit — distinguish failures, recover from blips
Three small but real correctness fixes from auditing recent commits: 1. getPlayableSongIds returned an empty Set for both "endpoint failed" and "succeeded but all unplayable" — the caller couldn't tell which. Return Set | null now: null = error (fall through to sequential retry), empty Set = authoritative "all unplayable" (short-circuit to a clear message instead of wasting 20+ retries). 2. Web store: fetchHomeData unconditionally wrote lastFetchTime even when every fetch rejected (network blip, server down). That cached the failure for 5 minutes — user had to hard-reload to recover. Now only commit lastFetchTime if at least one auth-status call succeeded. 3. Settings profile section: if GET /profile failed, profileConfigs stayed undefined and the row showed "加载中..." forever. Track a per-bot error state and render an inline "加载失败 / 重试" link so the user can recover without page reload. Out of scope but documented: - ein=29 hardcoded in fetchCollectedPlaylists (no pagination yet — users with 30+ collected QQ playlists get truncated). - /play-song single-failure UX (returns "Cannot play" message but frontend ignores; needs a global toast/notification primitive). - NetEase has no analogous batch precheck (could surface same "click and wait silent" issue if user has region-restricted NetEase playlists). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
3d0aca52d0
commit
892d9f7959
4 files changed
+57
-15
No files matched your search
@@ -255,15 +255,17 @@ export function createPlayerRouter(
|
||||
// ones, otherwise the playback retry loop wastes time guessing.
|
||||
let queueable: { id: string }[] = songs;
|
||||
const totalCount = songs.length;
|
||||
const qqLike = provider as { getPlayableSongIds?: (ids: string[]) => Promise<Set<string>> };
|
||||
const qqLike = provider as { getPlayableSongIds?: (ids: string[]) => Promise<Set<string> | null> };
|
||||
if (typeof qqLike.getPlayableSongIds === "function") {
|
||||
const playable = await qqLike.getPlayableSongIds(songs.map((s: { id: string }) => s.id));
|
||||
if (playable.size > 0) {
|
||||
if (playable !== null) {
|
||||
// Authoritative answer from upstream — even an empty set means
|
||||
// "we know none are playable", short-circuit immediately rather
|
||||
// than wasting 20+ retries.
|
||||
queueable = songs.filter((s: { id: string }) => playable.has(s.id));
|
||||
}
|
||||
// If batch returned nothing, leave queueable as-is and let the
|
||||
// sequential retry path try anyway (handles the case where the
|
||||
// batch endpoint failed entirely).
|
||||
// If null, the batch endpoint itself errored — fall through to
|
||||
// the sequential retry path, which still has a chance.
|
||||
}
|
||||
if (queueable.length === 0) {
|
||||
res.json({ message: `Loaded ${totalCount} songs but none were playable (likely copyright/region restrictions).` });
|
||||
|
||||
Reference in new issue
Block a user