mirror of
https://github.com/truewhile/MeBox.git
synced 2026-10-06 13:26:38 +08:00
fix subscription dedup availability tracking
This commit is contained in:
@@ -315,6 +315,7 @@ func (s *SubscriptionService) enqueueRSSSubscriptionCandidate(ctx context.Contex
|
|||||||
AllowExistingLibrary: sub.WashEnabled,
|
AllowExistingLibrary: sub.WashEnabled,
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
if IsDownloadDedupError(err) {
|
if IsDownloadDedupError(err) {
|
||||||
|
state.markCandidateAvailable(candidate)
|
||||||
state.markSeen(candidate.GUID)
|
state.markSeen(candidate.GUID)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -358,6 +359,12 @@ func (state *rssSubscriptionRunState) markTitleAvailable(title string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (state *rssSubscriptionRunState) markCandidateAvailable(candidate siteSearchCandidate) {
|
||||||
|
if state.washOff {
|
||||||
|
addSiteSearchCandidateAvailability(candidate, &state.availability)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (state *rssSubscriptionRunState) markSeen(guid string) {
|
func (state *rssSubscriptionRunState) markSeen(guid string) {
|
||||||
state.seen = append(state.seen, guid)
|
state.seen = append(state.seen, guid)
|
||||||
if state.seenSet != nil {
|
if state.seenSet != nil {
|
||||||
|
|||||||
@@ -226,7 +226,7 @@ func TestSubscriptionPendingDownloadAvailabilityIncludesLiveQBTorrents(t *testin
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSiteSearchDownloadDedupDoesNotMarkCandidateAvailable(t *testing.T) {
|
func TestSiteSearchDownloadDedupMarksCandidateAvailable(t *testing.T) {
|
||||||
db := newServiceTestDB(t, &model.DownloadTask{}, &model.Setting{})
|
db := newServiceTestDB(t, &model.DownloadTask{}, &model.Setting{})
|
||||||
repos := repository.New(db)
|
repos := repository.New(db)
|
||||||
sub := &model.Subscription{
|
sub := &model.Subscription{
|
||||||
@@ -281,10 +281,12 @@ func TestSiteSearchDownloadDedupDoesNotMarkCandidateAvailable(t *testing.T) {
|
|||||||
if title != "" {
|
if title != "" {
|
||||||
t.Fatalf("queued title = %q, want empty on dedup", title)
|
t.Fatalf("queued title = %q, want empty on dedup", title)
|
||||||
}
|
}
|
||||||
if _, ok := state.Availability.ExistingEpisodeKeys[episodeKey(1, 7)]; ok {
|
for _, episode := range []int{7, 8} {
|
||||||
t.Fatalf("deduped candidate should not mark E07 available: %#v", state.Availability.ExistingEpisodeKeys)
|
if _, ok := state.Availability.ExistingEpisodeKeys[episodeKey(1, episode)]; !ok {
|
||||||
|
t.Fatalf("deduped candidate should mark E%d available: %#v", episode, state.Availability.ExistingEpisodeKeys)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if len(state.Seen) != 0 {
|
if len(state.Seen) != 1 || state.Seen[0] != "site|mteam|nanyang-7-8" {
|
||||||
t.Fatalf("deduped candidate should not be marked seen: %#v", state.Seen)
|
t.Fatalf("deduped candidate should be marked seen: %#v", state.Seen)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,6 +68,7 @@ func (s *SubscriptionService) enqueueSiteSearchCandidate(ctx context.Context, su
|
|||||||
AllowExistingLibrary: sub.WashEnabled,
|
AllowExistingLibrary: sub.WashEnabled,
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
if IsDownloadDedupError(err) {
|
if IsDownloadDedupError(err) {
|
||||||
|
state.markCandidateAvailable(candidate)
|
||||||
s.logSiteSearchCandidateSkipped(sub, state, candidate, "download_dedup", mediaType, mediaCategory, savePath)
|
s.logSiteSearchCandidateSkipped(sub, state, candidate, "download_dedup", mediaType, mediaCategory, savePath)
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,77 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"go.uber.org/zap"
|
||||||
|
|
||||||
|
"github.com/ShukeBta/MediaStationGo/internal/model"
|
||||||
|
"github.com/ShukeBta/MediaStationGo/internal/repository"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestEnqueueSiteSearchDedupMarksEnglishRangeAvailableForChineseSubscription(t *testing.T) {
|
||||||
|
db := newServiceTestDB(t, &model.DownloadTask{}, &model.Setting{})
|
||||||
|
repos := repository.New(db)
|
||||||
|
if err := repos.Download.Create(t.Context(), &model.DownloadTask{
|
||||||
|
UserID: "u1",
|
||||||
|
Source: "qbittorrent",
|
||||||
|
URL: "https://pt.example/download?id=old",
|
||||||
|
Title: "Archives The Nanyang Mystery 2026 S01E07-S01E08 2160p WEB-DL",
|
||||||
|
SavePath: "/downloads/国产剧",
|
||||||
|
Status: "queued",
|
||||||
|
Progress: 0.1,
|
||||||
|
}); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
site := NewSiteService(zap.NewNop(), repos, "")
|
||||||
|
downloads := NewDownloadService(zap.NewNop(), repos, NewHub(zap.NewNop()), nil)
|
||||||
|
svc := NewSubscriptionService(nil, zap.NewNop(), repos, downloads, site, NewHub(zap.NewNop()))
|
||||||
|
sub := &model.Subscription{
|
||||||
|
Base: model.Base{ID: "sub-nanyang"},
|
||||||
|
UserID: "u1",
|
||||||
|
Name: "南部档案 自动订阅",
|
||||||
|
Filter: "南部档案 2026",
|
||||||
|
MediaType: "tv",
|
||||||
|
MediaCategory: "国产剧",
|
||||||
|
SavePath: "/downloads",
|
||||||
|
TotalEpisodes: 33,
|
||||||
|
}
|
||||||
|
state := &siteSearchRunState{
|
||||||
|
Keyword: "南部档案 2026",
|
||||||
|
SeenSet: map[string]struct{}{},
|
||||||
|
Availability: LocalAvailability{
|
||||||
|
TotalEpisodes: 33,
|
||||||
|
ExistingEpisodeKeys: map[string]struct{}{},
|
||||||
|
MissingEpisodeKeys: map[string]struct{}{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
candidate := siteSearchCandidate{
|
||||||
|
Item: SearchResult{
|
||||||
|
Title: "Archives The Nanyang Mystery 2026 S01E07-S01E08 2160p WEB-DL",
|
||||||
|
DownloadURL: "https://pt.example/download?id=new",
|
||||||
|
},
|
||||||
|
Download: "https://pt.example/download?id=new",
|
||||||
|
GUID: "site|m-team|nanyang-7-8",
|
||||||
|
Season: 1,
|
||||||
|
Episode: 7,
|
||||||
|
Episodes: []int{7, 8},
|
||||||
|
Pack: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
title, err := svc.enqueueSiteSearchCandidate(t.Context(), sub, candidate, state)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("enqueueSiteSearchCandidate returned %v, want dedup skip without error", err)
|
||||||
|
}
|
||||||
|
if title != "" {
|
||||||
|
t.Fatalf("title = %q, want empty because candidate was deduped", title)
|
||||||
|
}
|
||||||
|
for _, episode := range []int{7, 8} {
|
||||||
|
if _, ok := state.Availability.ExistingEpisodeKeys[episodeKey(1, episode)]; !ok {
|
||||||
|
t.Fatalf("availability missing E%d after dedup range: %#v", episode, state.Availability.ExistingEpisodeKeys)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if _, ok := state.SeenSet[candidate.GUID]; !ok {
|
||||||
|
t.Fatalf("seen set missing candidate guid after dedup")
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user