mirror of
https://github.com/GopeedLab/gopeed.git
synced 2026-11-12 19:33:41 +00:00
feat: add config for auto-torrent (#1219)
This commit is contained in:
1 parent
a3c1f84c2c
commit
7b840b4261
31 files changed
+742
-131
No files matched your search
+24
-11
@@ -178,17 +178,15 @@ type CreateTaskBatchItem struct {
|
||||
type DownloaderStoreConfig struct {
|
||||
FirstLoad bool `json:"-"` // FirstLoad is the flag that the config is first time init and not from store
|
||||
|
||||
DownloadDir string `json:"downloadDir"` // DownloadDir is the default directory to save the downloaded files
|
||||
MaxRunning int `json:"maxRunning"` // MaxRunning is the max running download count
|
||||
ProtocolConfig map[string]any `json:"protocolConfig"` // ProtocolConfig is special config for each protocol
|
||||
Extra map[string]any `json:"extra"`
|
||||
Proxy *DownloaderProxyConfig `json:"proxy"`
|
||||
Webhook *WebhookConfig `json:"webhook"` // Webhook is the webhook configuration
|
||||
Archive *ArchiveConfig `json:"archive"` // Archive is the archive extraction configuration
|
||||
|
||||
// New fields added for auto-management
|
||||
AutoDeleteTorrents bool `json:"autoDeleteTorrents"` // AutoDeleteTorrents enables automatic deletion of .torrent files after download
|
||||
AutoCleanMissingFiles bool `json:"autoCleanMissingFiles"` // AutoCleanMissingFiles enables automatic cleanup of tasks with missing files
|
||||
DownloadDir string `json:"downloadDir"` // DownloadDir is the default directory to save the downloaded files
|
||||
MaxRunning int `json:"maxRunning"` // MaxRunning is the max running download count
|
||||
ProtocolConfig map[string]any `json:"protocolConfig"` // ProtocolConfig is special config for each protocol
|
||||
Extra map[string]any `json:"extra"`
|
||||
Proxy *DownloaderProxyConfig `json:"proxy"`
|
||||
Webhook *WebhookConfig `json:"webhook"` // Webhook is the webhook configuration
|
||||
AutoTorrent *AutoTorrentConfig `json:"autoTorrent"` // AutoTorrent is the auto torrent task creation configuration
|
||||
Archive *ArchiveConfig `json:"archive"` // Archive is the archive extraction configuration
|
||||
AutoDeleteMissingFileTasks bool `json:"autoDeleteMissingFileTasks"` // AutoDeleteMissingFileTasks enables automatic deletion of tasks with missing files
|
||||
}
|
||||
|
||||
func (cfg *DownloaderStoreConfig) Init() *DownloaderStoreConfig {
|
||||
@@ -204,6 +202,12 @@ func (cfg *DownloaderStoreConfig) Init() *DownloaderStoreConfig {
|
||||
if cfg.Webhook == nil {
|
||||
cfg.Webhook = &WebhookConfig{}
|
||||
}
|
||||
if cfg.AutoTorrent == nil {
|
||||
cfg.AutoTorrent = &AutoTorrentConfig{
|
||||
Enable: false,
|
||||
DeleteAfterDownload: false,
|
||||
}
|
||||
}
|
||||
if cfg.Archive == nil {
|
||||
cfg.Archive = &ArchiveConfig{
|
||||
AutoExtract: false,
|
||||
@@ -235,6 +239,9 @@ func (cfg *DownloaderStoreConfig) Merge(beforeCfg *DownloaderStoreConfig) *Downl
|
||||
if cfg.Webhook == nil {
|
||||
cfg.Webhook = beforeCfg.Webhook
|
||||
}
|
||||
if cfg.AutoTorrent == nil {
|
||||
cfg.AutoTorrent = beforeCfg.AutoTorrent
|
||||
}
|
||||
if cfg.Archive == nil {
|
||||
cfg.Archive = beforeCfg.Archive
|
||||
}
|
||||
@@ -247,6 +254,12 @@ type WebhookConfig struct {
|
||||
URLs []string `json:"urls"` // URLs is the list of webhook URLs
|
||||
}
|
||||
|
||||
// AutoTorrentConfig is the auto torrent task creation configuration
|
||||
type AutoTorrentConfig struct {
|
||||
Enable bool `json:"enable"` // Enable enables automatic BT task creation when downloading .torrent files
|
||||
DeleteAfterDownload bool `json:"deleteAfterDownload"` // DeleteAfterDownload deletes the .torrent file after BT task creation
|
||||
}
|
||||
|
||||
// ArchiveConfig is the archive extraction configuration
|
||||
type ArchiveConfig struct {
|
||||
AutoExtract bool `json:"autoExtract"` // AutoExtract enables automatic extraction of archives after download
|
||||
|
||||
@@ -19,6 +19,10 @@ func TestDownloaderStoreConfig_Init(t *testing.T) {
|
||||
ProtocolConfig: map[string]any{},
|
||||
Proxy: &DownloaderProxyConfig{},
|
||||
Webhook: &WebhookConfig{},
|
||||
AutoTorrent: &AutoTorrentConfig{
|
||||
Enable: false,
|
||||
DeleteAfterDownload: false,
|
||||
},
|
||||
Archive: &ArchiveConfig{
|
||||
AutoExtract: false,
|
||||
DeleteAfterExtract: false,
|
||||
@@ -35,6 +39,10 @@ func TestDownloaderStoreConfig_Init(t *testing.T) {
|
||||
ProtocolConfig: map[string]any{},
|
||||
Proxy: &DownloaderProxyConfig{},
|
||||
Webhook: &WebhookConfig{},
|
||||
AutoTorrent: &AutoTorrentConfig{
|
||||
Enable: false,
|
||||
DeleteAfterDownload: false,
|
||||
},
|
||||
Archive: &ArchiveConfig{
|
||||
AutoExtract: false,
|
||||
DeleteAfterExtract: false,
|
||||
@@ -55,6 +63,10 @@ func TestDownloaderStoreConfig_Init(t *testing.T) {
|
||||
},
|
||||
Proxy: &DownloaderProxyConfig{},
|
||||
Webhook: &WebhookConfig{},
|
||||
AutoTorrent: &AutoTorrentConfig{
|
||||
Enable: false,
|
||||
DeleteAfterDownload: false,
|
||||
},
|
||||
Archive: &ArchiveConfig{
|
||||
AutoExtract: false,
|
||||
DeleteAfterExtract: false,
|
||||
@@ -75,6 +87,33 @@ func TestDownloaderStoreConfig_Init(t *testing.T) {
|
||||
Enable: true,
|
||||
},
|
||||
Webhook: &WebhookConfig{},
|
||||
AutoTorrent: &AutoTorrentConfig{
|
||||
Enable: false,
|
||||
DeleteAfterDownload: false,
|
||||
},
|
||||
Archive: &ArchiveConfig{
|
||||
AutoExtract: false,
|
||||
DeleteAfterExtract: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"Init AutoTorrent",
|
||||
&DownloaderStoreConfig{
|
||||
AutoTorrent: &AutoTorrentConfig{
|
||||
Enable: true,
|
||||
DeleteAfterDownload: true,
|
||||
},
|
||||
},
|
||||
&DownloaderStoreConfig{
|
||||
MaxRunning: 5,
|
||||
ProtocolConfig: map[string]any{},
|
||||
Proxy: &DownloaderProxyConfig{},
|
||||
Webhook: &WebhookConfig{},
|
||||
AutoTorrent: &AutoTorrentConfig{
|
||||
Enable: true,
|
||||
DeleteAfterDownload: true,
|
||||
},
|
||||
Archive: &ArchiveConfig{
|
||||
AutoExtract: false,
|
||||
DeleteAfterExtract: false,
|
||||
@@ -94,6 +133,10 @@ func TestDownloaderStoreConfig_Init(t *testing.T) {
|
||||
ProtocolConfig: map[string]any{},
|
||||
Proxy: &DownloaderProxyConfig{},
|
||||
Webhook: &WebhookConfig{},
|
||||
AutoTorrent: &AutoTorrentConfig{
|
||||
Enable: false,
|
||||
DeleteAfterDownload: false,
|
||||
},
|
||||
Archive: &ArchiveConfig{
|
||||
AutoExtract: true,
|
||||
DeleteAfterExtract: false,
|
||||
@@ -111,6 +154,7 @@ func TestDownloaderStoreConfig_Init(t *testing.T) {
|
||||
Extra: tt.fields.Extra,
|
||||
Proxy: tt.fields.Proxy,
|
||||
Webhook: tt.fields.Webhook,
|
||||
AutoTorrent: tt.fields.AutoTorrent,
|
||||
Archive: tt.fields.Archive,
|
||||
}
|
||||
if got := cfg.Init(); !reflect.DeepEqual(got, tt.want) {
|
||||
@@ -286,6 +330,45 @@ func TestDownloaderStoreConfig_Merge(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"Merge AutoTorrent No Override",
|
||||
&DownloaderStoreConfig{
|
||||
AutoTorrent: &AutoTorrentConfig{
|
||||
Enable: true,
|
||||
},
|
||||
},
|
||||
args{
|
||||
beforeCfg: &DownloaderStoreConfig{
|
||||
AutoTorrent: &AutoTorrentConfig{
|
||||
Enable: false,
|
||||
DeleteAfterDownload: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
&DownloaderStoreConfig{
|
||||
AutoTorrent: &AutoTorrentConfig{
|
||||
Enable: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"Merge AutoTorrent Override",
|
||||
&DownloaderStoreConfig{},
|
||||
args{
|
||||
beforeCfg: &DownloaderStoreConfig{
|
||||
AutoTorrent: &AutoTorrentConfig{
|
||||
Enable: true,
|
||||
DeleteAfterDownload: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
&DownloaderStoreConfig{
|
||||
AutoTorrent: &AutoTorrentConfig{
|
||||
Enable: true,
|
||||
DeleteAfterDownload: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"Merge Archive No Override",
|
||||
&DownloaderStoreConfig{
|
||||
@@ -336,6 +419,7 @@ func TestDownloaderStoreConfig_Merge(t *testing.T) {
|
||||
Extra: tt.fields.Extra,
|
||||
Proxy: tt.fields.Proxy,
|
||||
Webhook: tt.fields.Webhook,
|
||||
AutoTorrent: tt.fields.AutoTorrent,
|
||||
Archive: tt.fields.Archive,
|
||||
}
|
||||
if got := cfg.Merge(tt.args.beforeCfg); !reflect.DeepEqual(got, tt.want) {
|
||||
|
||||
@@ -283,7 +283,7 @@ func (d *Downloader) cleanupNonExistingTasks() {
|
||||
}
|
||||
|
||||
// If the feature is disabled, do nothing
|
||||
if !cfg.AutoCleanMissingFiles {
|
||||
if !cfg.AutoDeleteMissingFileTasks {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -957,10 +957,25 @@ func (d *Downloader) watch(task *Task) {
|
||||
|
||||
if e, ok := task.Meta.Opts.Extra.(*http.OptsExtra); ok {
|
||||
downloadFilePath := task.Meta.SingleFilepath()
|
||||
if e.AutoTorrent && strings.HasSuffix(downloadFilePath, ".torrent") {
|
||||
|
||||
cfg, err := d.GetConfig()
|
||||
shouldDelete := err == nil && cfg.AutoDeleteTorrents
|
||||
cfg, _ := d.GetConfig()
|
||||
|
||||
// Determine if auto-torrent is enabled (use global config if not explicitly set)
|
||||
autoTorrentEnabled := false
|
||||
if e.AutoTorrent != nil {
|
||||
autoTorrentEnabled = *e.AutoTorrent
|
||||
} else if cfg != nil && cfg.AutoTorrent != nil {
|
||||
autoTorrentEnabled = cfg.AutoTorrent.Enable
|
||||
}
|
||||
|
||||
if autoTorrentEnabled && strings.HasSuffix(downloadFilePath, ".torrent") {
|
||||
// Determine if should delete torrent file after creating BT task
|
||||
shouldDelete := false
|
||||
if e.DeleteTorrentAfterDownload != nil {
|
||||
shouldDelete = *e.DeleteTorrentAfterDownload
|
||||
} else if cfg != nil && cfg.AutoTorrent != nil {
|
||||
shouldDelete = cfg.AutoTorrent.DeleteAfterDownload
|
||||
}
|
||||
|
||||
go func() {
|
||||
_, err2 := d.CreateDirect(
|
||||
@@ -973,18 +988,26 @@ func (d *Downloader) watch(task *Task) {
|
||||
})
|
||||
if err2 != nil {
|
||||
d.Logger.Error().Err(err2).Msgf("auto create torrent task failed, task id: %s", task.ID)
|
||||
} else {
|
||||
return
|
||||
}
|
||||
|
||||
if shouldDelete {
|
||||
d.Delete(&TaskFilter{IDs: []string{task.ID}}, true)
|
||||
}
|
||||
if shouldDelete {
|
||||
d.Delete(&TaskFilter{IDs: []string{task.ID}}, true)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// Determine if auto-extract is enabled (use global config if not explicitly set)
|
||||
autoExtractEnabled := false
|
||||
if e.AutoExtract != nil {
|
||||
autoExtractEnabled = *e.AutoExtract
|
||||
} else if cfg != nil && cfg.Archive != nil {
|
||||
autoExtractEnabled = cfg.Archive.AutoExtract
|
||||
}
|
||||
|
||||
// Auto-extract archive files using the extraction queue
|
||||
// This ensures only one extraction runs at a time to prevent resource exhaustion
|
||||
if e.AutoExtract && isArchiveFile(downloadFilePath) {
|
||||
if autoExtractEnabled && isArchiveFile(downloadFilePath) {
|
||||
d.enqueueExtraction(task, downloadFilePath, e)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
"github.com/GopeedLab/gopeed/internal/test"
|
||||
"github.com/GopeedLab/gopeed/pkg/base"
|
||||
"github.com/GopeedLab/gopeed/pkg/protocol/http"
|
||||
"github.com/GopeedLab/gopeed/pkg/util"
|
||||
)
|
||||
|
||||
var testDownloadOpt = &base.Options{
|
||||
@@ -1037,7 +1038,7 @@ func TestDownloader_AutoExtractWithProgress(t *testing.T) {
|
||||
Name: "archive.zip",
|
||||
Extra: http.OptsExtra{
|
||||
Connections: 1,
|
||||
AutoExtract: true,
|
||||
AutoExtract: util.BoolPtr(true),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
@@ -1164,7 +1165,7 @@ func TestDownloader_AutoExtractWithDeleteAfterExtract(t *testing.T) {
|
||||
Name: "archive.zip",
|
||||
Extra: http.OptsExtra{
|
||||
Connections: 1,
|
||||
AutoExtract: true,
|
||||
AutoExtract: util.BoolPtr(true),
|
||||
DeleteAfterExtract: true,
|
||||
},
|
||||
})
|
||||
@@ -1258,7 +1259,7 @@ func TestDownloader_AutoExtractError(t *testing.T) {
|
||||
Name: "corrupt.zip",
|
||||
Extra: http.OptsExtra{
|
||||
Connections: 1,
|
||||
AutoExtract: true,
|
||||
AutoExtract: util.BoolPtr(true),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
@@ -2332,3 +2333,318 @@ func TestDownloader_CheckMultiPartArchiveReady_EmptyBaseName(t *testing.T) {
|
||||
t.Error("checkMultiPartArchiveReady() should return true for non-multi-part files")
|
||||
}
|
||||
}
|
||||
|
||||
// startTestTorrentServer starts a simple HTTP server that serves a torrent file
|
||||
func startTestTorrentServer(torrentPath string) net.Listener {
|
||||
listener, err := net.Listen("tcp", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
server := &gohttp.Server{
|
||||
Handler: gohttp.HandlerFunc(func(w gohttp.ResponseWriter, r *gohttp.Request) {
|
||||
data, err := os.ReadFile(torrentPath)
|
||||
if err != nil {
|
||||
w.WriteHeader(gohttp.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/x-bittorrent")
|
||||
w.Header().Set("Content-Disposition", "attachment; filename=ubuntu.torrent")
|
||||
w.Write(data)
|
||||
}),
|
||||
}
|
||||
go server.Serve(listener)
|
||||
return listener
|
||||
}
|
||||
|
||||
// TestDownloader_AutoTorrent tests the auto-torrent functionality
|
||||
// When a .torrent file is downloaded with AutoTorrent enabled, it should automatically create a BT task
|
||||
func TestDownloader_AutoTorrent(t *testing.T) {
|
||||
// Path to the test torrent file
|
||||
torrentPath := "../../internal/protocol/bt/testdata/ubuntu-22.04-live-server-amd64.iso.torrent"
|
||||
if _, err := os.Stat(torrentPath); os.IsNotExist(err) {
|
||||
t.Skip("Test torrent file not found, skipping test")
|
||||
}
|
||||
|
||||
// Start a simple HTTP server to serve the torrent file
|
||||
server := startTestTorrentServer(torrentPath)
|
||||
defer server.Close()
|
||||
|
||||
// Create a temporary directory for the test
|
||||
tempDir, err := os.MkdirTemp("", "auto_torrent_test")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(tempDir)
|
||||
|
||||
// Create downloader
|
||||
downloader := NewDownloader(nil)
|
||||
if err := downloader.Setup(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer func() {
|
||||
// Delete all tasks before clearing to avoid panic from BT tasks trying to access deleted resources
|
||||
downloader.Delete(nil, true)
|
||||
downloader.Clear()
|
||||
}()
|
||||
|
||||
// Track created tasks
|
||||
btTaskCreated := make(chan struct{}, 1)
|
||||
var originalTaskId string
|
||||
|
||||
downloader.Listener(func(event *Event) {
|
||||
if event.Key == EventKeyStart {
|
||||
// A new task started - if it's not the original, it's the BT task
|
||||
if event.Task != nil && event.Task.ID != originalTaskId && originalTaskId != "" {
|
||||
select {
|
||||
case btTaskCreated <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// Create request to download the torrent file
|
||||
req := &base.Request{
|
||||
URL: "http://" + server.Addr().String() + "/ubuntu.torrent",
|
||||
}
|
||||
|
||||
// Create task with AutoTorrent enabled
|
||||
downloadDir := tempDir + "/downloads"
|
||||
originalTaskId, err = downloader.CreateDirect(req, &base.Options{
|
||||
Path: downloadDir,
|
||||
Name: "ubuntu.torrent",
|
||||
Extra: http.OptsExtra{
|
||||
Connections: 1,
|
||||
AutoTorrent: util.BoolPtr(true),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
t.Logf("Original task ID: %s", originalTaskId)
|
||||
|
||||
// Wait for BT task to be created (with timeout)
|
||||
select {
|
||||
case <-btTaskCreated:
|
||||
t.Log("BT task created")
|
||||
case <-time.After(10 * time.Second):
|
||||
t.Log("Timeout waiting for BT task creation")
|
||||
}
|
||||
|
||||
// Give a small buffer for task creation to complete
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
|
||||
// Verify that a BT task was created
|
||||
tasks := downloader.GetTasks()
|
||||
|
||||
// At minimum, we should have 2 tasks: the original torrent download and the BT task
|
||||
if len(tasks) < 2 {
|
||||
t.Errorf("Expected at least 2 tasks (torrent download + BT task), got %d", len(tasks))
|
||||
} else {
|
||||
t.Logf("Successfully created %d tasks", len(tasks))
|
||||
}
|
||||
}
|
||||
|
||||
// TestDownloader_AutoTorrentWithDelete tests the auto-torrent with DeleteTorrentAfterDownload option
|
||||
func TestDownloader_AutoTorrentWithDelete(t *testing.T) {
|
||||
// Path to the test torrent file
|
||||
torrentPath := "../../internal/protocol/bt/testdata/ubuntu-22.04-live-server-amd64.iso.torrent"
|
||||
if _, err := os.Stat(torrentPath); os.IsNotExist(err) {
|
||||
t.Skip("Test torrent file not found, skipping test")
|
||||
}
|
||||
|
||||
// Start a simple HTTP server to serve the torrent file
|
||||
server := startTestTorrentServer(torrentPath)
|
||||
defer server.Close()
|
||||
|
||||
// Create a temporary directory for the test
|
||||
tempDir, err := os.MkdirTemp("", "auto_torrent_delete_test")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(tempDir)
|
||||
|
||||
// Create downloader
|
||||
downloader := NewDownloader(nil)
|
||||
if err := downloader.Setup(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer func() {
|
||||
// Delete all tasks before clearing to avoid panic from BT tasks trying to access deleted resources
|
||||
downloader.Delete(nil, true)
|
||||
downloader.Clear()
|
||||
}()
|
||||
|
||||
// Track task events
|
||||
var originalTaskId string
|
||||
originalTaskDeleted := make(chan struct{}, 1)
|
||||
btTaskCreated := make(chan struct{}, 1)
|
||||
|
||||
downloader.Listener(func(event *Event) {
|
||||
if event.Key == EventKeyStart {
|
||||
// BT task was created and started
|
||||
if event.Task != nil && event.Task.ID != originalTaskId && originalTaskId != "" {
|
||||
select {
|
||||
case btTaskCreated <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
if event.Key == EventKeyDelete {
|
||||
// Check if the deleted task is the original torrent task
|
||||
if event.Task != nil && event.Task.ID == originalTaskId {
|
||||
select {
|
||||
case originalTaskDeleted <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// Create request to download the torrent file
|
||||
req := &base.Request{
|
||||
URL: "http://" + server.Addr().String() + "/ubuntu.torrent",
|
||||
}
|
||||
|
||||
// Create task with AutoTorrent and DeleteTorrentAfterDownload enabled
|
||||
downloadDir := tempDir + "/downloads"
|
||||
originalTaskId, err = downloader.CreateDirect(req, &base.Options{
|
||||
Path: downloadDir,
|
||||
Name: "ubuntu.torrent",
|
||||
Extra: http.OptsExtra{
|
||||
Connections: 1,
|
||||
AutoTorrent: util.BoolPtr(true),
|
||||
DeleteTorrentAfterDownload: util.BoolPtr(true),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
t.Logf("Original task ID: %s", originalTaskId)
|
||||
|
||||
// Wait for original task to be deleted (this happens after BT task is created)
|
||||
select {
|
||||
case <-originalTaskDeleted:
|
||||
t.Log("Original torrent task was deleted as expected")
|
||||
case <-time.After(10 * time.Second):
|
||||
// Check manually if the task still exists
|
||||
originalTask := downloader.GetTask(originalTaskId)
|
||||
if originalTask != nil {
|
||||
t.Error("Original torrent task should have been deleted but still exists")
|
||||
} else {
|
||||
t.Log("Original torrent task was deleted (detected via GetTask)")
|
||||
}
|
||||
}
|
||||
|
||||
// Give a moment for BT task creation
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
|
||||
// Verify that original task is deleted and BT task exists
|
||||
originalTask := downloader.GetTask(originalTaskId)
|
||||
if originalTask != nil {
|
||||
t.Error("Original torrent task should have been deleted")
|
||||
}
|
||||
|
||||
// Verify remaining tasks (should have at least the BT task)
|
||||
tasks := downloader.GetTasks()
|
||||
t.Logf("Remaining tasks: %d", len(tasks))
|
||||
|
||||
// At least one task should remain (the BT task)
|
||||
if len(tasks) == 0 {
|
||||
t.Error("Expected at least one task (BT task) to remain")
|
||||
}
|
||||
|
||||
// None of the remaining tasks should be the original torrent task
|
||||
for _, task := range tasks {
|
||||
if task.ID == originalTaskId {
|
||||
t.Error("Original torrent task should have been deleted")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestDownloader_AutoTorrentDisabled tests that auto-torrent does not create BT task when disabled
|
||||
func TestDownloader_AutoTorrentDisabled(t *testing.T) {
|
||||
// Path to the test torrent file
|
||||
torrentPath := "../../internal/protocol/bt/testdata/ubuntu-22.04-live-server-amd64.iso.torrent"
|
||||
if _, err := os.Stat(torrentPath); os.IsNotExist(err) {
|
||||
t.Skip("Test torrent file not found, skipping test")
|
||||
}
|
||||
|
||||
// Start a simple HTTP server to serve the torrent file
|
||||
server := startTestTorrentServer(torrentPath)
|
||||
defer server.Close()
|
||||
|
||||
// Create a temporary directory for the test
|
||||
tempDir, err := os.MkdirTemp("", "auto_torrent_disabled_test")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(tempDir)
|
||||
|
||||
// Create downloader
|
||||
downloader := NewDownloader(nil)
|
||||
if err := downloader.Setup(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer func() {
|
||||
downloader.Delete(nil, true)
|
||||
downloader.Clear()
|
||||
}()
|
||||
|
||||
// Track task completion
|
||||
taskDone := make(chan struct{}, 1)
|
||||
|
||||
downloader.Listener(func(event *Event) {
|
||||
if event.Key == EventKeyDone {
|
||||
select {
|
||||
case taskDone <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// Create request to download the torrent file
|
||||
req := &base.Request{
|
||||
URL: "http://" + server.Addr().String() + "/ubuntu.torrent",
|
||||
}
|
||||
|
||||
// Create task with AutoTorrent explicitly disabled
|
||||
downloadDir := tempDir + "/downloads"
|
||||
_, err = downloader.CreateDirect(req, &base.Options{
|
||||
Path: downloadDir,
|
||||
Name: "ubuntu.torrent",
|
||||
Extra: http.OptsExtra{
|
||||
Connections: 1,
|
||||
AutoTorrent: util.BoolPtr(false),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Wait for task to complete
|
||||
select {
|
||||
case <-taskDone:
|
||||
// Task completed
|
||||
case <-time.After(10 * time.Second):
|
||||
t.Fatal("Timeout waiting for task to complete")
|
||||
}
|
||||
|
||||
// Give a small buffer
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
|
||||
// Verify that only 1 task exists (no BT task was created)
|
||||
tasks := downloader.GetTasks()
|
||||
if len(tasks) != 1 {
|
||||
t.Errorf("Expected exactly 1 task (torrent download only), got %d", len(tasks))
|
||||
}
|
||||
|
||||
// Verify the torrent file was downloaded
|
||||
torrentFilePath := downloadDir + "/ubuntu.torrent"
|
||||
if _, err := os.Stat(torrentFilePath); os.IsNotExist(err) {
|
||||
t.Error("Torrent file should have been downloaded")
|
||||
}
|
||||
}
|
||||
@@ -9,9 +9,14 @@ type ReqExtra struct {
|
||||
type OptsExtra struct {
|
||||
Connections int `json:"connections"`
|
||||
// AutoTorrent when task download complete, and it is a .torrent file, it will be auto create a new task for the torrent file
|
||||
AutoTorrent bool `json:"autoTorrent"`
|
||||
// nil means use global config, true/false means explicit setting
|
||||
AutoTorrent *bool `json:"autoTorrent"`
|
||||
// DeleteTorrentAfterDownload when true, deletes the .torrent file after creating BT task
|
||||
// nil means use global config, true/false means explicit setting
|
||||
DeleteTorrentAfterDownload *bool `json:"deleteTorrentAfterDownload"`
|
||||
// AutoExtract when task download complete, and it is an archive file, it will be auto extracted
|
||||
AutoExtract bool `json:"autoExtract"`
|
||||
// nil means use global config, true/false means explicit setting
|
||||
AutoExtract *bool `json:"autoExtract"`
|
||||
// ArchivePassword is the password for extracting password-protected archives
|
||||
ArchivePassword string `json:"archivePassword"`
|
||||
// DeleteAfterExtract when true, deletes the archive file after successful extraction
|
||||
|
||||
@@ -26,3 +26,13 @@ func DeepClone[T any](v *T) *T {
|
||||
json.Unmarshal(b, &t)
|
||||
return &t
|
||||
}
|
||||
|
||||
// Ptr returns a pointer to the given value.
|
||||
func Ptr[T any](v T) *T {
|
||||
return &v
|
||||
}
|
||||
|
||||
// BoolPtr returns a pointer to a bool value.
|
||||
func BoolPtr(v bool) *bool {
|
||||
return &v
|
||||
}
|
||||
Reference in new issue
Block a user