package worker import ( "context" "log/slog" "os" "path/filepath" "time" "github.com/TopherMayor/unified-media-manager/internal/config" "github.com/TopherMayor/unified-media-manager/internal/db" "github.com/TopherMayor/unified-media-manager/internal/service" ) type SubtitleSearchWorker struct { database *db.DB subtitleSvc *service.SubtitleService cfg *config.Config } func NewSubtitleSearchWorker(database *db.DB, subtitleSvc *service.SubtitleService, cfg *config.Config) *SubtitleSearchWorker { return &SubtitleSearchWorker{ database: database, subtitleSvc: subtitleSvc, cfg: cfg, } } func (w *SubtitleSearchWorker) Name() string { return "subtitle_search" } func (w *SubtitleSearchWorker) CronExpr() string { return w.cfg.WorkerSubtitleInterval } func (w *SubtitleSearchWorker) Run(ctx context.Context) error { ctx, cancel := context.WithTimeout(ctx, 5*time.Minute) defer cancel() searched := 0 downloaded := 0 errors := 0 rows, err := w.database.Pool.Query(ctx, `SELECT m.id, m.media_type, m.title, mf.path FROM media m JOIN media_files mf ON m.id = mf.media_id AND mf.deleted_at IS NULL WHERE m.deleted_at IS NULL AND m.status = 'available' ORDER BY m.id`) if err != nil { return err } defer rows.Close() type mediaFile struct { mediaID int64 mediaType string title string path string } var files []mediaFile for rows.Next() { var f mediaFile if err := rows.Scan(&f.mediaID, &f.mediaType, &f.title, &f.path); err != nil { slog.Error("failed to scan media file", "error", err) continue } files = append(files, f) } for _, f := range files { ext := filepath.Ext(f.path) basePath := f.path[:len(f.path)-len(ext)] srtPath := basePath + ".eng.srt" sdhPath := basePath + ".eng.sdh.srt" if _, err := os.Stat(srtPath); err == nil { continue } if _, err := os.Stat(sdhPath); err == nil { continue } results, err := w.subtitleSvc.Search(ctx, f.title, service.SubtitleSearchOptions{ LanguageCodes: []string{"eng"}, }) if err != nil { slog.Error("failed to search subtitles", "media_id", f.mediaID, "error", err) errors++ continue } searched++ if len(results) == 0 { continue } baseName := filepath.Base(basePath) _, err = w.subtitleSvc.Download(ctx, results[0].ID, filepath.Dir(f.path), baseName, results[0].LanguageCode, results[0].HI, results[0].Forced) if err != nil { slog.Error("failed to download subtitle", "media_id", f.mediaID, "error", err) errors++ continue } downloaded++ } slog.Info("subtitle search completed", "searched", searched, "downloaded", downloaded, "errors", errors) return nil }