0.1.52 Gateway
This commit is contained in:
@@ -97,20 +97,61 @@ func heroScheduleTimeZone(location *time.Location) string {
|
||||
return location.String()
|
||||
}
|
||||
|
||||
// heroSearchLimit is what either half of the picker's search may contribute, and what the
|
||||
// merged answer is trimmed back to.
|
||||
const heroSearchLimit = 20
|
||||
|
||||
// handleAdminHeroSearch answers the picker from the imported catalogue and from Emby.
|
||||
//
|
||||
// The catalogue alone is up to a sync interval stale, so a film imported this afternoon
|
||||
// was simply not findable here until the next hourly pass — and pinning is the one hero
|
||||
// decision an operator makes about a title *because* it has just arrived. Emby is asked
|
||||
// as well and `Find` imports what it returns, which is what makes a fresh id usable by the
|
||||
// policy validation and by the hero row itself rather than only by this list.
|
||||
//
|
||||
// Either half may fail without failing the search: a stale answer and a live one are both
|
||||
// better than an error, and the two are deliberately asked in that order so a gateway with
|
||||
// no Emby credentials configured still has a picker.
|
||||
func (s *Server) handleAdminHeroSearch(w http.ResponseWriter, r *http.Request) {
|
||||
items, err := s.store.SearchLibrary(r.Context(), r.URL.Query().Get("q"), 20)
|
||||
term := strings.TrimSpace(r.URL.Query().Get("q"))
|
||||
items, err := s.store.SearchLibrary(r.Context(), term, heroSearchLimit)
|
||||
if err != nil {
|
||||
s.loggerFor(r.Context()).Error("hero library search failed", "error", err)
|
||||
writeError(w, http.StatusInternalServerError, "could not search the library")
|
||||
return
|
||||
}
|
||||
results := make([]heroAdminItem, 0, len(items))
|
||||
for _, raw := range items {
|
||||
if item, ok := adminHeroItem(raw); ok {
|
||||
results = append(results, item)
|
||||
if s.syncer != nil && term != "" {
|
||||
found, findErr := s.syncer.Find(r.Context(), term, heroSearchLimit)
|
||||
if findErr != nil {
|
||||
s.loggerFor(r.Context()).Warn("hero live search unavailable", "error", findErr)
|
||||
}
|
||||
items = append(items, found...)
|
||||
}
|
||||
writeJSON(w, http.StatusOK, map[string]any{"items": results})
|
||||
writeJSON(w, http.StatusOK, map[string]any{"items": mergeHeroSearchResults(items, heroSearchLimit)})
|
||||
}
|
||||
|
||||
// mergeHeroSearchResults turns both halves of the search into one list.
|
||||
//
|
||||
// Almost every title comes back from both, so the dedupe is the ordinary case rather than
|
||||
// the exception, and it keeps the first sighting: the catalogue answers first, and its
|
||||
// ranking is the one an operator has been reading all along. Anything an id appears in
|
||||
// only once is either a title Emby has and the last import missed — the whole point — or
|
||||
// one deleted from Emby that the catalogue has not swept yet.
|
||||
func mergeHeroSearchResults(items []json.RawMessage, limit int) []heroAdminItem {
|
||||
results := make([]heroAdminItem, 0, len(items))
|
||||
seen := make(map[string]bool, len(items))
|
||||
for _, raw := range items {
|
||||
if len(results) >= limit {
|
||||
break
|
||||
}
|
||||
item, ok := adminHeroItem(raw)
|
||||
if !ok || seen[item.ID] {
|
||||
continue
|
||||
}
|
||||
seen[item.ID] = true
|
||||
results = append(results, item)
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
||||
func (s *Server) handleAdminHeroPolicy(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
Reference in New Issue
Block a user