VIP songs for non-VIP accounts return a ~30s trial fragment. The player used the full duration for isNearEnd, so the trial end didn't trigger auto-advance (~60s stall), and currentSong.duration stayed full, leaving the UI progress stuck.
- provider.ts: SongUrlResult {url, trialDuration?}; getSongUrl signature
- netease.ts: parseNeteaseTrial (freeTrialInfo start/end in seconds) + getSongUrl
- qq.ts: parseQqTrial (isTryout/tryEnd) + getSongUrl
- bilibili/youtube: getSongUrl returns {url}
- instance.ts: resolveAndPlay uses effectiveDuration = trialDuration ?? duration -> nearEnd at trial end -> native auto-advance; BotStatus.effectiveDuration
- VIP account: freeTrialInfo absent -> full duration -> full playback (no toggle)
Backward compatible (optional fields; getSongUrl has a single caller, updated).
Tests: parseTrial assertions (seconds/alias/ms-fallback). 14 pass.
Closes#64. Bilibili moved the unsigned /x/web-interface/search/type endpoint
behind their anti-bot wall; it now returns an HTML error page (出错啦!) even
with buvid3+buvid4 cookies, causing `play -b` to report "No results found".
Switch search() to /x/web-interface/wbi/search/type with proper wbi signing:
fetch img_key/sub_key from /nav, derive the mixin key via the standard
permutation, and sign each request with wts + w_rid (md5). Keys are cached
for 6h since they rotate ~daily. Other endpoints (view, playurl, popular,
top/rcmd) still work unsigned and are left unchanged.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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>