Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 52 additions & 10 deletions internal/offline_download/tool/add.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"github.com/OpenListTeam/OpenList/v4/drivers/thunder_browser"
"github.com/OpenListTeam/OpenList/v4/drivers/thunderx"
"github.com/OpenListTeam/OpenList/v4/internal/conf"
"github.com/OpenListTeam/OpenList/v4/internal/driver"
"github.com/OpenListTeam/OpenList/v4/internal/errs"
"github.com/OpenListTeam/OpenList/v4/internal/fs"
"github.com/OpenListTeam/OpenList/v4/internal/model"
Expand Down Expand Up @@ -69,7 +70,7 @@ func AddURL(ctx context.Context, args *AddURLArgs) (task.TaskExtensionInfo, erro
}
}
// try putting url
if args.Tool == "SimpleHttp" {
if args.Tool == "SimpleHttp" && !isEd2kURL(args.URL) {
if isSimpleHttpSchemeUnsupported(args.URL) {
return nil, fmt.Errorf("SimpleHttp tool does not support this URL scheme, please use aria2 or other tools for magnet/ed2k links")
}
Expand All @@ -83,13 +84,17 @@ func AddURL(ctx context.Context, args *AddURLArgs) (task.TaskExtensionInfo, erro
// ed2k 链接自动路由:如果当前工具不支持 ed2k,自动尝试使用迅雷系工具
if isEd2kURL(args.URL) {
if !isEd2kCapableTool(args.Tool) {
// 尝试找到一个可用的支持 ed2k 的工具
fallbackTool, fallbackName := findEd2kCapableTool()
if fallbackTool != nil {
// 使用找到的迅雷工具替代
args.Tool = fallbackName
if storageTool := ed2kToolForStorage(storage); storageTool != "" {
// Prefer the matching native tool when the destination storage supports ed2k.
args.Tool = storageTool
} else {
return nil, fmt.Errorf("ed2k protocol is not supported by %s. Please configure and use Thunder/ThunderX/ThunderBrowser for ed2k links", args.Tool)
// Otherwise, try to find an available Thunder-family tool.
fallbackTool, fallbackName := findEd2kCapableTool()
if fallbackTool != nil {
args.Tool = fallbackName
} else {
return nil, fmt.Errorf("ed2k protocol is not supported by %s. Please configure and use Thunder/ThunderX/ThunderBrowser for ed2k links", args.Tool)
}
}
}
}
Expand Down Expand Up @@ -217,8 +222,45 @@ func isEd2kURL(urlStr string) bool {
return strings.HasPrefix(strings.ToLower(urlStr), "ed2k://")
}

// ed2kCapableTools 支持 ed2k 协议的工具列表(迅雷系)
var ed2kCapableTools = []string{"Thunder", "ThunderX", "ThunderBrowser"}
func ed2kToolForStorage(storage driver.Driver) string {
switch toolNameForStorage(storage) {
case "115 Cloud", "115 Open":
return toolNameForStorage(storage)
default:
return ""
}
}

func toolNameForStorage(storage driver.Driver) string {
switch storage.(type) {
case *_115.Pan115:
return "115 Cloud"
case *_115_open.Open115:
return "115 Open"
case *_123.Pan123:
return "123Pan"
case *_123_open.Open123:
return "123 Open"
case *guangyapan.GuangYaPan:
return "GuangYaPan"
case *pikpak.PikPak:
return "PikPak"
case *thunder.Thunder:
return "Thunder"
case *thunderx.ThunderX:
return "ThunderX"
case *thunder_browser.ThunderBrowser, *thunder_browser.ThunderBrowserExpert:
return "ThunderBrowser"
default:
return ""
}
}

// ed2kCapableTools 支持 ed2k 协议的工具列表
var ed2kCapableTools = []string{"115 Cloud", "115 Open", "Thunder", "ThunderX", "ThunderBrowser"}

// ed2kFallbackTools 是当前可用于自动接管 ed2k 任务的工具列表。
var ed2kFallbackTools = []string{"Thunder", "ThunderX", "ThunderBrowser"}

// isEd2kCapableTool 检查工具是否支持 ed2k 协议
func isEd2kCapableTool(toolName string) bool {
Expand All @@ -232,7 +274,7 @@ func isEd2kCapableTool(toolName string) bool {

// findEd2kCapableTool 查找一个可用的支持 ed2k 的工具
func findEd2kCapableTool() (Tool, string) {
for _, name := range ed2kCapableTools {
for _, name := range ed2kFallbackTools {
t, err := Tools.Get(name)
if err != nil {
continue
Expand Down
86 changes: 86 additions & 0 deletions internal/offline_download/tool/add_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package tool

import (
"testing"

_115 "github.com/OpenListTeam/OpenList/v4/drivers/115"
_115_open "github.com/OpenListTeam/OpenList/v4/drivers/115_open"
_123 "github.com/OpenListTeam/OpenList/v4/drivers/123"
_123_open "github.com/OpenListTeam/OpenList/v4/drivers/123_open"
"github.com/OpenListTeam/OpenList/v4/drivers/guangyapan"
"github.com/OpenListTeam/OpenList/v4/drivers/pikpak"
"github.com/OpenListTeam/OpenList/v4/drivers/thunder"
"github.com/OpenListTeam/OpenList/v4/drivers/thunder_browser"
"github.com/OpenListTeam/OpenList/v4/drivers/thunderx"
"github.com/OpenListTeam/OpenList/v4/internal/driver"
)

func TestIsEd2kCapableTool(t *testing.T) {
tests := []struct {
name string
want bool
}{
{name: "115 Cloud", want: true},
{name: "115 Open", want: true},
{name: "Thunder", want: true},
{name: "ThunderX", want: true},
{name: "ThunderBrowser", want: true},
{name: "aria2", want: false},
{name: "SimpleHttp", want: false},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := isEd2kCapableTool(tt.name); got != tt.want {
t.Fatalf("isEd2kCapableTool(%q) = %v, want %v", tt.name, got, tt.want)
}
})
}
}

func TestEd2kToolForStorage(t *testing.T) {
tests := []struct {
name string
storage driver.Driver
want string
}{
{name: "115 Cloud", storage: &_115.Pan115{}, want: "115 Cloud"},
{name: "115 Open", storage: &_115_open.Open115{}, want: "115 Open"},
{name: "other", storage: nil, want: ""},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := ed2kToolForStorage(tt.storage); got != tt.want {
t.Fatalf("ed2kToolForStorage(%T) = %q, want %q", tt.storage, got, tt.want)
}
})
}
}

func TestToolNameForStorage(t *testing.T) {
tests := []struct {
name string
storage driver.Driver
want string
}{
{name: "115 Cloud", storage: &_115.Pan115{}, want: "115 Cloud"},
{name: "115 Open", storage: &_115_open.Open115{}, want: "115 Open"},
{name: "123Pan", storage: &_123.Pan123{}, want: "123Pan"},
{name: "123 Open", storage: &_123_open.Open123{}, want: "123 Open"},
{name: "GuangYaPan", storage: &guangyapan.GuangYaPan{}, want: "GuangYaPan"},
{name: "PikPak", storage: &pikpak.PikPak{}, want: "PikPak"},
{name: "Thunder", storage: &thunder.Thunder{}, want: "Thunder"},
{name: "ThunderX", storage: &thunderx.ThunderX{}, want: "ThunderX"},
{name: "ThunderBrowser", storage: &thunder_browser.ThunderBrowser{}, want: "ThunderBrowser"},
{name: "other", storage: nil, want: ""},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := toolNameForStorage(tt.storage); got != tt.want {
t.Fatalf("toolNameForStorage(%T) = %q, want %q", tt.storage, got, tt.want)
}
})
}
}
26 changes: 26 additions & 0 deletions internal/offline_download/tool/tools.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"sort"

"github.com/OpenListTeam/OpenList/v4/internal/model"
"github.com/OpenListTeam/OpenList/v4/internal/op"
)

var (
Expand Down Expand Up @@ -35,6 +36,31 @@ func (t ToolsManager) Names() []string {
return names
}

// NamesForPath returns ready tools and the native tool for the destination storage.
// Native tools can write directly to their own storage even without a temporary path setting.
func (t ToolsManager) NamesForPath(path string) []string {
names := t.Names()
storage, _, err := op.GetStorageAndActualPath(path)
if err != nil {
return names
}

name := toolNameForStorage(storage)
if name == "" {
return names
}
for _, existing := range names {
if existing == name {
return names
}
}
if _, ok := t[name]; ok {
names = append(names, name)
sort.Strings(names)
}
return names
}

func (t ToolsManager) Items() []model.SettingItem {
var items []model.SettingItem
for _, tool := range t {
Expand Down
2 changes: 1 addition & 1 deletion server/handles/offline_download.go
Original file line number Diff line number Diff line change
Expand Up @@ -518,7 +518,7 @@ func SetGuangYaPan(c *gin.Context) {
}

func OfflineDownloadTools(c *gin.Context) {
tools := tool.Tools.Names()
tools := tool.Tools.NamesForPath(c.Query("path"))
common.SuccessResp(c, tools)
}

Expand Down