mirror of
https://github.com/truewhile/MeBox.git
synced 2026-09-29 19:36:36 +08:00
161 lines
4.4 KiB
Go
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
|
|
}
|