diff --git a/cmd/pkg/downloaders/aria2/aria2.go b/cmd/pkg/downloaders/aria2/aria2.go index ab79ae5c..977a1dfb 100644 --- a/cmd/pkg/downloaders/aria2/aria2.go +++ b/cmd/pkg/downloaders/aria2/aria2.go @@ -566,7 +566,7 @@ func addAria2Task(ctx context.Context, aria *arigo.Client, task downloader.Downl func (a Aria2) waitResult(ctx context.Context, aria **arigo.Client, task downloader.DownloadTask, taskDir string, gid string, progress downloader.ProgressReporter) (downloader.Result, error) { initialProgress := progress if task.SourceType() != "http" { - initialProgress = func(downloader.ProgressUpdate) error { return nil } + initialProgress = reportMetadataProgress(progress) } primaryGID := gid status, err := a.waitAria2(ctx, aria, task, primaryGID, initialProgress) @@ -970,6 +970,19 @@ func (a Aria2) waitAria2(ctx context.Context, aria **arigo.Client, task download } } +func reportMetadataProgress(progress downloader.ProgressReporter) downloader.ProgressReporter { + return func(update downloader.ProgressUpdate) error { + update.Downloaded = 0 + update.Total = nil + update.Bps = 0 + if update.Runtime != nil { + update.Runtime.Phase = "metadata" + update.Runtime.ETASeconds = nil + } + return progress(update) + } +} + func (a Aria2) getAria2Peers(ctx context.Context, aria **arigo.Client, gid string) []arigo.Peer { peers, err := (*aria).GetPeers(gid) if err == nil { @@ -1028,7 +1041,7 @@ func aria2Detail(status arigo.Status, peers []arigo.Peer, geoIP geoip.Resolver) uploadBps := int64(status.UploadSpeed) detail := &downloader.TaskRuntime{ Engine: "aria2", - Phase: aria2Phase(string(status.Status), status.FollowedBy), + Phase: aria2Phase(status), State: string(status.Status), ETASeconds: aria2ETA(status), Connections: &connections, @@ -1065,14 +1078,17 @@ func aria2ETA(status arigo.Status) *int64 { return &eta } -func aria2Phase(state string, followedBy []string) string { - switch state { +func aria2Phase(status arigo.Status) string { + switch string(status.Status) { case string(arigo.StatusWaiting): - if len(followedBy) > 0 { + if len(status.FollowedBy) > 0 { return "metadata" } return "downloading" case string(arigo.StatusActive): + if !hasAria2LocalFile(status.Files) && system.IsAria2MetadataPath(firstAria2FilePath(status.Files)) { + return "metadata" + } return "downloading" case "complete", string(arigo.StatusCompleted): return "completed" @@ -1083,6 +1099,13 @@ func aria2Phase(state string, followedBy []string) string { } } +func firstAria2FilePath(files []arigo.File) string { + if len(files) == 0 { + return "" + } + return files[0].Path +} + func aria2Trackers(announceList [][]string) []downloader.Tracker { trackers := make([]downloader.Tracker, 0, 20) seen := map[string]struct{}{} diff --git a/cmd/pkg/downloaders/aria2/aria2_test.go b/cmd/pkg/downloaders/aria2/aria2_test.go index 15206214..38225b86 100644 --- a/cmd/pkg/downloaders/aria2/aria2_test.go +++ b/cmd/pkg/downloaders/aria2/aria2_test.go @@ -342,16 +342,19 @@ func TestAria2StatusMatchingHelpers(t *testing.T) { } func TestAria2RuntimeConversionHelpers(t *testing.T) { - if got := aria2Phase(string(arigo.StatusWaiting), []string{"child"}); got != "metadata" { + if got := aria2Phase(arigo.Status{Status: arigo.StatusWaiting, FollowedBy: []string{"child"}}); got != "metadata" { t.Fatalf("expected metadata phase, got %s", got) } - if got := aria2Phase(string(arigo.StatusWaiting), nil); got != "downloading" { + if got := aria2Phase(arigo.Status{Status: arigo.StatusActive, Files: []arigo.File{{Path: "[METADATA]fixture"}}}); got != "metadata" { + t.Fatalf("expected active metadata phase, got %s", got) + } + if got := aria2Phase(arigo.Status{Status: arigo.StatusWaiting}); got != "downloading" { t.Fatalf("expected waiting download phase, got %s", got) } - if got := aria2Phase(string(arigo.StatusCompleted), nil); got != "completed" { + if got := aria2Phase(arigo.Status{Status: arigo.StatusCompleted}); got != "completed" { t.Fatalf("expected completed phase, got %s", got) } - if got := aria2Phase(string(arigo.StatusRemoved), nil); got != "error" { + if got := aria2Phase(arigo.Status{Status: arigo.StatusRemoved}); got != "error" { t.Fatalf("expected removed phase error, got %s", got) } if aria2PeerProgress(arigo.Peer{}) != nil { @@ -385,6 +388,36 @@ func TestAria2RuntimeConversionHelpers(t *testing.T) { } } +func TestReportMetadataProgressKeepsRuntimeButHidesMetadataBytes(t *testing.T) { + total := int64(14151) + var got downloader.ProgressUpdate + reporter := reportMetadataProgress(func(update downloader.ProgressUpdate) error { + got = update + return nil + }) + + err := reporter(downloader.ProgressUpdate{ + Downloaded: 1024, + Total: &total, + Bps: 512, + Runtime: &downloader.TaskRuntime{ + Engine: "aria2", + Phase: "downloading", + ETASeconds: &total, + Trackers: []downloader.Tracker{{URL: "udp://tracker.example:1337/announce"}}, + }, + }) + if err != nil { + t.Fatal(err) + } + if got.Downloaded != 0 || got.Total != nil || got.Bps != 0 { + t.Fatalf("metadata progress must not expose metadata bytes, got %#v", got) + } + if got.Runtime == nil || got.Runtime.Phase != "metadata" || got.Runtime.ETASeconds != nil || len(got.Runtime.Trackers) != 1 { + t.Fatalf("expected metadata runtime with trackers, got %#v", got.Runtime) + } +} + func TestAria2FilesDetailAndResultHelpers(t *testing.T) { taskDir := t.TempDir() contentPath := filepath.Join(taskDir, "Torrent", "movie.mkv")