mirror of https://github.com/Xhofe/alist
178 lines
5.1 KiB
Go
178 lines
5.1 KiB
Go
package transmission
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/base64"
|
|
"fmt"
|
|
"net/http"
|
|
"net/url"
|
|
"strconv"
|
|
|
|
"github.com/alist-org/alist/v3/internal/conf"
|
|
"github.com/alist-org/alist/v3/internal/errs"
|
|
"github.com/alist-org/alist/v3/internal/model"
|
|
"github.com/alist-org/alist/v3/internal/offline_download/tool"
|
|
"github.com/alist-org/alist/v3/internal/setting"
|
|
"github.com/alist-org/alist/v3/pkg/utils"
|
|
"github.com/hekmon/transmissionrpc/v3"
|
|
"github.com/pkg/errors"
|
|
log "github.com/sirupsen/logrus"
|
|
)
|
|
|
|
type Transmission struct {
|
|
client *transmissionrpc.Client
|
|
}
|
|
|
|
func (t *Transmission) Run(task *tool.DownloadTask) error {
|
|
return errs.NotSupport
|
|
}
|
|
|
|
func (t *Transmission) Name() string {
|
|
return "Transmission"
|
|
}
|
|
|
|
func (t *Transmission) Items() []model.SettingItem {
|
|
// transmission settings
|
|
return []model.SettingItem{
|
|
{Key: conf.TransmissionUri, Value: "http://localhost:9091/transmission/rpc", Type: conf.TypeString, Group: model.OFFLINE_DOWNLOAD, Flag: model.PRIVATE},
|
|
{Key: conf.TransmissionSeedtime, Value: "0", Type: conf.TypeNumber, Group: model.OFFLINE_DOWNLOAD, Flag: model.PRIVATE},
|
|
}
|
|
}
|
|
|
|
func (t *Transmission) Init() (string, error) {
|
|
t.client = nil
|
|
uri := setting.GetStr(conf.TransmissionUri)
|
|
endpoint, err := url.Parse(uri)
|
|
if err != nil {
|
|
return "", errors.Wrap(err, "failed to init transmission client")
|
|
}
|
|
c, err := transmissionrpc.New(endpoint, nil)
|
|
if err != nil {
|
|
return "", errors.Wrap(err, "failed to init transmission client")
|
|
}
|
|
|
|
ok, serverVersion, serverMinimumVersion, err := c.RPCVersion(context.Background())
|
|
if err != nil {
|
|
return "", errors.Wrapf(err, "failed get transmission version")
|
|
}
|
|
|
|
if !ok {
|
|
return "", fmt.Errorf("remote transmission RPC version (v%d) is incompatible with the transmission library (v%d): remote needs at least v%d",
|
|
serverVersion, transmissionrpc.RPCVersion, serverMinimumVersion)
|
|
}
|
|
|
|
t.client = c
|
|
log.Infof("remote transmission RPC version (v%d) is compatible with our transmissionrpc library (v%d)\n",
|
|
serverVersion, transmissionrpc.RPCVersion)
|
|
log.Infof("using transmission version: %d", serverVersion)
|
|
return fmt.Sprintf("transmission version: %d", serverVersion), nil
|
|
}
|
|
|
|
func (t *Transmission) IsReady() bool {
|
|
return t.client != nil
|
|
}
|
|
|
|
func (t *Transmission) AddURL(args *tool.AddUrlArgs) (string, error) {
|
|
endpoint, err := url.Parse(args.Url)
|
|
if err != nil {
|
|
return "", errors.Wrap(err, "failed to parse transmission uri")
|
|
}
|
|
|
|
rpcPayload := transmissionrpc.TorrentAddPayload{
|
|
DownloadDir: &args.TempDir,
|
|
}
|
|
// http url for .torrent file
|
|
if endpoint.Scheme == "http" || endpoint.Scheme == "https" {
|
|
resp, err := http.Get(args.Url)
|
|
if err != nil {
|
|
return "", errors.Wrap(err, "failed to get .torrent file")
|
|
}
|
|
defer resp.Body.Close()
|
|
buffer := new(bytes.Buffer)
|
|
encoder := base64.NewEncoder(base64.StdEncoding, buffer)
|
|
// Stream file to the encoder
|
|
if _, err = utils.CopyWithBuffer(encoder, resp.Body); err != nil {
|
|
return "", errors.Wrap(err, "can't copy file content into the base64 encoder")
|
|
}
|
|
// Flush last bytes
|
|
if err = encoder.Close(); err != nil {
|
|
return "", errors.Wrap(err, "can't flush last bytes of the base64 encoder")
|
|
}
|
|
// Get the string form
|
|
b64 := buffer.String()
|
|
rpcPayload.MetaInfo = &b64
|
|
} else { // magnet uri
|
|
rpcPayload.Filename = &args.Url
|
|
}
|
|
|
|
torrent, err := t.client.TorrentAdd(context.TODO(), rpcPayload)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
if torrent.ID == nil {
|
|
return "", fmt.Errorf("failed get torrent ID")
|
|
}
|
|
gid := strconv.FormatInt(*torrent.ID, 10)
|
|
return gid, nil
|
|
}
|
|
|
|
func (t *Transmission) Remove(task *tool.DownloadTask) error {
|
|
gid, err := strconv.ParseInt(task.GID, 10, 64)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = t.client.TorrentRemove(context.TODO(), transmissionrpc.TorrentRemovePayload{
|
|
IDs: []int64{gid},
|
|
DeleteLocalData: false,
|
|
})
|
|
return err
|
|
}
|
|
|
|
func (t *Transmission) Status(task *tool.DownloadTask) (*tool.Status, error) {
|
|
gid, err := strconv.ParseInt(task.GID, 10, 64)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
infos, err := t.client.TorrentGetAllFor(context.TODO(), []int64{gid})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if len(infos) < 1 {
|
|
return nil, fmt.Errorf("failed get status, wrong gid: %s", task.GID)
|
|
}
|
|
info := infos[0]
|
|
|
|
s := &tool.Status{
|
|
Completed: *info.IsFinished,
|
|
Err: err,
|
|
}
|
|
s.Progress = *info.PercentDone * 100
|
|
s.TotalBytes = int64(*info.SizeWhenDone / 8)
|
|
|
|
switch *info.Status {
|
|
case transmissionrpc.TorrentStatusCheckWait,
|
|
transmissionrpc.TorrentStatusDownloadWait,
|
|
transmissionrpc.TorrentStatusCheck,
|
|
transmissionrpc.TorrentStatusDownload,
|
|
transmissionrpc.TorrentStatusIsolated:
|
|
s.Status = "[transmission] " + info.Status.String()
|
|
case transmissionrpc.TorrentStatusSeedWait,
|
|
transmissionrpc.TorrentStatusSeed:
|
|
s.Completed = true
|
|
case transmissionrpc.TorrentStatusStopped:
|
|
s.Err = errors.Errorf("[transmission] failed to download %s, status: %s, error: %s", task.GID, info.Status.String(), *info.ErrorString)
|
|
default:
|
|
s.Err = errors.Errorf("[transmission] unknown status occurred downloading %s, err: %s", task.GID, *info.ErrorString)
|
|
}
|
|
return s, nil
|
|
}
|
|
|
|
var _ tool.Tool = (*Transmission)(nil)
|
|
|
|
func init() {
|
|
tool.Tools.Add(&Transmission{})
|
|
}
|