Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
alist-org
GitHub Repository: alist-org/alist
Path: blob/main/internal/offline_download/transmission/client.go
1562 views
1
package transmission
2
3
import (
4
"bytes"
5
"context"
6
"encoding/base64"
7
"fmt"
8
"net/http"
9
"net/url"
10
"strconv"
11
12
"github.com/alist-org/alist/v3/internal/conf"
13
"github.com/alist-org/alist/v3/internal/errs"
14
"github.com/alist-org/alist/v3/internal/model"
15
"github.com/alist-org/alist/v3/internal/offline_download/tool"
16
"github.com/alist-org/alist/v3/internal/setting"
17
"github.com/alist-org/alist/v3/pkg/utils"
18
"github.com/hekmon/transmissionrpc/v3"
19
"github.com/pkg/errors"
20
log "github.com/sirupsen/logrus"
21
)
22
23
type Transmission struct {
24
client *transmissionrpc.Client
25
}
26
27
func (t *Transmission) Run(task *tool.DownloadTask) error {
28
return errs.NotSupport
29
}
30
31
func (t *Transmission) Name() string {
32
return "Transmission"
33
}
34
35
func (t *Transmission) Items() []model.SettingItem {
36
// transmission settings
37
return []model.SettingItem{
38
{Key: conf.TransmissionUri, Value: "http://localhost:9091/transmission/rpc", Type: conf.TypeString, Group: model.OFFLINE_DOWNLOAD, Flag: model.PRIVATE},
39
{Key: conf.TransmissionSeedtime, Value: "0", Type: conf.TypeNumber, Group: model.OFFLINE_DOWNLOAD, Flag: model.PRIVATE},
40
}
41
}
42
43
func (t *Transmission) Init() (string, error) {
44
t.client = nil
45
uri := setting.GetStr(conf.TransmissionUri)
46
endpoint, err := url.Parse(uri)
47
if err != nil {
48
return "", errors.Wrap(err, "failed to init transmission client")
49
}
50
c, err := transmissionrpc.New(endpoint, nil)
51
if err != nil {
52
return "", errors.Wrap(err, "failed to init transmission client")
53
}
54
55
ok, serverVersion, serverMinimumVersion, err := c.RPCVersion(context.Background())
56
if err != nil {
57
return "", errors.Wrapf(err, "failed get transmission version")
58
}
59
60
if !ok {
61
return "", fmt.Errorf("remote transmission RPC version (v%d) is incompatible with the transmission library (v%d): remote needs at least v%d",
62
serverVersion, transmissionrpc.RPCVersion, serverMinimumVersion)
63
}
64
65
t.client = c
66
log.Infof("remote transmission RPC version (v%d) is compatible with our transmissionrpc library (v%d)\n",
67
serverVersion, transmissionrpc.RPCVersion)
68
log.Infof("using transmission version: %d", serverVersion)
69
return fmt.Sprintf("transmission version: %d", serverVersion), nil
70
}
71
72
func (t *Transmission) IsReady() bool {
73
return t.client != nil
74
}
75
76
func (t *Transmission) AddURL(args *tool.AddUrlArgs) (string, error) {
77
endpoint, err := url.Parse(args.Url)
78
if err != nil {
79
return "", errors.Wrap(err, "failed to parse transmission uri")
80
}
81
82
rpcPayload := transmissionrpc.TorrentAddPayload{
83
DownloadDir: &args.TempDir,
84
}
85
// http url for .torrent file
86
if endpoint.Scheme == "http" || endpoint.Scheme == "https" {
87
resp, err := http.Get(args.Url)
88
if err != nil {
89
return "", errors.Wrap(err, "failed to get .torrent file")
90
}
91
defer resp.Body.Close()
92
buffer := new(bytes.Buffer)
93
encoder := base64.NewEncoder(base64.StdEncoding, buffer)
94
// Stream file to the encoder
95
if _, err = utils.CopyWithBuffer(encoder, resp.Body); err != nil {
96
return "", errors.Wrap(err, "can't copy file content into the base64 encoder")
97
}
98
// Flush last bytes
99
if err = encoder.Close(); err != nil {
100
return "", errors.Wrap(err, "can't flush last bytes of the base64 encoder")
101
}
102
// Get the string form
103
b64 := buffer.String()
104
rpcPayload.MetaInfo = &b64
105
} else { // magnet uri
106
rpcPayload.Filename = &args.Url
107
}
108
109
torrent, err := t.client.TorrentAdd(context.TODO(), rpcPayload)
110
if err != nil {
111
return "", err
112
}
113
114
if torrent.ID == nil {
115
return "", fmt.Errorf("failed get torrent ID")
116
}
117
gid := strconv.FormatInt(*torrent.ID, 10)
118
return gid, nil
119
}
120
121
func (t *Transmission) Remove(task *tool.DownloadTask) error {
122
gid, err := strconv.ParseInt(task.GID, 10, 64)
123
if err != nil {
124
return err
125
}
126
err = t.client.TorrentRemove(context.TODO(), transmissionrpc.TorrentRemovePayload{
127
IDs: []int64{gid},
128
DeleteLocalData: false,
129
})
130
return err
131
}
132
133
func (t *Transmission) Status(task *tool.DownloadTask) (*tool.Status, error) {
134
gid, err := strconv.ParseInt(task.GID, 10, 64)
135
if err != nil {
136
return nil, err
137
}
138
infos, err := t.client.TorrentGetAllFor(context.TODO(), []int64{gid})
139
if err != nil {
140
return nil, err
141
}
142
143
if len(infos) < 1 {
144
return nil, fmt.Errorf("failed get status, wrong gid: %s", task.GID)
145
}
146
info := infos[0]
147
148
s := &tool.Status{
149
Completed: *info.IsFinished,
150
Err: err,
151
}
152
s.Progress = *info.PercentDone * 100
153
s.TotalBytes = int64(*info.SizeWhenDone / 8)
154
155
switch *info.Status {
156
case transmissionrpc.TorrentStatusCheckWait,
157
transmissionrpc.TorrentStatusDownloadWait,
158
transmissionrpc.TorrentStatusCheck,
159
transmissionrpc.TorrentStatusDownload,
160
transmissionrpc.TorrentStatusIsolated:
161
s.Status = "[transmission] " + info.Status.String()
162
case transmissionrpc.TorrentStatusSeedWait,
163
transmissionrpc.TorrentStatusSeed:
164
s.Completed = true
165
case transmissionrpc.TorrentStatusStopped:
166
s.Err = errors.Errorf("[transmission] failed to download %s, status: %s, error: %s", task.GID, info.Status.String(), *info.ErrorString)
167
default:
168
s.Err = errors.Errorf("[transmission] unknown status occurred downloading %s, err: %s", task.GID, *info.ErrorString)
169
}
170
return s, nil
171
}
172
173
var _ tool.Tool = (*Transmission)(nil)
174
175
func init() {
176
tool.Tools.Add(&Transmission{})
177
}
178
179