fix(bt): honor selected files when creating resolved tasks

This commit is contained in:
Levi committed 2026-09-17 00:03:03 +08:00
1 parent 16f2d30476
commit 5bb040eaaf
11 files changed
+265 -20

No files matched your search

+1 -1
View File
@@ -156,7 +156,7 @@ func (s *Service) createTask(ctx *Context) *Response {
err error
)
if request.Rid != "" {
taskID, err = s.Downloader.Create(request.Rid)
taskID, err = s.Downloader.CreateWithOptions(request.Rid, request.Opts)
} else if request.Req != nil {
taskID, err = s.Downloader.CreateDirect(request.Req, request.Opts)
} else {
+24
View File
@@ -582,12 +582,36 @@ func (d *Downloader) CreateDirectBatch(req *base.CreateTaskBatch) (taskId []stri
}
func (d *Downloader) Create(rrId string) (taskId string, err error) {
return d.CreateWithOptions(rrId, nil)
}
// CreateWithOptions creates a resolved task using the submitted options, or the
// resolve-time options when opts is nil.
func (d *Downloader) CreateWithOptions(rrId string, opts *base.Options) (taskId string, err error) {
d.fetcherMapLock.RLock()
fetcher, ok := d.fetcherCache[rrId]
d.fetcherMapLock.RUnlock()
if !ok {
return "", errors.New("invalid resource id")
}
if opts != nil {
opts, err = d.initOptions(opts.Clone())
if err != nil {
return "", err
}
if res := fetcher.Meta().Res; res != nil {
for _, index := range opts.SelectFiles {
if index < 0 || index >= len(res.Files) {
return "", fmt.Errorf("invalid selected file index: %d", index)
}
}
opts.InitSelectFiles(len(res.Files))
}
fetcher.Meta().Opts = opts
}
if res := fetcher.Meta().Res; res != nil {
res.CalcSize(fetcher.Meta().Opts.SelectFiles)
}
defer func() {
d.fetcherMapLock.Lock()
delete(d.fetcherCache, rrId)
+64
View File
@@ -0,0 +1,64 @@
package download
import (
"reflect"
"testing"
"github.com/GopeedLab/gopeed/internal/fetcher"
"github.com/GopeedLab/gopeed/pkg/base"
)
func TestCreateResolvedFileSelection(t *testing.T) {
for _, tc := range []struct {
name string
selection []int
submit bool
want []int
size int64
}{
{"partial", []int{2, 0}, true, []int{2, 0}, 40},
{"default all", nil, true, []int{0, 1, 2}, 60},
{"legacy retains resolve options", nil, false, []int{1}, 20},
} {
t.Run(tc.name, func(t *testing.T) {
manager := &generationTestManager{}
d := NewDownloader(&DownloaderConfig{FetchManagers: []fetcher.FetcherManager{manager}})
if err := d.Setup(); err != nil {
t.Fatal(err)
}
defer d.Clear()
d.cfg.MaxRunning = 0
original := &base.Options{Path: t.TempDir(), SelectFiles: []int{1}}
rr, err := d.Resolve(&base.Request{URL: "generation://selection"}, original)
if err != nil {
t.Fatal(err)
}
d.fetcherCache[rr.ID].Meta().Res = &base.Resource{Name: "bundle", Files: []*base.FileInfo{
{Name: "a", Size: 10}, {Name: "b", Size: 20}, {Name: "c", Size: 30},
}}
var id string
if tc.submit {
id, err = d.CreateWithOptions(rr.ID, &base.Options{Path: original.Path, SelectFiles: tc.selection})
} else {
id, err = d.Create(rr.ID)
}
if err != nil {
t.Fatal(err)
}
task := d.GetTask(id)
if !reflect.DeepEqual(task.Meta.Opts.SelectFiles, tc.want) {
t.Fatalf("selection = %v, want %v", task.Meta.Opts.SelectFiles, tc.want)
}
if task.Meta.Res.Size != tc.size {
t.Fatalf("queued size = %d, want %d", task.Meta.Res.Size, tc.size)
}
var saved Task
if _, err := d.storage.Get(bucketTask, id, &saved); err != nil {
t.Fatal(err)
}
if saved.Meta.Res.Size != tc.size || !reflect.DeepEqual(saved.Meta.Opts.SelectFiles, tc.want) {
t.Fatalf("persisted metadata = %+v", saved.Meta)
}
})
}
}
+1 -1
View File
@@ -315,7 +315,7 @@ func registerTools(server *mcp.Server, downloader *download.Downloader) {
)
switch {
case input.RID != "":
id, err = downloader.Create(input.RID)
id, err = downloader.CreateWithOptions(input.RID, input.Opts.baseOptions())
case input.Req != nil:
var req *base.Request
req, err = input.Req.baseRequest()
+1 -1
View File
@@ -43,7 +43,7 @@ func CreateTask(w http.ResponseWriter, r *http.Request) {
err error
)
if req.Rid != "" {
taskId, err = Downloader.Create(req.Rid)
taskId, err = Downloader.CreateWithOptions(req.Rid, req.Opts)
} else if req.Req != nil {
taskId, err = Downloader.CreateDirect(req.Req, req.Opts)
} else {
+28
View File
@@ -120,6 +120,34 @@ func TestCreateTask(t *testing.T) {
})
}
func TestCreateResolvedTaskUsesSubmittedOptions(t *testing.T) {
doTest(func() {
done := make(chan *download.Event, 1)
Downloader.Listener(func(event *download.Event) {
if event.Key == download.EventKeyFinally {
done <- event
}
})
resolved := httpRequestCheckOk[*download.ResolveResult](http.MethodPost, "/api/v1/resolve", resolveReq)
opts := createOpts.Clone()
opts.Name = "selected-options.bin"
opts.SelectFiles = []int{0}
id := httpRequestCheckOk[string](http.MethodPost, "/api/v1/tasks", &model.CreateTask{Rid: resolved.ID, Opts: opts})
select {
case <-done:
case <-time.After(10 * time.Second):
t.Fatal("resolved task did not finish")
}
task := Downloader.GetTask(id)
if task.Meta.Opts.Name != opts.Name || !reflect.DeepEqual(task.Meta.Opts.SelectFiles, opts.SelectFiles) {
t.Fatalf("submitted options were lost: %+v", task.Meta.Opts)
}
if got := test.FileMd5(filepath.Join(opts.Path, opts.Name)); got != test.FileMd5(test.BuildFile) {
t.Fatalf("download using submitted options has wrong checksum: %s", got)
}
})
}
func TestCreateDirectTask(t *testing.T) {
doTest(func() {
var wg sync.WaitGroup