0.3.27 - Omni search fixes, Services in genre browser..

This commit is contained in:
ponzischeme89
2026-08-26 08:42:56 +12:00
parent 919a12f8d5
commit 9d11bb7282
26 changed files with 2202 additions and 590 deletions
+41 -14
View File
@@ -50,18 +50,45 @@ func (s *Server) handleGenreItems(w http.ResponseWriter, r *http.Request, sess s
writeError(w, http.StatusBadRequest, "a genre is required")
return
}
s.handleBrowseItems(w, r, sess, genre)
s.handleBrowseItems(w, r, sess, "Genres", "genre", genre)
}
func (s *Server) handleLibraryItems(w http.ResponseWriter, r *http.Request, sess store.Session) {
s.handleBrowseItems(w, r, sess, "")
s.handleBrowseItems(w, r, sess, "Genres", "genre", "")
}
// handleServiceItems answers the Genres page's Services shortcuts — Apple TV+, Netflix,
// Disney+ and the rest of the studio/network catalogue in ui/genre/GenreCategories.kt.
//
// It is the same shelf as a genre, filtered on a different Emby field: services are
// filtered on Studios, which is where a metadata agent (TMDb chief among them) records a
// title's production or distribution company — "Netflix", "Apple TV+", "Disney+" are
// ordinary studio names to Emby, so no new metadata or import step is needed. Like
// genres, the catalogue mapping a service to its Emby spellings is a client-side product
// list rather than server data, for the same reason genreCategories() is: the order is a
// design decision that must never jump around while home rows are arriving, and a service
// with no matching titles simply returns an empty page rather than needing to be hidden
// from a central registry.
func (s *Server) handleServiceItems(w http.ResponseWriter, r *http.Request, sess store.Session) {
service := strings.TrimSpace(r.PathValue("service"))
if service == "" {
writeError(w, http.StatusBadRequest, "a service is required")
return
}
s.handleBrowseItems(w, r, sess, "Studios", "service", service)
}
func (s *Server) handleBrowseItems(
w http.ResponseWriter,
r *http.Request,
sess store.Session,
genre string,
// filterField is the Emby query parameter the filter value is written into — "Genres"
// for a genre shelf, "Studios" for a service shelf. filterLabel is only for the cache
// key and the log line, so the two shelves' entries can never collide or be confused
// for one another in a shared keyspace.
filterField string,
filterLabel string,
filterValue string,
) {
ctx := r.Context()
limit := queryInt(r, "limit", genrePageSize, genrePageMax)
@@ -73,8 +100,8 @@ func (s *Server) handleBrowseItems(
}
filterKey := "all"
if genre != "" {
filterKey = "genre:" + genre
if filterValue != "" {
filterKey = filterLabel + ":" + filterValue
}
key := cache.UserKey(viewerKeyOf(ctx, sess), "browse:"+itemType+":"+filterKey+":"+itoa(offset)+":"+itoa(limit))
if raw, err := s.cache.Get(ctx, key); err == nil {
@@ -95,8 +122,8 @@ func (s *Server) handleBrowseItems(
"SortBy": {"PremiereDate,SortName"},
"SortOrder": {"Descending"},
}, fieldsRow)
if genre != "" {
params.Set("Genres", genre)
if filterValue != "" {
params.Set(filterField, filterValue)
}
// rowParams turns this off for the home rows, which never page. Here it is the number
// the scroll stops on.
@@ -115,20 +142,20 @@ func (s *Server) handleBrowseItems(
total := genreTotal(result.TotalRecordCount, offset, len(items), limit)
// The first page is somebody opening a genre, which is a navigation event worth the
// log; the pages after it are one viewer scrolling and would bury it.
if offset == 0 && genre != "" {
s.loggerFor(ctx).Info("genre browsed", "genre", genre, "results", len(items), "total", total)
// The first page is somebody opening a genre or a service, which is a navigation event
// worth the log; the pages after it are one viewer scrolling and would bury it.
if offset == 0 && filterValue != "" {
s.loggerFor(ctx).Info(filterLabel+" browsed", filterLabel, filterValue, "results", len(items), "total", total)
} else if offset == 0 {
s.loggerFor(ctx).Info("library browsed", "type", itemType, "results", len(items), "total", total)
} else if genre != "" {
s.loggerFor(ctx).Debug("genre page", "genre", genre, "offset", offset, "results", len(items))
} else if filterValue != "" {
s.loggerFor(ctx).Debug(filterLabel+" page", filterLabel, filterValue, "offset", offset, "results", len(items))
} else {
s.loggerFor(ctx).Debug("library page", "type", itemType, "offset", offset, "results", len(items))
}
body, err := json.Marshal(genrePage{
Genre: genre,
Genre: filterValue,
Items: items,
Offset: offset,
Limit: limit,