The admin Activity page could not name the build a session was streaming from. Android already sent X-Silo-Client-Version and the server already stored it intact, but playbackClientDisplayName routed it through shortPlaybackClientVersion, which strips non-numeric runes, truncates to two components, and drops a trailing ".0" — so a client reporting "1.0.0" rendered as "Silo Android TV 1". The Apple clients sent no client name or version at all and fell back to user-agent sniffing. Adds two additive, opaque wire fields alongside the existing client headers — X-Silo-Client-Build (<=64) and X-Silo-Client-Channel (<=32) — with client_playback_context.app_build/app_channel as the v3 fallback, which is also where the previously discarded app_version now gets used. The server never parses, compares, or enum-validates either value: Apple uses a per-platform TestFlight sequence and Android a per-marketing- version counter, and keeping them opaque lets both coexist without a shared scheme. Any future minimum-version gating belongs on client_version, which is semver. Only the named-client branch of playbackClientDisplayName stops truncating; the user-agent branch keeps shortPlaybackClientVersion, so browser labels stay "Chrome 120" rather than a full UA version string. The compact session row is unchanged in width — it is shared with AdminDashboard, AdminStats, and HouseholdStreamsPanel — and the exact string lands in the row tooltip and a new Client card in the expanded panel. Diagnostic logs carry client_name/version/build/channel on both "playback plan decided" lines and on session expiry. opslog stores an open attrs JSONB, so this needs no migration. activity_log is deliberately untouched: it is the highest-volume table and the value is constant per device. Jellyfin compat sessions keep an empty build — the MediaBrowser auth header vocabulary has no build concept, and synthesizing one from a user agent would be a guess. Part of the client-version-visibility work spanning silo-android and silo-apple. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
48 lines
1.7 KiB
Go
48 lines
1.7 KiB
Go
package main
|
|
|
|
import (
|
|
"github.com/Silo-Server/silo-server/internal/playback"
|
|
"github.com/Silo-Server/silo-server/internal/worker"
|
|
)
|
|
|
|
// buildLiveSessionSync converts an in-memory playback session into the shared
|
|
// live admin session snapshot. For live admin views, play_method tracks the
|
|
// current transport method rather than the preserved semantic/base method used
|
|
// by player-facing responses and playback history.
|
|
func buildLiveSessionSync(s *playback.Session, reportingNode string) worker.SessionSync {
|
|
if s == nil {
|
|
return worker.SessionSync{ReportingNode: reportingNode}
|
|
}
|
|
|
|
return worker.SessionSync{
|
|
SessionID: s.ID,
|
|
UserID: s.UserID,
|
|
ProfileID: s.ProfileID,
|
|
MediaFileID: s.MediaFileID,
|
|
RequestedMediaFileID: s.RequestedMediaFileID,
|
|
PlayMethod: string(s.PlayMethod),
|
|
ReportingNode: reportingNode,
|
|
ClientIP: s.ClientIP,
|
|
ClientName: s.ClientName,
|
|
ClientVersion: s.ClientVersion,
|
|
ClientBuild: s.ClientBuild,
|
|
ClientChannel: s.ClientChannel,
|
|
ClientUserAgent: s.ClientUserAgent,
|
|
AudioTrackIndex: s.AudioTrackIndex,
|
|
TranscodeAudio: s.TranscodeAudio,
|
|
StreamBitrateKbps: s.StreamBitrateKbps,
|
|
TranscodeNodeURL: s.TranscodeNodeURL,
|
|
TargetResolution: s.TargetResolution,
|
|
TargetVideoCodec: s.TargetVideoCodec,
|
|
TargetAudioCodec: s.TargetAudioCodec,
|
|
TargetBitrateKbps: s.TargetBitrateKbps,
|
|
TranscodeHWAccel: s.TranscodeHWAccel,
|
|
StartedAt: s.StartedAt,
|
|
UpdatedAt: s.UpdatedAt,
|
|
PositionSeconds: s.Position,
|
|
IsPaused: s.IsPaused,
|
|
HasWebSocket: s.HasWebSocket,
|
|
IsJellyfinCompat: s.IsJellyfinCompat,
|
|
}
|
|
}
|