Добавил логи

This commit is contained in:
2026-06-14 19:37:09 +03:00
parent d4bf8a8cad
commit 81ed58ecff
28 changed files with 379 additions and 121 deletions
+35 -22
View File
@@ -81,14 +81,14 @@ func (w *Worker) recognizeOne(ctx context.Context, id int64) {
// относительных путей файлов в абсолютные при раскладке.
func (w *Worker) runRecognize(ctx context.Context, d store.Download) (recognize.Result, string, error) {
if !d.Infohash.Valid {
return recognize.Result{}, "", fmt.Errorf("нет infohash")
return recognize.Result{}, "", fmt.Errorf("no infohash")
}
t, ok, err := w.torrentByInfohash(ctx, d.Infohash.String)
if err != nil {
return recognize.Result{}, "", err
}
if !ok {
return recognize.Result{}, "", fmt.Errorf("торрент не найден в qBittorrent")
return recognize.Result{}, "", fmt.Errorf("torrent not found in qBittorrent")
}
files, err := w.qbt.Files(ctx, t.Hash)
if err != nil {
@@ -209,7 +209,7 @@ func (w *Worker) Apply(ctx context.Context, id int64) error {
w.mu.Lock()
defer w.mu.Unlock()
if w.layouter == nil {
return fmt.Errorf("apply: раскладчик не сконфигурирован")
return fmt.Errorf("apply: layouter not configured")
}
d, err := w.store.GetDownload(ctx, id)
@@ -217,7 +217,7 @@ func (w *Worker) Apply(ctx context.Context, id int64) error {
return fmt.Errorf("apply: %w", err)
}
if d.State != store.StateReview && d.State != store.StateDeferred {
return fmt.Errorf("apply: задача %d в состоянии %s (ожидалось review/deferred)", id, d.State)
return fmt.Errorf("apply: download %d is in state %s (expected review/deferred)", id, d.State)
}
plan, tag, err := w.effectivePlan(ctx, id)
@@ -226,7 +226,7 @@ func (w *Worker) Apply(ctx context.Context, id int64) error {
}
t, ok, err := w.torrentByInfohash(ctx, d.Infohash.String)
if err != nil || !ok {
return fmt.Errorf("apply: торрент не найден: %v", err)
return fmt.Errorf("apply: torrent not found: %v", err)
}
w.transition(ctx, *d, store.StateLinking, "", "")
@@ -243,7 +243,7 @@ func (w *Worker) linkPlan(ctx context.Context, d *store.Download, plan recognize
links, err := w.layouter.BuildLinks(toLayoutPlan(plan, savePath, providerTag))
if err != nil {
w.transition(ctx, *d, store.StateReview, "build", err.Error())
return fmt.Errorf("построение ссылок: %w", err)
return fmt.Errorf("build links: %w", err)
}
batch := w.newID()
@@ -263,7 +263,7 @@ func (w *Worker) linkPlan(ctx context.Context, d *store.Download, plan recognize
}
if len(fl) > 0 {
if err := w.store.CreateFileLinks(ctx, fl); err != nil {
return fmt.Errorf("запись ссылок: %w", err)
return fmt.Errorf("persist links: %w", err)
}
}
@@ -285,7 +285,7 @@ func (w *Worker) linkPlan(ctx context.Context, d *store.Download, plan recognize
func (w *Worker) Refine(ctx context.Context, id int64, hint string) error {
hint = strings.TrimSpace(hint)
if hint == "" {
return fmt.Errorf("refine: пустая подсказка")
return fmt.Errorf("refine: empty hint")
}
w.mu.Lock()
defer w.mu.Unlock()
@@ -297,6 +297,7 @@ func (w *Worker) Refine(ctx context.Context, id int64, hint string) error {
if err := w.store.AddHint(ctx, id, hint); err != nil {
return fmt.Errorf("refine: %w", err)
}
w.log.Info("review: hint added, re-recognizing", "download_id", id, "hint", hint)
w.transition(ctx, *d, store.StateRecognizing, "", "")
return nil
}
@@ -305,7 +306,7 @@ func (w *Worker) Refine(ctx context.Context, id int64, hint string) error {
// — чтобы LLM пересобрал роли файлов под новый тип.
func (w *Worker) SetType(ctx context.Context, id int64, mediaType string) error {
if mediaType != string(recognize.MediaMovie) && mediaType != string(recognize.MediaSeries) {
return fmt.Errorf("set type: недопустимый тип %q", mediaType)
return fmt.Errorf("set type: invalid type %q", mediaType)
}
w.mu.Lock()
defer w.mu.Unlock()
@@ -333,7 +334,7 @@ func (w *Worker) SetType(ctx context.Context, id int64, mediaType string) error
func (w *Worker) IgnoreFile(ctx context.Context, id int64, src string) error {
src = strings.TrimSpace(src)
if src == "" {
return fmt.Errorf("ignore: пустой путь")
return fmt.Errorf("ignore: empty path")
}
w.mu.Lock()
defer w.mu.Unlock()
@@ -353,6 +354,7 @@ func (w *Worker) IgnoreFile(ctx context.Context, id int64, src string) error {
if err := w.store.SetOverride(ctx, id, ovrIgnoredFiles, string(b)); err != nil {
return fmt.Errorf("ignore: %w", err)
}
w.log.Info("review: file ignored", "download_id", id, "src", src)
return nil
}
@@ -366,7 +368,7 @@ func (w *Worker) Defer(ctx context.Context, id int64) error {
return fmt.Errorf("defer: %w", err)
}
if d.State.IsTerminal() {
return fmt.Errorf("defer: задача %d терминальна (%s)", id, d.State)
return fmt.Errorf("defer: download %d is terminal (%s)", id, d.State)
}
w.transition(ctx, *d, store.StateDeferred, "", "")
return nil
@@ -378,7 +380,7 @@ func (w *Worker) Undo(ctx context.Context, id int64) error {
w.mu.Lock()
defer w.mu.Unlock()
if w.layouter == nil {
return fmt.Errorf("undo: раскладчик не сконфигурирован")
return fmt.Errorf("undo: layouter not configured")
}
d, err := w.store.GetDownload(ctx, id)
@@ -386,14 +388,14 @@ func (w *Worker) Undo(ctx context.Context, id int64) error {
return fmt.Errorf("undo: %w", err)
}
if d.State != store.StateDone {
return fmt.Errorf("undo: задача %d в состоянии %s (ожидалось done)", id, d.State)
return fmt.Errorf("undo: download %d is in state %s (expected done)", id, d.State)
}
batch, err := w.store.LatestBatchID(ctx, id)
if err != nil {
return fmt.Errorf("undo: %w", err)
}
if batch == "" {
return fmt.Errorf("undo: нечего откатывать")
return fmt.Errorf("undo: nothing to revert")
}
rows, err := w.store.ListFileLinksByBatch(ctx, batch)
if err != nil {
@@ -422,7 +424,7 @@ func (w *Worker) requireReviewable(ctx context.Context, id int64, op string) (*s
return nil, fmt.Errorf("%s: %w", op, err)
}
if d.State != store.StateReview && d.State != store.StateDeferred {
return nil, fmt.Errorf("%s: задача %d в состоянии %s (ожидалось review/deferred)", op, id, d.State)
return nil, fmt.Errorf("%s: download %d is in state %s (expected review/deferred)", op, id, d.State)
}
return d, nil
}
@@ -448,7 +450,7 @@ func (w *Worker) ChooseCandidate(ctx context.Context, id, candidateID int64) err
return fmt.Errorf("choose candidate: %w", err)
}
if rec == nil || cand == nil || cand.RecognitionID != rec.ID {
return fmt.Errorf("choose candidate: кандидат %d не относится к текущему распознаванию", candidateID)
return fmt.Errorf("choose candidate: candidate %d does not belong to the current recognition", candidateID)
}
pins := map[string]string{ovrProvider: cand.Provider, ovrProviderID: cand.ProviderID}
@@ -478,10 +480,10 @@ func (w *Worker) SetProviderID(ctx context.Context, id int64, provider, provider
switch provider {
case "tmdb", "tvdb", "imdb":
default:
return fmt.Errorf("set provider: недопустимый провайдер %q (tmdb/tvdb/imdb)", provider)
return fmt.Errorf("set provider: invalid provider %q (tmdb/tvdb/imdb)", provider)
}
if providerID == "" {
return fmt.Errorf("set provider: пустой id")
return fmt.Errorf("set provider: empty id")
}
w.mu.Lock()
defer w.mu.Unlock()
@@ -495,6 +497,8 @@ func (w *Worker) SetProviderID(ctx context.Context, id int64, provider, provider
if err := w.store.SetOverride(ctx, id, ovrProviderID, providerID); err != nil {
return fmt.Errorf("set provider: %w", err)
}
w.log.Info("review: provider set manually",
"download_id", id, "provider", provider, "provider_id", providerID)
return nil
}
@@ -512,6 +516,7 @@ func (w *Worker) ClearProvider(ctx context.Context, id int64) error {
if err := w.store.SetOverride(ctx, id, ovrProviderID, ""); err != nil {
return fmt.Errorf("clear provider: %w", err)
}
w.log.Info("review: provider cleared (no metadata base)", "download_id", id)
return nil
}
@@ -557,19 +562,27 @@ func (w *Worker) ReviewData(ctx context.Context, id int64) (*ReviewData, error)
if rec != nil {
if cands, cerr := w.store.ListCandidatesByRecognition(ctx, rec.ID); cerr == nil {
rd.Candidates = cands
} else {
w.log.Debug("review data: list candidates failed (skipped)",
"download_id", id, "err", cerr)
}
}
if rec != nil && rec.Plan.Valid {
var plan recognize.Plan
if err := json.Unmarshal([]byte(rec.Plan.String), &plan); err == nil {
if err := json.Unmarshal([]byte(rec.Plan.String), &plan); err != nil {
w.log.Warn("review data: unmarshal plan failed", "download_id", id, "err", err)
} else {
plan = applyOverrides(plan, overrides)
rd.Plan = plan
// Превью строим по относительным путям с provider-тегом; ошибку
// игнорируем — просто покажем причины без превью.
// логируем на Debug — просто покажем причины без превью.
if w.layouter != nil {
tag := providerTag(prov, pid)
if links, lerr := w.layouter.BuildLinks(toLayoutPlan(plan, "", tag)); lerr == nil {
rd.Preview = links
} else {
w.log.Debug("review data: build preview failed (skipped)",
"download_id", id, "err", lerr)
}
}
}
@@ -585,11 +598,11 @@ func (w *Worker) effectivePlan(ctx context.Context, id int64) (recognize.Plan, s
return recognize.Plan{}, "", err
}
if rec == nil || !rec.Plan.Valid {
return recognize.Plan{}, "", fmt.Errorf("нет плана распознавания")
return recognize.Plan{}, "", fmt.Errorf("no recognition plan")
}
var plan recognize.Plan
if err := json.Unmarshal([]byte(rec.Plan.String), &plan); err != nil {
return recognize.Plan{}, "", fmt.Errorf("разбор плана: %w", err)
return recognize.Plan{}, "", fmt.Errorf("parse plan: %w", err)
}
overrides, err := w.store.ListOverrides(ctx, id)
if err != nil {
+2 -2
View File
@@ -491,7 +491,7 @@ func newApplyFixture(t *testing.T, plan recognize.Plan) applyFixture {
t.Fatal(err)
}
}
lay, err := layout.New(layout.Config{MoviesDir: movies, SeriesDir: series})
lay, err := layout.New(layout.Config{MoviesDir: movies, SeriesDir: series}, nil)
if err != nil {
t.Fatal(err)
}
@@ -653,7 +653,7 @@ func TestRecognizeOne_AutoApplies(t *testing.T) {
_ = os.MkdirAll(filepath.Dir(p), 0o755)
_ = os.WriteFile(p, []byte("x"), 0o644)
}
lay, _ := layout.New(layout.Config{MoviesDir: movies, SeriesDir: series})
lay, _ := layout.New(layout.Config{MoviesDir: movies, SeriesDir: series}, nil)
st := newMemStore()
st.put(completedDownload(1))