summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-05-09 21:42:48 +0300
committerPaul Buetow <paul@buetow.org>2026-05-09 21:42:48 +0300
commitd96c86cfeef342d17cc88e1cb3cc75bdf060339e (patch)
treeb96fc2765fa0efa516557073045b18c84674f54a
parent914092f4d519aa7107439d48b882d149ddff1118 (diff)
more on this
-rw-r--r--cmd/player/main.go16
-rw-r--r--internal/service/gc.go5
-rw-r--r--internal/service/podcast.go1
-rw-r--r--internal/service/scan.go7
4 files changed, 24 insertions, 5 deletions
diff --git a/cmd/player/main.go b/cmd/player/main.go
index 94cad3e..b043de5 100644
--- a/cmd/player/main.go
+++ b/cmd/player/main.go
@@ -8,6 +8,7 @@ import (
"net/http"
"os"
"os/signal"
+ "runtime/debug"
"syscall"
"time"
@@ -80,6 +81,12 @@ func buildLogger(logLevel string) *slog.Logger {
return slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{Level: level}))
}
+func recoverBackgroundWorkerPanic(logger *slog.Logger, worker string) {
+ if r := recover(); r != nil {
+ logger.Error("background worker panic", "worker", worker, "panic", r, "stack", string(debug.Stack()))
+ }
+}
+
// wireDeps constructs the core service layer dependencies.
func wireDeps(cfg *internal.Config, store repository.Store, logger *slog.Logger, appCtx context.Context) *appDeps {
clk := clock.RealClock{}
@@ -130,9 +137,12 @@ func startBackgroundWorkers(deps *appDeps) {
for {
select {
case <-ticker.C:
- if err := deps.podcastSvc.CheckFeeds(context.Background()); err != nil {
- deps.logger.Error("podcast feed check failed", "err", err)
- }
+ func() {
+ defer recoverBackgroundWorkerPanic(deps.logger, "podcast checker")
+ if err := deps.podcastSvc.CheckFeeds(context.Background()); err != nil {
+ deps.logger.Error("podcast feed check failed", "err", err)
+ }
+ }()
case <-deps.appCtx.Done():
return
}
diff --git a/internal/service/gc.go b/internal/service/gc.go
index 8ff3204..f7ea396 100644
--- a/internal/service/gc.go
+++ b/internal/service/gc.go
@@ -70,7 +70,10 @@ func (w *GCWorker) Start() {
for {
select {
case <-tickCh:
- w.run(w.ctx)
+ func() {
+ defer recoverWorkerPanic(w.logger, "gc")
+ w.run(w.ctx)
+ }()
w.notifyRunDone()
case <-w.stopCh:
return
diff --git a/internal/service/podcast.go b/internal/service/podcast.go
index ad5e22b..269df68 100644
--- a/internal/service/podcast.go
+++ b/internal/service/podcast.go
@@ -656,6 +656,7 @@ func (s *podcastService) CheckFeeds(ctx context.Context) error {
wg.Add(1)
go func(f model.PodcastFeed) {
defer wg.Done()
+ defer recoverWorkerPanic(s.logger, "podcast feed check")
if err := s.checkFeed(ctx, f); err != nil {
s.logger.Warn("podcast feed check failed", "feed_id", f.ID, "feed_url", f.FeedURL, "err", err)
} else {
diff --git a/internal/service/scan.go b/internal/service/scan.go
index d2cc5bb..1bd7fda 100644
--- a/internal/service/scan.go
+++ b/internal/service/scan.go
@@ -59,6 +59,12 @@ func (s *scanService) TriggerRescan(ctx context.Context) error {
go func() {
defer cancel()
+ defer s.notifyDone()
+ defer func() {
+ if err := recoverWorkerPanic(s.logger, "rescan"); err != nil {
+ progress.Done(err)
+ }
+ }()
err := s.scanner.Scan(scanCtx, s.mediaRoot, progress)
if err == nil {
err = scanCtx.Err()
@@ -70,7 +76,6 @@ func (s *scanService) TriggerRescan(ctx context.Context) error {
progress.Done(nil)
s.logger.Info("rescan completed")
}
- s.notifyDone()
}()
return nil
}