0.3.22 - PINs

This commit is contained in:
ponzischeme89
2026-08-25 11:39:55 +12:00
parent 396d35e2f5
commit 0fcc02f57e
2697 changed files with 5360 additions and 50 deletions
+48 -6
View File
@@ -41,7 +41,10 @@ const (
fieldsDetail = "Overview,Taglines,Genres,MediaStreams,People,Studios,ProductionYear,PremiereDate,OriginalTitle,ProductionLocations,OfficialRating,CommunityRating,RunTimeTicks,SeriesId,SeriesName,Status,ProviderIds,ParentIndexNumber,IndexNumber,PrimaryImageAspectRatio,CollectionName"
fieldsScreensaver = "Overview,Taglines,Genres,ProductionYear,CommunityRating,Studios,OfficialRating,RunTimeTicks"
rowImageTypes = "Backdrop,Primary,Logo"
// Thumb rides every row, not only Continue Watching's: rowParams is the one query
// shape every row shares, and Emby only reports ParentThumbItemId/ParentThumbImageTag
// on an item when Thumb is among the requested types.
rowImageTypes = "Backdrop,Primary,Logo,Thumb"
screensaverImageTypes = "Backdrop,Logo"
)
@@ -90,6 +93,11 @@ func (s *Server) handleHome(w http.ResponseWriter, r *http.Request, sess store.S
continueDefinition,
ProtocolVersion,
).Enabled
// The For You window is the viewer's own device clock, not the household's: the
// greeting above this row already draws from the TV's local time, and a gateway
// hosted (or configured) in a different zone must not tell somebody "Good morning"
// while leading with "Late-night picks for you". See clientLocalNow.
forYouWindow := homeForYouWindowAt(clientLocalNow(r, now.In(s.householdLocation())))
// The hero revision is part of the key rather than something to invalidate. An
// operator's change therefore makes the entries built under the old policy simply
// unreachable, and they age out on their own TTL — where the sweep it replaced dropped
@@ -102,7 +110,7 @@ func (s *Server) handleHome(w http.ResponseWriter, r *http.Request, sess store.S
":r"+strconv.FormatBool(radarrSchedule)+":h"+strconv.FormatBool(hero)+
":c"+strconv.FormatBool(continueWatching)+
":f"+strconv.FormatInt(featurePolicy.Revision, 10)+
":hr"+heroRev+":d"+sess.DeviceID,
":hr"+heroRev+":w"+forYouWindow.ID+":d"+sess.DeviceID,
)
if raw, err := s.cache.Get(ctx, key); err == nil {
@@ -298,9 +306,7 @@ func (s *Server) handleHome(w http.ResponseWriter, r *http.Request, sess store.S
wg.Add(1)
go func() {
defer wg.Done()
location := s.householdLocation()
window := homeForYouWindowAt(time.Now().In(location))
prepared, hit, stale, err := s.forYou.PreparedRows(ctx, sess, window.Minutes)
prepared, hit, stale, err := s.forYou.PreparedRows(ctx, sess, forYouWindow.Minutes)
if err != nil {
s.loggerFor(ctx).Warn("prepared Home For You row failed", "user", sess.EmbyUserID, "error", err)
return
@@ -308,7 +314,7 @@ func (s *Server) handleHome(w http.ResponseWriter, r *http.Request, sess store.S
if !hit || len(prepared) == 0 {
return
}
homeRows := preparedHomeForYouRows(prepared, window)
homeRows := preparedHomeForYouRows(prepared, forYouWindow)
if len(homeRows) == 0 {
return
}
@@ -850,6 +856,42 @@ type homeForYouWindow struct {
Minutes int
}
// clientLocalHourHeader carries the viewer's own device clock, in hours, on every request.
// It exists so a time-sensitive row can agree with the greeting drawn above it: the
// greeting is the TV's own local time, and a household's configured timezone (or the
// gateway's host clock, when none is set) is a different fact that happens to usually
// agree with it — usually, not always, and the times it disagrees are exactly what "Good
// morning" beside "Late-night picks for you" looks like.
const clientLocalHourHeader = "X-Memby-Local-Hour"
// clientLocalNow substitutes the client's reported hour into householdFallback, so an
// older client (or one that failed to send the header) still gets an answer — the
// household's own idea of the time, which is what this row used before there was
// anything to disagree with it.
func clientLocalNow(r *http.Request, householdFallback time.Time) time.Time {
hour, ok := parseClientLocalHour(r.Header.Get(clientLocalHourHeader))
if !ok {
return householdFallback
}
return time.Date(
householdFallback.Year(), householdFallback.Month(), householdFallback.Day(),
hour, householdFallback.Minute(), householdFallback.Second(), 0,
householdFallback.Location(),
)
}
func parseClientLocalHour(value string) (int, bool) {
value = strings.TrimSpace(value)
if value == "" {
return 0, false
}
hour, err := strconv.Atoi(value)
if err != nil || hour < 0 || hour > 23 {
return 0, false
}
return hour, true
}
// homeForYouWindowAt keeps Home useful without asking the viewer for a duration.
// These deliberately broad windows suit a household TV: short before lunch, an
// episode-sized pick in the afternoon/late evening, and film headroom at night.