feat(server): capability framework, AudioMuse sonic routing & PsyLab Connections (#1033)

* feat(server): probe AudioMuse via OpenSubsonic and add PsyLab Connections tab

Navidrome ≥0.62: detect sonicSimilarity extension for reliable plugin signal;
older servers keep the legacy getSimilarSongs probe. PsyLab gets a Connections
tab with session, endpoint, and active-server capability details.

* feat(psylab): polish Connections tab, admin role probe, and tab bar layout

Status badges and Navidrome admin/user validation in Connections; prevent
PsyLab tab row from vertically collapsing under the Logs flex layout.

* docs: add CHANGELOG and credits for PR #1032

* feat(settings): auto-enable AudioMuse on Navidrome 0.62+ with status indicator

Replace the per-server manual toggle with a probe-driven badge when
sonicSimilarity is available; pre-0.62 Navidrome keeps the legacy toggle.

* feat(server): capability framework with AudioMuse sonic routing

Add a declarative server-capability catalog (src/serverCapabilities/) that
picks a feature strategy per server generation, runs only the needed probes,
and routes API calls. AudioMuse Instant Mix now prefers the OpenSubsonic
sonicSimilarity endpoint (getSonicSimilarTracks) on Navidrome 0.62+ and
falls back to legacy getSimilarSongs.

- catalog/context/resolve: eligibility, detection, activation, call routing
- storeView: read facade over the existing per-server probe maps
- getSonicSimilarTracks API client + fetchSimilarTracksRouted router
- route Instant Mix and Lucky Mix through the resolver
- ServersTab + PsyLab Connections read the resolver (auto status vs toggle)
- tests: resolve, storeView, router

* docs: update CHANGELOG and credits for PR #1033

Renamed branch supersedes PR #1032: point changelog/credits at #1033 and
document the server-capability framework, auto-managed AudioMuse indicator,
and sonic Instant Mix routing.

* refactor(server): address PR #1033 review — idempotent probe, drop dead code

- Make scheduleInstantMixProbeForServer idempotent: skip when a definitive
  result is cached; re-probe only on force (add/edit/test server), a prior
  error, or a server version/type change (invalidated in setSubsonicServerIdentity).
  Removes the steady-state 120 s re-probe, the present→probing→present flicker,
  and the momentary legacy-fallback routing window.
- Remove now-dead identity helpers (showAudiomuseNavidromeServerSetting,
  isAudiomusePluginAutoManaged, isNavidromeSonicSimilarityEligible,
  resolveAudiomusePluginProbeUiStatus + type) and the superseded
  probeAudiomusePluginWithCredentials; the catalog is the single source of truth.
- Drop the never-emitted 'unsupported' AudiomusePluginProbeResult variant.
- Fill audiomuseStatus* keys in all 8 non-en locales.
- Tests: probe idempotency + version-change invalidation; retarget
  OpenSubsonic test to fetchOpenSubsonicExtensionsWithCredentials.
This commit is contained in:
cucadmuh
2026-06-09 00:16:29 +03:00
committed by GitHub
parent 0878cbf308
commit 6118b3940f
43 changed files with 1867 additions and 74 deletions
+134 -2
View File
@@ -95,21 +95,28 @@
.sidebar-perf-modal__tabs {
display: flex;
gap: 6px;
flex: 0 0 auto;
flex-shrink: 0;
gap: 4px;
margin: 12px 0 10px;
padding: 4px;
border-radius: 12px;
background: color-mix(in srgb, var(--bg-card, var(--bg-app)) 88%, transparent);
border: 1px solid color-mix(in srgb, var(--text-muted) 18%, transparent);
overflow-x: auto;
scrollbar-width: thin;
}
.sidebar-perf-modal__tab {
flex: 1;
flex: 0 0 auto;
min-width: 4.5rem;
min-height: 2.25rem;
display: inline-flex;
align-items: center;
justify-content: center;
gap: 6px;
padding: 8px 10px;
white-space: nowrap;
border: none;
border-radius: 9px;
background: transparent;
@@ -312,6 +319,130 @@
font-size: 11px;
}
.perf-metric-section__body {
padding: 0 10px 10px;
}
.perf-conn-summary {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin-bottom: 12px;
padding: 10px;
border-radius: 12px;
border: 1px solid color-mix(in srgb, var(--text-muted) 16%, transparent);
background: color-mix(in srgb, var(--bg-card, var(--bg-app)) 88%, transparent);
}
.perf-conn-summary__item {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 4px;
min-width: 88px;
flex: 1 1 auto;
}
.perf-conn-summary__label {
font-size: 10px;
font-weight: 600;
letter-spacing: 0.05em;
text-transform: uppercase;
color: color-mix(in srgb, var(--text-muted) 90%, transparent);
}
.perf-status-badge {
display: inline-flex;
align-items: center;
gap: 4px;
padding: 2px 8px;
border-radius: 999px;
font-size: 11px;
font-weight: 600;
line-height: 1.35;
border: 1px solid transparent;
white-space: nowrap;
}
.perf-status-badge--ok {
color: #9ece6a;
background: color-mix(in srgb, #9ece6a 14%, transparent);
border-color: color-mix(in srgb, #9ece6a 28%, transparent);
}
.perf-status-badge--warn {
color: #e0af68;
background: color-mix(in srgb, #e0af68 14%, transparent);
border-color: color-mix(in srgb, #e0af68 28%, transparent);
}
.perf-status-badge--error {
color: #f7768e;
background: color-mix(in srgb, #f7768e 14%, transparent);
border-color: color-mix(in srgb, #f7768e 28%, transparent);
}
.perf-status-badge--neutral {
color: #7aa2f7;
background: color-mix(in srgb, #7aa2f7 14%, transparent);
border-color: color-mix(in srgb, #7aa2f7 28%, transparent);
}
.perf-status-badge--muted {
color: var(--text-muted);
background: color-mix(in srgb, var(--text-muted) 10%, transparent);
border-color: color-mix(in srgb, var(--text-muted) 20%, transparent);
}
.perf-server-dl {
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
gap: 0;
font-size: 12px;
border-radius: 10px;
border: 1px solid color-mix(in srgb, var(--text-muted) 12%, transparent);
overflow: hidden;
}
.perf-server-dl__row {
display: grid;
grid-template-columns: minmax(110px, 38%) 1fr;
gap: 8px 12px;
align-items: center;
padding: 8px 10px;
border-top: 1px solid color-mix(in srgb, var(--text-muted) 10%, transparent);
}
.perf-server-dl__row:first-child {
border-top: none;
}
.perf-server-dl__row:nth-child(even) {
background: color-mix(in srgb, var(--bg-sidebar, var(--bg-app)) 40%, transparent);
}
.perf-server-dl__row dt {
margin: 0;
color: var(--text-muted);
font-weight: 500;
font-size: 11px;
}
.perf-server-dl__row dd {
margin: 0;
color: var(--text-primary, var(--text));
word-break: break-word;
}
.perf-server-dl__code {
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
font-size: 11px;
color: color-mix(in srgb, var(--text-primary, var(--text)) 92%, var(--accent));
word-break: break-all;
}
.perf-live-poll {
margin-bottom: 12px;
padding: 10px 12px;
@@ -907,6 +1038,7 @@
}
.sidebar-perf-modal__actions {
flex-shrink: 0;
margin-top: 14px;
display: flex;
justify-content: flex-end;