Files
MeBox/internal/service/aria2_adp.go
T
2026-06-27 01:45:18 +08:00

161 lines
4.4 KiB
Go

// Package service — Aria2 下载适配器。
//
// Aria2Adapter 实现了 DownloadAdapter 接口,通过 Aria2 JSON-RPC API
// 管理下载任务。
package service
import (
"context"
"encoding/json"
"fmt"
"net/http"
"strings"
"sync"
"time"
)
// Aria2Adapter 是 Aria2 的 DownloadAdapter 实现。
type Aria2Adapter struct {
mu sync.Mutex
cfg DownloadClientConfig
client *http.Client
idSeq int
}
// NewAria2Adapter 创建新的 Aria2 适配器。
func NewAria2Adapter() *Aria2Adapter {
return &Aria2Adapter{
client: NewInternalHTTPClient(20 * time.Second),
}
}
// AddTorrent 通过 URL 添加种子或磁力链接。
func (a *Aria2Adapter) AddTorrent(ctx context.Context, torrentURL, savePath string) (string, error) {
a.mu.Lock()
defer a.mu.Unlock()
// Aria2 addUri 的参数: [secret, [uris], options]
uris := []string{torrentURL}
options := map[string]string{}
if savePath != "" {
options["dir"] = savePath
}
result, err := a.rpcLocked(ctx, "aria2.addUri", []interface{}{uris, options})
if err != nil {
return "", err
}
var gid string
if err := json.Unmarshal(result, &gid); err != nil {
return "", err
}
return gid, nil
}
// AddMagnet 通过磁力链接添加下载。
func (a *Aria2Adapter) AddMagnet(ctx context.Context, magnet, savePath string) (string, error) {
return a.AddTorrent(ctx, magnet, savePath)
}
// Pause 暂停下载任务(通过 GID)。
func (a *Aria2Adapter) Pause(ctx context.Context, hash string) error {
a.mu.Lock()
defer a.mu.Unlock()
_, err := a.rpcLocked(ctx, "aria2.pause", []interface{}{hash})
return err
}
// Resume 恢复下载任务(通过 GID)。
func (a *Aria2Adapter) Resume(ctx context.Context, hash string) error {
a.mu.Lock()
defer a.mu.Unlock()
_, err := a.rpcLocked(ctx, "aria2.unpause", []interface{}{hash})
return err
}
// Remove 移除下载任务。
func (a *Aria2Adapter) Remove(ctx context.Context, hash string, deleteFiles bool) error {
a.mu.Lock()
defer a.mu.Unlock()
if deleteFiles {
_, err := a.rpcLocked(ctx, "aria2.removeDownloadResult", []interface{}{hash})
return err
}
_, err := a.rpcLocked(ctx, "aria2.remove", []interface{}{hash})
return err
}
// List 列出所有活动/等待/已停止的任务。
func (a *Aria2Adapter) List(ctx context.Context, filter string) ([]TorrentInfo, error) {
a.mu.Lock()
defer a.mu.Unlock()
var allResults []TorrentInfo
// 获取活动任务
active, err := a.rpcLocked(ctx, "aria2.tellActive", []interface{}{
[]string{"gid", "bittorrent", "totalLength", "completedLength", "downloadSpeed", "uploadSpeed", "status", "dir", "numSeeders", "connections", "errorCode"},
})
if err == nil && active != nil {
items := a.parseAria2Items(active)
allResults = append(allResults, items...)
}
// 获取等待中的任务
waiting, err := a.rpcLocked(ctx, "aria2.tellWaiting", []interface{}{
0, 100,
[]string{"gid", "bittorrent", "totalLength", "completedLength", "downloadSpeed", "uploadSpeed", "status", "dir", "numSeeders", "connections", "errorCode"},
})
if err == nil && waiting != nil {
items := a.parseAria2Items(waiting)
allResults = append(allResults, items...)
}
// 获取已停止的任务
stopped, err := a.rpcLocked(ctx, "aria2.tellStopped", []interface{}{
0, 100,
[]string{"gid", "bittorrent", "totalLength", "completedLength", "downloadSpeed", "uploadSpeed", "status", "dir", "numSeeders", "connections", "errorCode"},
})
if err == nil && stopped != nil {
items := a.parseAria2Items(stopped)
allResults = append(allResults, items...)
}
if filter != "" {
filtered := make([]TorrentInfo, 0, len(allResults))
for _, item := range allResults {
if strings.EqualFold(item.State, filter) {
filtered = append(filtered, item)
}
}
return filtered, nil
}
return allResults, nil
}
// GetInfo 获取单个任务信息。
func (a *Aria2Adapter) GetInfo(ctx context.Context, hash string) (*TorrentInfo, error) {
a.mu.Lock()
defer a.mu.Unlock()
result, err := a.rpcLocked(ctx, "aria2.tellStatus", []interface{}{
hash,
[]string{"gid", "bittorrent", "totalLength", "completedLength", "downloadSpeed", "uploadSpeed", "status", "dir", "numSeeders", "connections"},
})
if err != nil {
return nil, err
}
var item map[string]interface{}
if err := json.Unmarshal(result, &item); err != nil {
return nil, err
}
info := a.parseSingleItem(item)
if info == nil {
return nil, fmt.Errorf("task %s not found", hash)
}
return info, nil
}