Фикс повторного распознавания
This commit is contained in:
@@ -286,11 +286,11 @@ func (w *Worker) linkPlan(ctx context.Context, d *store.Download, plan recognize
|
||||
return nil
|
||||
}
|
||||
|
||||
// Relink повторно привязывает откатанную задачу (reverted): возвращает её на
|
||||
// распознавание, и поллинг-цикл перезапустит recognize. Авто-раскладку при
|
||||
// этом не делаем — ручная перепривязка всегда проходит через ревью с
|
||||
// подтверждением (force_review). Источник (раздача в qBittorrent) для этого
|
||||
// должен быть на месте.
|
||||
// Relink повторно привязывает откатанную (reverted) или отклонённую
|
||||
// (cancelled) задачу: возвращает её на распознавание, и поллинг-цикл
|
||||
// перезапустит recognize. Авто-раскладку при этом не делаем — ручная
|
||||
// перепривязка всегда проходит через ревью с подтверждением (force_review).
|
||||
// Источник (раздача в qBittorrent) для этого должен быть на месте.
|
||||
func (w *Worker) Relink(ctx context.Context, id int64) error {
|
||||
w.mu.Lock()
|
||||
defer w.mu.Unlock()
|
||||
@@ -299,8 +299,8 @@ func (w *Worker) Relink(ctx context.Context, id int64) error {
|
||||
if err != nil {
|
||||
return fmt.Errorf("relink: %w", err)
|
||||
}
|
||||
if d.State != store.StateReverted {
|
||||
return fmt.Errorf("relink: download %d is in state %s (expected reverted)", id, d.State)
|
||||
if d.State != store.StateReverted && d.State != store.StateCancelled {
|
||||
return fmt.Errorf("relink: download %d is in state %s (expected reverted/cancelled)", id, d.State)
|
||||
}
|
||||
if !d.Infohash.Valid {
|
||||
return fmt.Errorf("relink: download %d has no infohash", id)
|
||||
@@ -325,7 +325,23 @@ func (w *Worker) Relink(ctx context.Context, id int64) error {
|
||||
return fmt.Errorf("relink: %w", err)
|
||||
}
|
||||
w.transition(ctx, *d, store.StateRecognizing, "", "")
|
||||
w.log.Info("relink: re-recognizing reverted download", "download_id", id)
|
||||
w.log.Info("relink: re-recognizing download", "download_id", id, "from", d.State)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Rerecognize перезапускает распознавание для задачи в review/deferred без
|
||||
// добавления подсказки: контекст и прежние подсказки уже накоплены. Поллинг-
|
||||
// цикл проведёт задачу recognizing → review заново.
|
||||
func (w *Worker) Rerecognize(ctx context.Context, id int64) error {
|
||||
w.mu.Lock()
|
||||
defer w.mu.Unlock()
|
||||
|
||||
d, err := w.requireReviewable(ctx, id, "rerecognize")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
w.log.Info("review: re-recognizing without hint", "download_id", id)
|
||||
w.transition(ctx, *d, store.StateRecognizing, "", "")
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -120,14 +120,58 @@ func TestRelink_RevertedToRecognizing(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRelink_RejectsNonReverted(t *testing.T) {
|
||||
func TestRelink_CancelledToRecognizing(t *testing.T) {
|
||||
st := newMemStore()
|
||||
st.put(completedDownload(1)) // не reverted
|
||||
d := revertedDownload(1)
|
||||
d.State = store.StateCancelled
|
||||
st.put(d)
|
||||
qb := &fakeQbt{torrents: []qbt.Torrent{{Hash: ihTest, Name: "Show", SavePath: "/d"}}}
|
||||
w := testWorkerWith(st, qb, &fakeRecognizer{result: seriesResult()}, nil)
|
||||
|
||||
if err := w.Relink(context.Background(), 1); err != nil {
|
||||
t.Fatalf("Relink: %v", err)
|
||||
}
|
||||
if st.downloads[1].State != store.StateRecognizing {
|
||||
t.Fatalf("state = %q, want recognizing", st.downloads[1].State)
|
||||
}
|
||||
if st.overrides[1][ovrForceReview] != "1" {
|
||||
t.Errorf("force_review override = %q, want 1", st.overrides[1][ovrForceReview])
|
||||
}
|
||||
}
|
||||
|
||||
func TestRelink_RejectsActiveState(t *testing.T) {
|
||||
st := newMemStore()
|
||||
st.put(completedDownload(1)) // не reverted/cancelled
|
||||
qb := &fakeQbt{torrents: []qbt.Torrent{{Hash: ihTest}}}
|
||||
w := testWorkerWith(st, qb, &fakeRecognizer{}, nil)
|
||||
|
||||
if err := w.Relink(context.Background(), 1); err == nil {
|
||||
t.Fatal("ожидали ошибку для не-reverted задачи, получили nil")
|
||||
t.Fatal("ожидали ошибку для не-reverted/cancelled задачи, получили nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRerecognize_ReviewToRecognizing(t *testing.T) {
|
||||
st := newMemStore()
|
||||
d := completedDownload(1)
|
||||
d.State = store.StateReview
|
||||
st.put(d)
|
||||
w := testWorkerWith(st, &fakeQbt{}, &fakeRecognizer{}, nil)
|
||||
|
||||
if err := w.Rerecognize(context.Background(), 1); err != nil {
|
||||
t.Fatalf("Rerecognize: %v", err)
|
||||
}
|
||||
if st.downloads[1].State != store.StateRecognizing {
|
||||
t.Fatalf("state = %q, want recognizing", st.downloads[1].State)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRerecognize_RejectsNonReview(t *testing.T) {
|
||||
st := newMemStore()
|
||||
st.put(completedDownload(1)) // completed, не review/deferred
|
||||
w := testWorkerWith(st, &fakeQbt{}, &fakeRecognizer{}, nil)
|
||||
|
||||
if err := w.Rerecognize(context.Background(), 1); err == nil {
|
||||
t.Fatal("ожидали ошибку для не-review задачи, получили nil")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user