wip: adapt qBittorrent
This commit is contained in:
parent
12dfb60a66
commit
1490da8b53
@ -164,10 +164,6 @@ func InitialSettings() []model.SettingItem {
|
|||||||
{Key: conf.SSODefaultDir, Value: "/", Type: conf.TypeString, Group: model.SSO, Flag: model.PRIVATE},
|
{Key: conf.SSODefaultDir, Value: "/", Type: conf.TypeString, Group: model.SSO, Flag: model.PRIVATE},
|
||||||
{Key: conf.SSODefaultPermission, Value: "0", Type: conf.TypeNumber, Group: model.SSO, Flag: model.PRIVATE},
|
{Key: conf.SSODefaultPermission, Value: "0", Type: conf.TypeNumber, Group: model.SSO, Flag: model.PRIVATE},
|
||||||
{Key: conf.SSOCompatibilityMode, Value: "false", Type: conf.TypeBool, Group: model.SSO, Flag: model.PUBLIC},
|
{Key: conf.SSOCompatibilityMode, Value: "false", Type: conf.TypeBool, Group: model.SSO, Flag: model.PUBLIC},
|
||||||
|
|
||||||
// qbittorrent settings
|
|
||||||
{Key: conf.QbittorrentUrl, Value: "http://admin:adminadmin@localhost:8080/", Type: conf.TypeString, Group: model.SINGLE, Flag: model.PRIVATE},
|
|
||||||
{Key: conf.QbittorrentSeedtime, Value: "0", Type: conf.TypeNumber, Group: model.SINGLE, Flag: model.PRIVATE},
|
|
||||||
}
|
}
|
||||||
initialSettingItems = append(initialSettingItems, tool.Tools.Items()...)
|
initialSettingItems = append(initialSettingItems, tool.Tools.Items()...)
|
||||||
if flags.Dev {
|
if flags.Dev {
|
||||||
|
@ -2,4 +2,5 @@ package offline_download
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
_ "github.com/alist-org/alist/v3/internal/offline_download/aria2"
|
_ "github.com/alist-org/alist/v3/internal/offline_download/aria2"
|
||||||
|
_ "github.com/alist-org/alist/v3/internal/offline_download/qbit"
|
||||||
)
|
)
|
||||||
|
80
internal/offline_download/qbit/qbit.go
Normal file
80
internal/offline_download/qbit/qbit.go
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
package qbit
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/alist-org/alist/v3/internal/conf"
|
||||||
|
"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/qbittorrent"
|
||||||
|
"github.com/alist-org/alist/v3/internal/setting"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
type QBittorrent struct {
|
||||||
|
client qbittorrent.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *QBittorrent) Items() []model.SettingItem {
|
||||||
|
// aria2 settings
|
||||||
|
return []model.SettingItem{
|
||||||
|
{Key: conf.QbittorrentUrl, Value: "http://admin:adminadmin@localhost:8080/", Type: conf.TypeString, Group: model.OFFLINE_DOWNLOAD, Flag: model.PRIVATE},
|
||||||
|
{Key: conf.QbittorrentSeedtime, Value: "0", Type: conf.TypeNumber, Group: model.OFFLINE_DOWNLOAD, Flag: model.PRIVATE},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *QBittorrent) Init() (string, error) {
|
||||||
|
a.client = nil
|
||||||
|
url := setting.GetStr(conf.QbittorrentUrl)
|
||||||
|
qbClient, err := qbittorrent.New(url)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
a.client = qbClient
|
||||||
|
return "ok", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *QBittorrent) IsReady() bool {
|
||||||
|
return a.client != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *QBittorrent) AddURL(args *tool.AddUrlArgs) (string, error) {
|
||||||
|
err := a.client.AddFromLink(args.Url, args.TempDir, args.UID)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return args.UID, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *QBittorrent) Remove(tid string) error {
|
||||||
|
err := a.client.Delete(tid, true)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *QBittorrent) Status(tid string) (*tool.Status, error) {
|
||||||
|
info, err := a.client.GetInfo(tid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
s := &tool.Status{}
|
||||||
|
s.Progress = float64(info.Completed) / float64(info.Size) * 100
|
||||||
|
switch info.State {
|
||||||
|
case qbittorrent.UPLOADING, qbittorrent.PAUSEDUP, qbittorrent.QUEUEDUP, qbittorrent.STALLEDUP, qbittorrent.FORCEDUP, qbittorrent.CHECKINGUP:
|
||||||
|
s.Completed = true
|
||||||
|
case qbittorrent.ALLOCATING, qbittorrent.DOWNLOADING, qbittorrent.METADL, qbittorrent.PAUSEDDL, qbittorrent.QUEUEDDL, qbittorrent.STALLEDDL, qbittorrent.CHECKINGDL, qbittorrent.FORCEDDL, qbittorrent.CHECKINGRESUMEDATA, qbittorrent.MOVING:
|
||||||
|
s.Status = "[qBittorrent] downloading"
|
||||||
|
case qbittorrent.ERROR, qbittorrent.MISSINGFILES, qbittorrent.UNKNOWN:
|
||||||
|
s.Err = errors.Errorf("[qBittorrent] failed to download %s, error: %s", tid, info.State)
|
||||||
|
default:
|
||||||
|
s.Err = errors.Errorf("[qBittorrent] unknown error occurred downloading %s", tid)
|
||||||
|
}
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *QBittorrent) GetFiles(tid string) []tool.File {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ tool.Tool = (*QBittorrent)(nil)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
tool.Tools.Add("qBittorrent", &QBittorrent{})
|
||||||
|
}
|
366
pkg/qbittorrent/client.go
Normal file
366
pkg/qbittorrent/client.go
Normal file
@ -0,0 +1,366 @@
|
|||||||
|
package qbittorrent
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"mime/multipart"
|
||||||
|
"net/http"
|
||||||
|
"net/http/cookiejar"
|
||||||
|
"net/url"
|
||||||
|
|
||||||
|
"github.com/alist-org/alist/v3/pkg/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Client interface {
|
||||||
|
AddFromLink(link string, savePath string, id string) error
|
||||||
|
GetInfo(id string) (TorrentInfo, error)
|
||||||
|
GetFiles(id string) ([]FileInfo, error)
|
||||||
|
Delete(id string, deleteFiles bool) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type client struct {
|
||||||
|
url *url.URL
|
||||||
|
client http.Client
|
||||||
|
Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(webuiUrl string) (Client, error) {
|
||||||
|
u, err := url.Parse(webuiUrl)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
jar, err := cookiejar.New(nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var c = &client{
|
||||||
|
url: u,
|
||||||
|
client: http.Client{Jar: jar},
|
||||||
|
}
|
||||||
|
|
||||||
|
err = c.checkAuthorization()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *client) checkAuthorization() error {
|
||||||
|
// check authorization
|
||||||
|
if c.authorized() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// check authorization after logging in
|
||||||
|
err := c.login()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if c.authorized() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return errors.New("unauthorized qbittorrent url")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *client) authorized() bool {
|
||||||
|
resp, err := c.post("/api/v2/app/version", nil)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return resp.StatusCode == 200 // the status code will be 403 if not authorized
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *client) login() error {
|
||||||
|
// prepare HTTP request
|
||||||
|
v := url.Values{}
|
||||||
|
v.Set("username", c.url.User.Username())
|
||||||
|
passwd, _ := c.url.User.Password()
|
||||||
|
v.Set("password", passwd)
|
||||||
|
resp, err := c.post("/api/v2/auth/login", v)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// check result
|
||||||
|
body := make([]byte, 2)
|
||||||
|
_, err = resp.Body.Read(body)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if string(body) != "Ok" {
|
||||||
|
return errors.New("failed to login into qBittorrent webui with url: " + c.url.String())
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *client) post(path string, data url.Values) (*http.Response, error) {
|
||||||
|
u := c.url.JoinPath(path)
|
||||||
|
u.User = nil // remove userinfo for requests
|
||||||
|
|
||||||
|
req, err := http.NewRequest("POST", u.String(), bytes.NewReader([]byte(data.Encode())))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if data != nil {
|
||||||
|
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := c.client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if resp.Cookies() != nil {
|
||||||
|
c.client.Jar.SetCookies(u, resp.Cookies())
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *client) AddFromLink(link string, savePath string, id string) error {
|
||||||
|
err := c.checkAuthorization()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
writer := multipart.NewWriter(buf)
|
||||||
|
|
||||||
|
addField := func(name string, value string) {
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = writer.WriteField(name, value)
|
||||||
|
}
|
||||||
|
addField("urls", link)
|
||||||
|
addField("savepath", savePath)
|
||||||
|
addField("tags", "alist-"+id)
|
||||||
|
addField("autoTMM", "false")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = writer.Close()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
u := c.url.JoinPath("/api/v2/torrents/add")
|
||||||
|
u.User = nil // remove userinfo for requests
|
||||||
|
req, err := http.NewRequest("POST", u.String(), buf)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
req.Header.Add("Content-Type", writer.FormDataContentType())
|
||||||
|
|
||||||
|
resp, err := c.client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// check result
|
||||||
|
body := make([]byte, 2)
|
||||||
|
_, err = resp.Body.Read(body)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if resp.StatusCode != 200 || string(body) != "Ok" {
|
||||||
|
return errors.New("failed to add qBittorrent task: " + link)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type TorrentStatus string
|
||||||
|
|
||||||
|
const (
|
||||||
|
ERROR TorrentStatus = "error"
|
||||||
|
MISSINGFILES TorrentStatus = "missingFiles"
|
||||||
|
UPLOADING TorrentStatus = "uploading"
|
||||||
|
PAUSEDUP TorrentStatus = "pausedUP"
|
||||||
|
QUEUEDUP TorrentStatus = "queuedUP"
|
||||||
|
STALLEDUP TorrentStatus = "stalledUP"
|
||||||
|
CHECKINGUP TorrentStatus = "checkingUP"
|
||||||
|
FORCEDUP TorrentStatus = "forcedUP"
|
||||||
|
ALLOCATING TorrentStatus = "allocating"
|
||||||
|
DOWNLOADING TorrentStatus = "downloading"
|
||||||
|
METADL TorrentStatus = "metaDL"
|
||||||
|
PAUSEDDL TorrentStatus = "pausedDL"
|
||||||
|
QUEUEDDL TorrentStatus = "queuedDL"
|
||||||
|
STALLEDDL TorrentStatus = "stalledDL"
|
||||||
|
CHECKINGDL TorrentStatus = "checkingDL"
|
||||||
|
FORCEDDL TorrentStatus = "forcedDL"
|
||||||
|
CHECKINGRESUMEDATA TorrentStatus = "checkingResumeData"
|
||||||
|
MOVING TorrentStatus = "moving"
|
||||||
|
UNKNOWN TorrentStatus = "unknown"
|
||||||
|
)
|
||||||
|
|
||||||
|
// https://github.com/DGuang21/PTGo/blob/main/app/client/client_distributer.go
|
||||||
|
type TorrentInfo struct {
|
||||||
|
AddedOn int `json:"added_on"` // 将 torrent 添加到客户端的时间(Unix Epoch)
|
||||||
|
AmountLeft int64 `json:"amount_left"` // 剩余大小(字节)
|
||||||
|
AutoTmm bool `json:"auto_tmm"` // 此 torrent 是否由 Automatic Torrent Management 管理
|
||||||
|
Availability float64 `json:"availability"` // 当前百分比
|
||||||
|
Category string `json:"category"` //
|
||||||
|
Completed int64 `json:"completed"` // 完成的传输数据量(字节)
|
||||||
|
CompletionOn int `json:"completion_on"` // Torrent 完成的时间(Unix Epoch)
|
||||||
|
ContentPath string `json:"content_path"` // torrent 内容的绝对路径(多文件 torrent 的根路径,单文件 torrent 的绝对文件路径)
|
||||||
|
DlLimit int `json:"dl_limit"` // Torrent 下载速度限制(字节/秒)
|
||||||
|
Dlspeed int `json:"dlspeed"` // Torrent 下载速度(字节/秒)
|
||||||
|
Downloaded int64 `json:"downloaded"` // 已经下载大小
|
||||||
|
DownloadedSession int64 `json:"downloaded_session"` // 此会话下载的数据量
|
||||||
|
Eta int `json:"eta"` //
|
||||||
|
FLPiecePrio bool `json:"f_l_piece_prio"` // 如果第一个最后一块被优先考虑,则为true
|
||||||
|
ForceStart bool `json:"force_start"` // 如果为此 torrent 启用了强制启动,则为true
|
||||||
|
Hash string `json:"hash"` //
|
||||||
|
LastActivity int `json:"last_activity"` // 上次活跃的时间(Unix Epoch)
|
||||||
|
MagnetURI string `json:"magnet_uri"` // 与此 torrent 对应的 Magnet URI
|
||||||
|
MaxRatio float64 `json:"max_ratio"` // 种子/上传停止种子前的最大共享比率
|
||||||
|
MaxSeedingTime int `json:"max_seeding_time"` // 停止种子种子前的最长种子时间(秒)
|
||||||
|
Name string `json:"name"` //
|
||||||
|
NumComplete int `json:"num_complete"` //
|
||||||
|
NumIncomplete int `json:"num_incomplete"` //
|
||||||
|
NumLeechs int `json:"num_leechs"` // 连接到的 leechers 的数量
|
||||||
|
NumSeeds int `json:"num_seeds"` // 连接到的种子数
|
||||||
|
Priority int `json:"priority"` // 速度优先。如果队列被禁用或 torrent 处于种子模式,则返回 -1
|
||||||
|
Progress float64 `json:"progress"` // 进度
|
||||||
|
Ratio float64 `json:"ratio"` // Torrent 共享比率
|
||||||
|
RatioLimit int `json:"ratio_limit"` //
|
||||||
|
SavePath string `json:"save_path"`
|
||||||
|
SeedingTime int `json:"seeding_time"` // Torrent 完成用时(秒)
|
||||||
|
SeedingTimeLimit int `json:"seeding_time_limit"` // max_seeding_time
|
||||||
|
SeenComplete int `json:"seen_complete"` // 上次 torrent 完成的时间
|
||||||
|
SeqDl bool `json:"seq_dl"` // 如果启用顺序下载,则为true
|
||||||
|
Size int64 `json:"size"` //
|
||||||
|
State TorrentStatus `json:"state"` // 参见https://github.com/qbittorrent/qBittorrent/wiki/WebUI-API-(qBittorrent-4.1)#get-torrent-list
|
||||||
|
SuperSeeding bool `json:"super_seeding"` // 如果启用超级播种,则为true
|
||||||
|
Tags string `json:"tags"` // Torrent 的逗号连接标签列表
|
||||||
|
TimeActive int `json:"time_active"` // 总活动时间(秒)
|
||||||
|
TotalSize int64 `json:"total_size"` // 此 torrent 中所有文件的总大小(字节)(包括未选择的文件)
|
||||||
|
Tracker string `json:"tracker"` // 第一个具有工作状态的tracker。如果没有tracker在工作,则返回空字符串。
|
||||||
|
TrackersCount int `json:"trackers_count"` //
|
||||||
|
UpLimit int `json:"up_limit"` // 上传限制
|
||||||
|
Uploaded int64 `json:"uploaded"` // 累计上传
|
||||||
|
UploadedSession int64 `json:"uploaded_session"` // 当前session累计上传
|
||||||
|
Upspeed int `json:"upspeed"` // 上传速度(字节/秒)
|
||||||
|
}
|
||||||
|
|
||||||
|
type InfoNotFoundError struct {
|
||||||
|
Id string
|
||||||
|
Err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i InfoNotFoundError) Error() string {
|
||||||
|
return "there should be exactly one task with tag \"alist-" + i.Id + "\""
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewInfoNotFoundError(id string) InfoNotFoundError {
|
||||||
|
return InfoNotFoundError{Id: id}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *client) GetInfo(id string) (TorrentInfo, error) {
|
||||||
|
var infos []TorrentInfo
|
||||||
|
|
||||||
|
err := c.checkAuthorization()
|
||||||
|
if err != nil {
|
||||||
|
return TorrentInfo{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
v := url.Values{}
|
||||||
|
v.Set("tag", "alist-"+id)
|
||||||
|
response, err := c.post("/api/v2/torrents/info", v)
|
||||||
|
if err != nil {
|
||||||
|
return TorrentInfo{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
body, err := io.ReadAll(response.Body)
|
||||||
|
if err != nil {
|
||||||
|
return TorrentInfo{}, err
|
||||||
|
}
|
||||||
|
err = utils.Json.Unmarshal(body, &infos)
|
||||||
|
if err != nil {
|
||||||
|
return TorrentInfo{}, err
|
||||||
|
}
|
||||||
|
if len(infos) != 1 {
|
||||||
|
return TorrentInfo{}, NewInfoNotFoundError(id)
|
||||||
|
}
|
||||||
|
return infos[0], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type FileInfo struct {
|
||||||
|
Index int `json:"index"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Size int64 `json:"size"`
|
||||||
|
Progress float32 `json:"progress"`
|
||||||
|
Priority int `json:"priority"`
|
||||||
|
IsSeed bool `json:"is_seed"`
|
||||||
|
PieceRange []int `json:"piece_range"`
|
||||||
|
Availability float32 `json:"availability"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *client) GetFiles(id string) ([]FileInfo, error) {
|
||||||
|
var infos []FileInfo
|
||||||
|
|
||||||
|
err := c.checkAuthorization()
|
||||||
|
if err != nil {
|
||||||
|
return []FileInfo{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
tInfo, err := c.GetInfo(id)
|
||||||
|
if err != nil {
|
||||||
|
return []FileInfo{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
v := url.Values{}
|
||||||
|
v.Set("hash", tInfo.Hash)
|
||||||
|
response, err := c.post("/api/v2/torrents/files", v)
|
||||||
|
if err != nil {
|
||||||
|
return []FileInfo{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
body, err := io.ReadAll(response.Body)
|
||||||
|
if err != nil {
|
||||||
|
return []FileInfo{}, err
|
||||||
|
}
|
||||||
|
err = utils.Json.Unmarshal(body, &infos)
|
||||||
|
if err != nil {
|
||||||
|
return []FileInfo{}, err
|
||||||
|
}
|
||||||
|
return infos, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *client) Delete(id string, deleteFiles bool) error {
|
||||||
|
err := c.checkAuthorization()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
info, err := c.GetInfo(id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
v := url.Values{}
|
||||||
|
v.Set("hashes", info.Hash)
|
||||||
|
if deleteFiles {
|
||||||
|
v.Set("deleteFiles", "true")
|
||||||
|
} else {
|
||||||
|
v.Set("deleteFiles", "false")
|
||||||
|
}
|
||||||
|
response, err := c.post("/api/v2/torrents/delete", v)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if response.StatusCode != 200 {
|
||||||
|
return errors.New("failed to delete qbittorrent task")
|
||||||
|
}
|
||||||
|
|
||||||
|
v = url.Values{}
|
||||||
|
v.Set("tags", "alist-"+id)
|
||||||
|
response, err = c.post("/api/v2/torrents/deleteTags", v)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if response.StatusCode != 200 {
|
||||||
|
return errors.New("failed to delete qbittorrent tag")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
@ -1,12 +1,10 @@
|
|||||||
package handles
|
package handles
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/alist-org/alist/v3/internal/aria2"
|
|
||||||
"github.com/alist-org/alist/v3/internal/conf"
|
"github.com/alist-org/alist/v3/internal/conf"
|
||||||
"github.com/alist-org/alist/v3/internal/model"
|
"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/offline_download/tool"
|
||||||
"github.com/alist-org/alist/v3/internal/op"
|
"github.com/alist-org/alist/v3/internal/op"
|
||||||
"github.com/alist-org/alist/v3/internal/qbittorrent"
|
|
||||||
"github.com/alist-org/alist/v3/server/common"
|
"github.com/alist-org/alist/v3/server/common"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
@ -30,7 +28,8 @@ func SetAria2(c *gin.Context) {
|
|||||||
common.ErrorResp(c, err, 500)
|
common.ErrorResp(c, err, 500)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
version, err := aria2.InitClient(2)
|
_tool, err := tool.Tools.Get("aria2")
|
||||||
|
version, err := _tool.Init()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
common.ErrorResp(c, err, 500)
|
common.ErrorResp(c, err, 500)
|
||||||
return
|
return
|
||||||
@ -57,7 +56,12 @@ func SetQbittorrent(c *gin.Context) {
|
|||||||
common.ErrorResp(c, err, 500)
|
common.ErrorResp(c, err, 500)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err := qbittorrent.InitClient(); err != nil {
|
_tool, err := tool.Tools.Get("qBittorrent")
|
||||||
|
if err != nil {
|
||||||
|
common.ErrorResp(c, err, 500)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if _, err := _tool.Init(); err != nil {
|
||||||
common.ErrorResp(c, err, 500)
|
common.ErrorResp(c, err, 500)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user