diff --git a/internal/service/download_add.go b/internal/service/download_add.go index d9d7b69..a2362ad 100644 --- a/internal/service/download_add.go +++ b/internal/service/download_add.go @@ -207,7 +207,7 @@ func localMediaRowsMatchDownloadTitle(title string, rows []model.Media) bool { } } if hasSeriesPack { - return true + return len(wanted) == 0 } for _, ref := range wanted { if _, ok := existing[episodeKey(ref.Season, ref.Episode)]; !ok { diff --git a/internal/service/download_add_existing_test.go b/internal/service/download_add_existing_test.go index e562a6c..96d69d5 100644 --- a/internal/service/download_add_existing_test.go +++ b/internal/service/download_add_existing_test.go @@ -145,6 +145,49 @@ func TestAddDownloadWithMetaSkipsEpisodeRangeWhenFullyInLibrary(t *testing.T) { } } +func TestAddDownloadWithMetaQueuesExplicitEpisodeWhenOnlySeriesPackInLibrary(t *testing.T) { + var addCalls int32 + qb := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + switch r.URL.Path { + case "/api/v2/auth/login": + _, _ = w.Write([]byte("Ok.")) + case "/api/v2/torrents/info": + http.Error(w, "temporary list unavailable", http.StatusInternalServerError) + case "/api/v2/torrents/add": + atomic.AddInt32(&addCalls, 1) + _, _ = w.Write([]byte("Ok.")) + default: + http.NotFound(w, r) + } + })) + defer qb.Close() + + db := newServiceTestDB(t, &model.Media{}, &model.DownloadTask{}, &model.DownloadClient{}, &model.Setting{}) + repos := repository.New(db) + configureTestDefaultQB(t, repos, qb.URL) + if err := db.Create(&model.Media{ + Title: "Archives The Nanyang Mystery S01 Complete", + Path: "/media/tv/Archives The Nanyang Mystery/Season 01/Archives The Nanyang Mystery S01 Complete.mkv", + }).Error; err != nil { + t.Fatal(err) + } + + svc := NewDownloadService(zap.NewNop(), repos, NewHub(zap.NewNop()), nil) + task, err := svc.AddDownloadWithMeta(t.Context(), "u1", "magnet:?xt=urn:btih:fefefefefefefefefefefefefefefefefefefefe&dn=Archives+The+Nanyang+Mystery+2026+S01E29-E33", "/downloads/tv", DownloadTaskMeta{ + SubscriptionID: "sub-nanyang", + Title: "Archives The Nanyang Mystery 2026 S01E29-E33 2160p WEB-DL", + }) + if err != nil { + t.Fatalf("AddDownloadWithMeta returned %v, want queued because explicit missing episodes are not proven by a pack row", err) + } + if task == nil { + t.Fatal("task = nil, want queued task") + } + if got := atomic.LoadInt32(&addCalls); got != 1 { + t.Fatalf("qb add calls = %d, want 1", got) + } +} + func TestAddDownloadWithMetaSkipsExistingLocalEpisodeWithReleaseGroup(t *testing.T) { db := newServiceTestDB(t, &model.Media{}, &model.DownloadTask{}, &model.Setting{}, &model.DownloadClient{}) repos := repository.New(db)