0.2.72 - Magic button, Next up fixes

This commit is contained in:
ponzischeme89
2026-08-17 11:41:36 +12:00
parent 36cb1324fd
commit 12b27c77c3
2655 changed files with 5435 additions and 254 deletions
+51 -4
View File
@@ -24,7 +24,7 @@ import (
// Anything that identifies a *television* rather than a person is deliberately absent:
// the device name, the update source, the screensaver's rotation and ring colour. Those
// belong to the box in the room and must not follow someone to another one.
const preferenceSchemaVersion = 1
const preferenceSchemaVersion = 2
type preferenceKind string
@@ -40,6 +40,8 @@ const (
preferenceList preferenceKind = "list"
// One of Numbers.
preferenceNumber preferenceKind = "number"
// Short free-form display text, bounded by MaxLength.
preferenceText preferenceKind = "text"
)
type preferenceOption struct {
@@ -57,8 +59,10 @@ type preferenceDefinition struct {
Numbers []int `json:"numbers,omitempty"`
// Unit names what a number counts, for the admin console's editor. Without it every
// number reads as minutes, which is what the first one to exist happened to be.
Unit string `json:"unit,omitempty"`
Default any `json:"default"`
Unit string `json:"unit,omitempty"`
MaxLength int `json:"maxLength,omitempty"`
AdminOnly bool `json:"adminOnly,omitempty"`
Default any `json:"default"`
}
func option(value, label string) preferenceOption {
@@ -66,6 +70,11 @@ func option(value, label string) preferenceOption {
}
var preferenceCatalogue = []preferenceDefinition{
{
Key: "profileInitials", Name: "Profile initials", Area: "Profile",
Description: "Up to two characters shown in this person's user-switcher avatar. Leave blank to generate them from their name.",
Kind: preferenceText, Default: "", MaxLength: 2, AdminOnly: true,
},
{
Key: "homeSections", Name: "Home rows", Area: "Home",
Description: "Which built-in rows the launcher shows, in order.",
@@ -246,6 +255,23 @@ func normalizePreferences(raw map[string]any) map[string]any {
return result
}
// Device writes must carry admin-owned fields forward even when the client predates them.
// Without this merge, changing an unrelated setting on an older television would silently
// put profile initials back on automatic.
func preserveAdminPreferences(incoming map[string]any, stored json.RawMessage) map[string]any {
merged := make(map[string]any, len(incoming)+1)
for key, value := range incoming {
merged[key] = value
}
current := decodePreferences(stored)
for _, definition := range preferenceCatalogue {
if definition.AdminOnly {
merged[definition.Key] = current[definition.Key]
}
}
return merged
}
func normalizePreference(definition preferenceDefinition, value any) any {
switch definition.Kind {
case preferenceToggle:
@@ -290,6 +316,14 @@ func normalizePreference(definition preferenceDefinition, value any) any {
if number, ok := asInt(value); ok && slices.Contains(definition.Numbers, number) {
return number
}
case preferenceText:
if typed, ok := value.(string); ok {
trimmed := strings.TrimSpace(typed)
if !strings.ContainsAny(trimmed, "\n\r") &&
(definition.MaxLength <= 0 || len([]rune(trimmed)) <= definition.MaxLength) {
return strings.ToUpper(trimmed)
}
}
}
return defaultValue(definition)
}
@@ -396,7 +430,14 @@ func (s *Server) handlePreferences(w http.ResponseWriter, r *http.Request, sess
writeError(w, http.StatusBadRequest, "malformed request body")
return
}
raw, err := json.Marshal(normalizePreferences(req.Preferences))
current, err := s.store.UserPreferences(r.Context(), sess.EmbyUserID)
if err != nil {
writeError(w, http.StatusInternalServerError, "could not read your settings")
return
}
raw, err := json.Marshal(normalizePreferences(
preserveAdminPreferences(req.Preferences, current.Preferences),
))
if err != nil {
writeError(w, http.StatusBadRequest, "could not read those settings")
return
@@ -557,6 +598,12 @@ func preferenceValueLabel(definition preferenceDefinition, value any) string {
return "None"
}
return strings.Join(entries, ", ")
case preferenceText:
text, _ := value.(string)
if text == "" {
return "Automatic"
}
return text
}
return ""
}