0.2.72 - Magic button, Next up fixes
This commit is contained in:
@@ -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 ""
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user