Commit Graph
157 Commits
Author SHA1 Message Date
Claude 6184f38330 Fix 7 corner cases found in second review pass
Critical:
- Persist serverProtocol/ts6ApiKey in database schema so TS6 config
  survives restarts (added columns + manager save/load)

Medium:
- Clear udpErrorTimer on disconnect to prevent memory leak and stale
  log messages from firing after teardown
- Guard against double connect() by disconnecting old client first
- Add settled guard in TS6HttpQuery.request() to prevent double
  reject when both res error and req error fire
- Add res.on("error") handler to TS6HttpQuery response stream

Low:
- Cap probeTS3Query banner buffer at 256 bytes to prevent memory abuse
  from non-TS3 services sending large data on port 10011
- Remove unnecessary EventEmitter inheritance from TS6HttpQuery
- Update database test fixtures with new serverProtocol/ts6ApiKey fields

https://claude.ai/code/session_016WhH58avUD9xy2dgADJgTh
2026-04-03 13:38:49 +00:00
Claude 9e45193e2c Fix corner cases in protocol detection and connection cleanup
- Fix double-resolve race in probeTS3Query (data event vs connect timer)
  by guarding with a resolved flag
- Fix double-resolve in probeTS6HttpQuery similarly
- Support custom query ports in detectServerProtocol via DetectOptions
- Clean up httpQuery and detectedProtocol on disconnect()
- Add res.on("error") handler in HTTP Query client to avoid unhandled errors
- Improve logging: warn with actionable message when protocol is unknown

https://claude.ai/code/session_016WhH58avUD9xy2dgADJgTh
2026-04-03 13:34:37 +00:00
Claude 9e51ba6cd4 Add dual TS3/TS6 protocol support with auto-detection
The @honeybbq/teamspeak-client library already handles TS6 license block
type 8 (Ts5Server) in its handshake, so voice connections work with both
TS3 and TS6 servers. This commit adds the surrounding infrastructure:

- protocol-detect.ts: Auto-detect server type by probing TS3 ServerQuery
  (port 10011) and TS6 HTTP Query (port 10080) in parallel
- http-query.ts: TS6 HTTP Query client replacing the raw-TCP ServerQuery
  that TS6 servers no longer support (ports 10080/10443)
- client.ts: Protocol-aware connection with auto-detection, TS6 HTTP
  Query setup, and forced protocol override option
- manager.ts: Pass through serverProtocol and ts6ApiKey config options
- connection.ts: Mark legacy TS3 ServerQuery as deprecated for TS6

https://claude.ai/code/session_016WhH58avUD9xy2dgADJgTh
2026-04-03 13:03:47 +00:00
Claude 1c88dd7a35 Add comprehensive error handling to prevent process crashes
- Global uncaughtException/unhandledRejection handlers in index.ts
- FFmpeg stdout/stderr stream error handlers in player.ts
- HTTP server and WebSocket server error handlers in server.ts
- Safe WebSocket broadcast with try-catch in websocket.ts
- Catch async errors from textMessage handler in instance.ts
- Reset voiceFramesSent counter on reconnect in client.ts

https://claude.ai/code/session_01EjpEsC2GCsvwbu4n3XC8EE
2026-03-31 16:58:14 +00:00
Claude 168f9f0286 Fix EINTR crash from pino ThreadStream during FFmpeg playback
The pino logger's ThreadStream was throwing unhandled EINTR errors
when FFmpeg child processes were spawned, crashing the entire bot.
Add an error handler to suppress EINTR and prevent process crashes.

https://claude.ai/code/session_01EjpEsC2GCsvwbu4n3XC8EE
2026-03-31 16:52:53 +00:00
Claude 1993f84fad Fix FFmpeg binary resolution: test binary actually works before using it
The ffmpeg-static bundled binary was crashing immediately (exitCode: null,
no data produced). Now we run `ffmpeg -version` to verify the binary works
before selecting it, with automatic fallback to system ffmpeg.

Also logs ffmpeg binary path at info level and captures signal in close event.

https://claude.ai/code/session_01EjpEsC2GCsvwbu4n3XC8EE
2026-03-31 16:40:38 +00:00
Claude 1a880ef20b Add diagnostic logging for playback pipeline debugging
Adds info-level logs at each stage of the audio pipeline to help
diagnose why playback produces no audible output:
- FFmpeg first PCM data received
- FFmpeg process exit code
- FFmpeg stderr (errors/HTTP/stream info at info level)
- First opus frame encoded and emitted
- First voice packet sent to TeamSpeak
- Error catching in frame encoding loop

https://claude.ai/code/session_01EjpEsC2GCsvwbu4n3XC8EE
2026-03-31 16:30:53 +00:00
Claude 9e52b0bda9 refactor: cache resolved ffmpeg path at module load time
Avoid repeated filesystem checks on every play() call by resolving
the ffmpeg binary path once at module load. Also verify execute
permission after chmod to handle noexec mounts.

https://claude.ai/code/session_01CqfKgV8GuCmWNpfx86H62X
2026-03-31 16:02:00 +00:00
Claude 58caff3209 fix: auto-fix ffmpeg binary execute permission on startup
The bundled ffmpeg-static binary may lose execute permission after npm
install on some platforms, causing EACCES errors during playback. Now
checks and fixes the permission automatically before spawning ffmpeg.

https://claude.ai/code/session_01CqfKgV8GuCmWNpfx86H62X
2026-03-31 15:58:54 +00:00
saopig1andClaude Opus 4.6 43004fc44a fix: 停止机器人后UI消失 & UDP send error日志刷屏
- botDisconnected事件改为更新状态而非删除,停止机器人后正确显示离线状态
- 节流UDP send error日志,2秒内重复错误只输出一条汇总

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 21:50:04 +08:00
saopig1andClaude Opus 4.6 d0d2f57066 fix: 自动连接改为非阻塞并行 — 一个bot连接失败不影响其他bot
之前 loadSavedBots 用 await 顺序连接所有bot,一个超时会阻塞
后续所有bot和web server启动。改为 .then()/.catch() 并行连接,
每个bot独立成败互不影响。

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 21:33:32 +08:00
saopig1andClaude Opus 4.6 e466f5cd61 fix: NetEase API端口冲突检查,Settings页面create-bot样式修复
- NetEase API启动前检查端口是否被占用(与QQ API相同逻辑)
- 添加.create-bot CSS(分隔线+间距)
- 防止EADDRINUSE崩溃

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 21:13:11 +08:00
saopig1andClaude Opus 4.6 4dbbaaf8f5 fix: 删除bot后重置activeBotId,编辑立即更新名称,config fetch await防空值
1. 删除活跃bot后将activeBotId置null,由fetchBots自动选新的
2. updateBot同时更新内存中的name(无需重启即可看到名称变化)
3. openEditBot改为async await config请求,失败时用bot名称做fallback

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 20:54:33 +08:00
saopig1andClaude Opus 4.6 067a30233f feat: 机器人管理 — 删除、编辑配置、完整创建表单
- PUT /api/bot/:id 更新机器人配置(保存到数据库,重启生效)
- GET /api/bot/:id/config 获取保存的配置
- BotManager.updateBot/getBotConfig 方法
- Settings 页面:删除按钮(带确认)、编辑弹窗(修改所有字段)
- 创建表单:完整字段(名称、地址、端口、昵称、默认频道)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 20:50:49 +08:00
saopig1andClaude Opus 4.6 223ba45e65 feat: 启动时自动连接所有已保存的机器人实例
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 20:28:43 +08:00
saopig1andClaude Opus 4.6 fbf8cc82de fix: cidCache 大小限制为500条,防止内存泄漏
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 20:20:42 +08:00
saopig1andClaude Opus 4.6 cf293299c8 feat: B站首页推荐 — 个性化推荐/音乐区排行/热门视频
- BiliBiliProvider.getDailyRecommendSongs: 个性化推荐(登录后更准确),
  fallback 到音乐区排行榜
- BiliBiliProvider.getPopularVideos: 热门视频列表
- 新增 GET /api/music/bilibili/popular 端点
- 首页新增"B站热门"板块,带B站蓝色徽标
- Store 缓存 bilibiliPopular 数据

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 20:12:06 +08:00
saopig1andClaude Opus 4.6 960ca756a6 fix: 使用SPI API获取buvid3替代访问主页 — 可靠解决412问题
之前通过axios访问bilibili.com主页获取cookie失败(axios不解析set-cookie)。
改用专用API: /x/frontend/finger/spi 直接获取buvid3和buvid4值,
构造cookie字符串传入搜索请求。已验证搜索正常返回结果和封面URL。

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 19:54:21 +08:00
saopig1andClaude Opus 4.6 dcec26feb1 fix: B站搜索412错误 — 自动获取buvid3匿名Cookie绕过反爬
B站搜索API强制要求buvid3 cookie,否则返回412。
Provider启动时自动从bilibili.com获取buvid3/b_nut cookie,
与用户登录cookie合并发送。

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 19:46:51 +08:00
saopig1andClaude Opus 4.6 341faffdbb fix: B站封面 — http转https防止mixed content,CDN参数@300w_300h_1c正方形裁切
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 19:40:07 +08:00
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
saopig1andClaude Opus 4.6 eb0088d110 fix: edge case bugs + toggle lyrics from player bar and lyrics button
Bugs fixed:
- play-at: stop current playback before jumping to queue index
- playNext retry: use break instead of return to ensure stateChange emits
- sendVoiceData: skip if disconnecting to avoid errors during teardown

UI: player-left and lyrics button now toggle lyrics page (open/close)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 14:50:14 +08:00
saopig1andClaude Opus 4.6 d8c0911593 feat: 双击播放队列中的歌曲(不清空队列)
- 新增 POST /api/player/:botId/play-at 端点,跳转到队列指定索引
- 使用 queue.playAt(index) + resolveAndPlay,保留队列完整
- Queue.vue 添加 @dblclick 处理,双击即播放对应歌曲
- 添加 store.playAtIndex() action

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 14:37:53 +08:00
saopig1andClaude Opus 4.6 17991b936a fix: resolve critical race conditions and high-severity bugs
- Add isAdvancing guard to playNext() to prevent concurrent calls from
  skipping songs (trackEnd + user !next race condition)
- Replace removeAllListeners in WebSocket setup with tracked named
  listeners, attach only once per bot instead of every 5 seconds
- Add .catch() to unhandled playNext() in cmdVote
- Add sessionId counter to AudioPlayer to discard stale setTimeout
  callbacks after stop()+play() transitions
- Defer nulling TS3 client until disconnect() promise resolves
- Add null checks for providers in play-by-id and add-by-id endpoints
- Add PCM buffer backpressure (pause FFmpeg at 960KB, resume at 480KB)
- Change volume slider from @input to @change to avoid flooding server
- Wrap error-reporting sendTextMessage in try/catch to prevent
  unhandled rejection in error handler
- Clamp elapsed getter to song duration
- Log FFmpeg stderr at debug level instead of swallowing
- Return null from prev() in Sequential mode instead of wrapping

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 14:31:03 +08:00
saopig1andClaude Opus 4.6 4c71a0a37f fix: auto-play next song — set ffmpeg=null on close, handle async playNext
Two bugs prevented auto-advance:
1. FFmpeg close handler was empty — ChildProcess object persisted after
   close, so frame loop check `!this.ffmpeg` never triggered trackEnd
2. playNext() is async (resolves URL) but trackEnd handler didn't catch
   rejected promises, causing silent failures

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 14:14:56 +08:00
saopig1andClaude Opus 4.6 61d9bbe0b7 feat: bundle FFmpeg via ffmpeg-static for one-click deployment
Users no longer need to install FFmpeg separately — it is now bundled as
an npm dependency (ffmpeg-static). player.ts resolves the bundled binary
automatically and falls back to system PATH. start.bat and a new
setup.bat script handle dependency installation and project building.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 03:43:10 +08:00
saopig1andClaude Opus 4.6 b74c8e8add feat: add audio quality selector (standard to jymaster/Hi-Res)
- MusicProvider interface: setQuality/getQuality methods
- NeteaseProvider: passes quality level to /song/url/v1
- Default: exhigh (320kbps MP3)
- Settings page: 6-option grid selector
- GET/POST /api/music/quality endpoints

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 03:37:26 +08:00
saopig1andClaude Opus 4.6 0439bd047f fix: server-side elapsed tracking via frame count, periodic sync, playlist respects play mode
Progress/lyrics sync:
- AudioPlayer tracks framesPlayed (ground truth: frames * 20ms + seekOffset)
- BotStatus.elapsed is the real elapsed from server
- Frontend polls GET /elapsed every 3s for ground truth
- Client interpolates between syncs (serverElapsed + timeSinceSync)
- Pause freezes at interpolated value, resume resyncs from server
- No more client-side timestamp drift

Playlist play all:
- Stops current playback before loading
- Respects play mode: random/rloop picks random first song, seq plays first

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 03:27:16 +08:00
saopig1andClaude Opus 4.6 1cdface33c feat: implement seeking (FFmpeg -ss restart), fix lyrics sync with seekOffset
- AudioPlayer.seek() restarts FFmpeg with -ss parameter for actual seeking
- New POST /api/player/:botId/seek endpoint
- BotStatus includes seekOffset and playStartTime from server
- Frontend elapsed = seekOffset + (now - playStartedAt)
- Progress bar click now seeks to clicked position
- All timing actions use _resetTiming() helper with seekOffset
- Lyrics sync uses store.elapsed which includes seekOffset

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 03:16:37 +08:00
saopig1andClaude Opus 4.6 080c2836c9 refactor: lazy URL resolution — store only metadata, fetch URL at play time
Follows TS3AudioBot-NetEaseCloudmusic-plugin pattern:
- QueuedSong.url is now optional (metadata-only queue entries)
- resolveAndPlay() fetches URL on-demand right before playback
- Playlist/album/FM load instantly (only metadata stored)
- playNext() resolves URL lazily, skips up to 3 songs on failure
- Avoids expired URLs for songs deep in the queue

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 03:05:40 +08:00
saopig1andClaude Opus 4.6 2e4d6d8d40 feat: play-by-id and play-playlist endpoints, fix lyrics sync timing
- New endpoints: play-by-id, add-by-id, play-playlist (load all songs at once)
- Playlist 'Play All' now uses server-side bulk load (no N sequential searches)
- Search/Home/Playlist views use playById instead of search-by-name
- Fixed updateBotStatus timing: capture prev state before mutation
- Early return on song change prevents stale pause/resume logic

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 02:55:15 +08:00
saopig1andClaude Opus 4.6 dc9d181783 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>
2026-03-30 02:40:02 +08:00
saopig1andClaude Opus 4.6 94a0df8391 feat: add QQ Music API integration, Pinia home cache, and unified search
- Start @sansenjian/qq-music-api server via dynamic import in api-server.ts
- Rewrite QQ Music provider to use real API endpoints (getSearchByKey,
  getMusicPlay, getSongListDetail, getAlbumInfo, getLyric, etc.)
- Cache home page data (recommend playlists, daily songs, user playlists)
  in Pinia store with 5-minute TTL to avoid refetching on every mount
- Add unified /api/music/search/all endpoint that queries both Netease
  and QQ providers in parallel and returns merged results
- Remove platform toggle from Search.vue; search both platforms at once
- Add platform badge (网易云 / QQ) to SongCard component

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 02:31:15 +08:00
saopig1andClaude Opus 4.6 ee5a75ab5e fix: add play mode toggle, wire play history API, add back navigation buttons
- Add play mode cycle button (seq/loop/random/rloop) to Player bar
- Wire GET /api/player/:botId/history to query database instead of returning empty array
- Pass database instance from server.ts to createPlayerRouter
- Add back navigation button to Playlist, Lyrics, History, Search, and Settings views
- SongCard already had dblclick-to-play (no change needed)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 02:16:18 +08:00
saopig1andClaude Opus 4.6 67584d2622 feat: add YesPlayMusic-style home page with daily recs, FM, and user playlists
Add backend endpoints for daily recommended songs, personal FM, user
playlists, and playlist detail. Extend NeteaseProvider and MusicProvider
interface with getDailyRecommendSongs and getUserPlaylists. Rewrite Home
page with FM card, daily recommendations grid, and user playlists section.
Fix Playlist view to fetch detail and songs from separate endpoints.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 02:07:11 +08:00
saopig1andClaude Opus 4.6 6b725f43f9 fix: replace setInterval with drift-correcting setTimeout for audio timing
- High-precision frame loop using performance.now() tracking
- PCM-level volume control (live adjustable, no FFmpeg restart)
- Proper buffer drain on track end
- Fixes audio quality and playback speed issues

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 02:01:16 +08:00
saopig1andClaude Opus 4.6 9755ba2909 fix: QR login now persists cookies, shows server-generated QR image, passes timestamp
- Auth router accepts CookieStore and persists cookies on login
- NeteaseProvider passes timestamp to prevent cached QR responses
- QR image from server used directly (qrimg field)
- Cookie saved to disk on confirmed QR scan

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 01:47:25 +08:00
saopig1andClaude Opus 4.6 f77a6f0fd7 fix: start NeteaseCloudMusicApi server for real — enables QR code login
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 01:37:35 +08:00
saopig1andClaude Opus 4.6 0559f09f0a feat: replace ServerQuery with full TS3 client protocol — bot is now VISIBLE
Switch from TCP ServerQuery (port 10011, invisible) to real TS3 client
protocol via @honeybbq/teamspeak-client (UDP 9987, ECDH handshake,
AES-EAX encryption). Bot now appears as a regular client visible to
all users in TeamSpeak.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 01:28:25 +08:00
saopig1andClaude Opus 4.6 df7b944742 test: add integration test suite — 74 tests passing against local TS3 server
Fix @discordjs/opus ESM import for Node 24 compatibility.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 01:08:57 +08:00
saopig1andClaude Opus 4.6 2a966e914c fix: address Phase 6 review — error handling on player routes, SPA fallback scope, WS cleanup
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 00:49:34 +08:00
saopig1andClaude Opus 4.6 0b54809113 feat: add web backend — Express REST API, WebSocket, full wiring in entry point
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 00:47:19 +08:00
saopig1andClaude Opus 4.6 e5d5185f14 fix: address Phase 5 review — use strict equality check in cmdMode
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 00:45:14 +08:00
saopig1andClaude Opus 4.6 6692f49a85 feat: add bot core — command parser, BotInstance with full commands, BotManager
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 00:43:41 +08:00
saopig1andClaude Opus 4.6 fbc4cbe4d6 fix: address Phase 4 review — cookie file permissions (0600), gitignore cookies dir
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 00:41:24 +08:00
saopig1andClaude Opus 4.6 94902fc2c4 feat: add music source service — NetEase/QQ providers, unified interface, cookie auth
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 00:39:47 +08:00
saopig1andClaude Opus 4.6 17d2decc91 fix: address Phase 3 review — Logger type export, PlayerEvents interface, PCM backpressure
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 00:37:16 +08:00
saopig1andClaude Opus 4.6 5afc8c5224 feat: add audio engine — Opus encoder, play queue with 4 modes, FFmpeg player
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 00:34:57 +08:00
saopig1andClaude Opus 4.6 d66355a829 fix: address Phase 2 review — error handlers, drain queue, prevent listener accumulation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 00:32:22 +08:00
saopig1andClaude Opus 4.6 a6bfd9d32c feat: add TCP connection, UDP voice, and high-level TS3Client
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 00:29:47 +08:00