0.2.76 - Icon Packs
This commit is contained in:
@@ -250,6 +250,14 @@ func (s *Syncer) run(
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// EmbyCredentials is how the ingest worker borrows the same account the scheduled import
|
||||
// uses. One rule for "who does the gateway talk to Emby as" rather than two, so a
|
||||
// household with a service account configured never has an event-driven read appear in
|
||||
// somebody's Emby history as their television.
|
||||
func (s *Syncer) EmbyCredentials(ctx context.Context) (emby.Credentials, error) {
|
||||
return s.credentials(ctx)
|
||||
}
|
||||
|
||||
// credentials prefers the configured service account and otherwise borrows the most
|
||||
// recent TV session.
|
||||
func (s *Syncer) credentials(ctx context.Context) (emby.Credentials, error) {
|
||||
@@ -333,44 +341,74 @@ func (s *Syncer) Find(ctx context.Context, term string, limit int) ([]json.RawMe
|
||||
return found, nil
|
||||
}
|
||||
|
||||
// disabledSyncPoll is how often a switched-off schedule wakes to ask whether it still is.
|
||||
// A setting an operator has just changed must not need a restart, which is the same reason
|
||||
// the Emby reachability probe keeps ticking slowly while it is off.
|
||||
const disabledSyncPoll = 5 * time.Minute
|
||||
|
||||
// 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
|
||||
// covers both without ever asking Emby for the whole catalogue again.
|
||||
func (s *Syncer) Schedule(ctx context.Context, interval time.Duration, paused ...func() bool) {
|
||||
if interval <= 0 {
|
||||
// It is reconciliation now rather than discovery. Where the *arr webhooks are configured, a
|
||||
// file is in the catalogue within a minute of Sonarr or Radarr putting it there and this
|
||||
// pass exists for what they do not manage — media dropped in by hand, a title edited in
|
||||
// Emby, a webhook that never arrived because the gateway was down. Where they are not, it
|
||||
// is still the only thing that notices anything, which is why the interval is the
|
||||
// operator's rather than a constant.
|
||||
//
|
||||
// interval is a function rather than a value because it is read every cycle: an operator
|
||||
// who has just lengthened the sweep must see that take effect without restarting the
|
||||
// container. Zero means switched off, and this keeps waking to ask.
|
||||
func (s *Syncer) Schedule(
|
||||
ctx context.Context, interval func() time.Duration, paused ...func() bool,
|
||||
) {
|
||||
if interval == nil {
|
||||
s.log.Info("library auto-sync disabled")
|
||||
return
|
||||
}
|
||||
ticker := time.NewTicker(interval)
|
||||
defer ticker.Stop()
|
||||
|
||||
s.log.Info("library auto-sync scheduled", "interval", interval.String())
|
||||
s.log.Info("library auto-sync scheduled", "interval", durationLabel(interval()))
|
||||
for {
|
||||
wait := interval()
|
||||
disabled := wait <= 0
|
||||
if disabled {
|
||||
wait = disabledSyncPoll
|
||||
}
|
||||
timer := time.NewTimer(wait)
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
timer.Stop()
|
||||
return
|
||||
case <-ticker.C:
|
||||
if len(paused) > 0 && paused[0] != nil && paused[0]() {
|
||||
s.log.Debug("skipping scheduled sync; quiet time is active")
|
||||
case <-timer.C:
|
||||
}
|
||||
timer.Stop()
|
||||
if disabled {
|
||||
continue
|
||||
}
|
||||
if len(paused) > 0 && paused[0] != nil && paused[0]() {
|
||||
s.log.Debug("skipping scheduled sync; quiet time is active")
|
||||
continue
|
||||
}
|
||||
if s.Running() {
|
||||
s.log.Info("skipping scheduled sync; one is already running")
|
||||
continue
|
||||
}
|
||||
if _, err := s.Sync(ctx, "incremental", "schedule"); err != nil {
|
||||
if errors.Is(err, ErrNoCredentials) {
|
||||
// Nobody has signed in yet. Not worth an error-level log every hour.
|
||||
s.log.Info("skipping scheduled sync; no credentials yet")
|
||||
continue
|
||||
}
|
||||
if s.Running() {
|
||||
s.log.Info("skipping scheduled sync; one is already running")
|
||||
continue
|
||||
}
|
||||
if _, err := s.Sync(ctx, "incremental", "schedule"); err != nil {
|
||||
if errors.Is(err, ErrNoCredentials) {
|
||||
// Nobody has signed in yet. Not worth an error-level log every hour.
|
||||
s.log.Info("skipping scheduled sync; no credentials yet")
|
||||
continue
|
||||
}
|
||||
s.log.Error("scheduled sync failed", "error", err)
|
||||
}
|
||||
s.log.Error("scheduled sync failed", "error", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func durationLabel(value time.Duration) string {
|
||||
if value <= 0 {
|
||||
return "off"
|
||||
}
|
||||
return value.String()
|
||||
}
|
||||
|
||||
// syncItem mirrors the Emby fields promoted to columns.
|
||||
type syncItem struct {
|
||||
ID string `json:"Id"`
|
||||
|
||||
Reference in New Issue
Block a user