Make the default playback source a user-configurable setting so servers
that mostly play e.g. Bilibili no longer need to type `-b` on every
`!play`. Previously defaultPlatform() always picked the first enabled
provider by a fixed priority order, with no way to override it.
- config: add optional `defaultPlatform: GateableProvider | null`.
loadConfig sanitizes it — kept only when it names a known provider that
is also currently enabled, else null. defaultPlatform() returns the
preference when enabled, otherwise falls back to the fixed priority order.
- POST /api/bot/settings accepts `defaultPlatform` (validated against the
possibly-updated enabledProviders; null/"" clears it), and reconciles a
stored default that a new enabledProviders list no longer allows. GET and
POST responses expose the field.
- WebUI: new "默认音源" section with a source picker; saving refreshes the
store's default source so it takes effect immediately without a restart.
- Tests: extend config defaultPlatform priority tests and add coverage for
the settings endpoint and /providers routing.
- README: document `defaultPlatform` in the enabledProviders section.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Revert the jellyfin-only default introduced by PR #123 so upgrading
users keep their online sources; Jellyfin becomes opt-in:
- default enabledProviders is now the online set (netease/qq/bilibili/
youtube/kugou); defaultPlatform() uses a fixed priority order
(netease -> qq -> kugou -> jellyfin -> bilibili -> youtube) instead
of jellyfin-first, so chat/REST/WebUI default to netease again
- Settings: Jellyfin card is always visible with a new enable toggle
(its enabled bit is enabledProviders membership); guards against
clobbering other providers before the list loads
- Setup wizard: saving the Jellyfin step auto-enables the source when
a server URL was entered
- Search/player store fallbacks flip from jellyfin to netease; !help
no longer hardcodes Jellyfin lines
- tests: update default-platform assertions, add coverage for the new
default set, legacy configs without enabledProviders, priority
order, and explicit jellyfin-only configs
- README: reframe Jellyfin as optional (badges, command table, quality
tiers, dedicated section, changelog), document the enabledProviders
default and the v1.10.0 jellyfin-only window fix, credit @ItsEricRao
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Conflict resolution + cross-PR integration:
- player.ts: kept #88's POST /:botId/fm route AND gated it with
requirePermission('player.control') so the new control endpoint honors #80's
permission model (it was added without gating).
- bot.ts: kept #81's relocated /settings routes (the relocation fixes the GET
/settings shadow bug) and dropped #80's now-duplicate bottom copy; gated
POST /settings with requirePermission('bot.manage').
- Navbar.vue: composed #82's dedicated-link scope with #80's permission filter —
displayedBots is now the INTERSECTION (scope ∩ controllable allow-list).
- database.ts: kept BOTH new table sets (#87 favorite_playlists + #80
user_permissions/user_bot_access).
- bot.test.ts: updated to createRequireAuth(sessions, permissions) for #80's new
two-arg signature.
#80 review fixes (credential exposure / IDOR, adversarially verified):
- GET /:id/config now requires bot.manage + bot access AND redacts ts6ApiKey +
identity from the response (was readable by any authenticated member).
- GET /:id and GET /:id/avatar now require bot access (were ungated read oracles).