0.1.52 Gateway

This commit is contained in:
ponzischeme89
2026-08-17 19:09:17 +12:00
parent d5a8d3ad88
commit 1da91e40a1
46 changed files with 1626 additions and 106 deletions
+60
View File
@@ -273,6 +273,66 @@ func (s *Syncer) credentials(ctx context.Context) (emby.Credentials, error) {
}, nil
}
// Find searches Emby for a term and imports whatever comes back, answering with the
// payloads as they were stored.
//
// The catalogue is a copy, and a copy is only ever as fresh as the last import — up to
// MEMBY_SYNC_INTERVAL behind, an hour by default. That is invisible to a viewer, whose
// rows are read from Emby live, and very visible to an operator, who cannot pin a film to
// the hero until the gateway has heard of it. So the picker asks Emby itself, and what it
// finds is *adopted* rather than merely displayed: an id that came back from here resolves
// through LibraryItemsByID immediately, which is what the policy validation and the hero
// row both read, so nothing downstream has to know the title arrived early.
//
// It imports with exactly the fields a scheduled pass uses. Writing a thinner payload
// would leave an adopted title missing People, MediaStreams and ProviderIds until Emby
// next reported it changed — which for a film nobody edits again is never.
func (s *Syncer) Find(ctx context.Context, term string, limit int) ([]json.RawMessage, error) {
trimmed := strings.TrimSpace(term)
if trimmed == "" || limit <= 0 {
return nil, nil
}
cred, err := s.credentials(ctx)
if err != nil {
return nil, err
}
page, err := s.emby.Items(ctx, cred, url.Values{
"SearchTerm": {trimmed},
"IncludeItemTypes": {syncItemTypes},
"Recursive": {"true"},
"Limit": {strconv.Itoa(limit)},
"Fields": {syncFields},
"ImageTypeLimit": {"1"},
"EnableImages": {"true"},
"EnableImageTypes": {syncImageTypes},
"EnableTotalRecordCount": {"false"},
"EnableUserData": {"false"},
})
if err != nil {
return nil, fmt.Errorf("library: search emby: %w", err)
}
items := make([]store.LibraryItem, 0, len(page.Items))
found := make([]json.RawMessage, 0, len(page.Items))
for _, raw := range page.Items {
item, ok := toLibraryItem(raw)
if !ok {
continue
}
items = append(items, item)
found = append(found, item.Payload)
}
if len(items) == 0 {
return nil, nil
}
// Stamped now, like any other import. A full pass sweeps rows carrying a stamp older
// than the pass itself, so a title adopted while one is running is never its victim.
if _, err := s.store.UpsertLibraryItems(ctx, items, time.Now().UTC()); err != nil {
return nil, err
}
s.log.Info("library search adopted titles", "term", trimmed, "found", len(found))
return found, nil
}
// Schedule runs an incremental import on an interval until ctx is cancelled.
//
// New episodes tend to land through the day and films weekly; an hourly incremental pass