App v0.2.26 and gateway 0.1.20
Client: seek controls, Bazarr subtitle download and cast panel in the player; MDBList ratings strip; episode and schedule detail pages; series pace estimate; what's new panel; install-permission onboarding step; synced per-profile preferences; Emby outage banner. Gateway: rebuilt admin console (one fragment per page), preference history and restore, merged Continue Watching, Emby health probe, subtitle selection and Bazarr download, structured request logging with per-request identity, and embedded build version. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
2675e6d82b
commit
4a4df7a73c
@@ -17,6 +17,8 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/ponzischeme89/memby/server/internal/api"
|
||||
"github.com/ponzischeme89/memby/server/internal/bazarr"
|
||||
"github.com/ponzischeme89/memby/server/internal/buildinfo"
|
||||
"github.com/ponzischeme89/memby/server/internal/cache"
|
||||
"github.com/ponzischeme89/memby/server/internal/config"
|
||||
"github.com/ponzischeme89/memby/server/internal/emby"
|
||||
@@ -46,7 +48,12 @@ func main() {
|
||||
|
||||
logLevel := logging.ParseLevel(os.Getenv("MEMBY_LOG_LEVEL"))
|
||||
logCapacity := logging.ParseCapacity(os.Getenv("MEMBY_LOG_BUFFER_CAPACITY"), 5_000)
|
||||
log, events := logging.NewBuffered(os.Stdout, logLevel, logCapacity)
|
||||
logFormat := logging.ParseFormat(os.Getenv("MEMBY_LOG_FORMAT"))
|
||||
log, events := logging.NewBuffered(os.Stdout, logLevel, logCapacity, logFormat)
|
||||
// Every line names the build that wrote it. A gateway is deployed from a working
|
||||
// tree, often while a television is running an older app, so "which server said
|
||||
// this" is a real question that a reader should never have to scroll for.
|
||||
log = log.With("version", buildinfo.Version())
|
||||
|
||||
if err := run(log, events); err != nil {
|
||||
log.Error("fatal", "error", err)
|
||||
@@ -102,8 +109,16 @@ func run(log *slog.Logger, events *logging.Buffer) error {
|
||||
radarrClient = radarr.New(cfg.RadarrURL, cfg.RadarrAPIKey, cfg.UpstreamTimeout)
|
||||
log.Info("radarr integration enabled", "url", cfg.RadarrURL)
|
||||
}
|
||||
// Bazarr gets its own timeout rather than UpstreamTimeout: a manual search queries
|
||||
// live subtitle providers and legitimately takes tens of seconds, and cutting it short
|
||||
// reads to the viewer as "no subtitles found" rather than as a timeout.
|
||||
var bazarrClient *bazarr.Client
|
||||
if cfg.BazarrURL != "" {
|
||||
bazarrClient = bazarr.New(cfg.BazarrURL, cfg.BazarrAPIKey, cfg.BazarrTimeout)
|
||||
log.Info("bazarr integration enabled", "url", cfg.BazarrURL)
|
||||
}
|
||||
|
||||
recommender := recommend.NewEngine(embyClient, log)
|
||||
recommender := recommend.NewEngine(embyClient, log.With("component", "recommendations"))
|
||||
if cfg.RecommendationWeights != "" {
|
||||
_ = json.Unmarshal([]byte(cfg.RecommendationWeights), &recommender.WeightedConfig)
|
||||
}
|
||||
@@ -127,11 +142,11 @@ func run(log *slog.Logger, events *logging.Buffer) error {
|
||||
UserID: cfg.SyncUserID,
|
||||
Token: cfg.SyncAPIKey,
|
||||
DeviceID: "memby-gateway-sync",
|
||||
}, log)
|
||||
}, log.With("component", "library"))
|
||||
var forYouService *foryou.Service
|
||||
if tracearrClient != nil {
|
||||
forYouService = foryou.New(
|
||||
st, tracearrClient, recommender, log,
|
||||
st, tracearrClient, recommender, log.With("component", "for-you"),
|
||||
cfg.ForYouMinRebuildAge, cfg.ForYouRefreshInterval,
|
||||
)
|
||||
forYouService.ConfigureTimeContext(cfg.SonarrLocation)
|
||||
@@ -149,6 +164,7 @@ func run(log *slog.Logger, events *logging.Buffer) error {
|
||||
ForYou: forYouService,
|
||||
Sonarr: sonarrClient,
|
||||
Radarr: radarrClient,
|
||||
Bazarr: bazarrClient,
|
||||
MDBList: mdblistClient,
|
||||
Syncer: syncer,
|
||||
Log: log,
|
||||
@@ -197,7 +213,11 @@ func run(log *slog.Logger, events *logging.Buffer) error {
|
||||
)
|
||||
go func() {
|
||||
importCtx, cancel := context.WithTimeout(ctx, cfg.SyncTimeout)
|
||||
if _, err := forYouService.Import(importCtx, false); err != nil {
|
||||
// Only if one is actually owed. An unconditional startup import made every
|
||||
// redeploy or container bounce a fresh pass over Tracearr's history.
|
||||
if _, _, err := forYouService.ImportIfDue(
|
||||
importCtx, cfg.TracearrSyncInterval, cfg.TracearrFullInterval,
|
||||
); err != nil {
|
||||
cancel()
|
||||
log.Warn("startup Tracearr import failed", "error", err)
|
||||
return
|
||||
@@ -226,9 +246,10 @@ func run(log *slog.Logger, events *logging.Buffer) error {
|
||||
|
||||
errCh := make(chan error, 1)
|
||||
go func() {
|
||||
log.Info("server ready",
|
||||
log.Info("gateway ready",
|
||||
"listen", cfg.ListenAddr,
|
||||
"emby", cfg.EmbyURL,
|
||||
"protocol", api.ProtocolVersion,
|
||||
"sync_every", cfg.SyncInterval,
|
||||
)
|
||||
if err := httpServer.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
|
||||
|
||||
Reference in New Issue
Block a user