tstor/pkg/qbittorrent/torrent.go

1531 lines
50 KiB
Go
Raw Permalink Normal View History

2024-08-31 23:00:13 +00:00
package qbittorrent
import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"io"
"mime/multipart"
"net/http"
"net/url"
"strconv"
"strings"
"golang.org/x/exp/constraints"
)
type Torrent interface {
// GetTorrents get torrent list
GetTorrents(ctx context.Context, opt *TorrentOption) ([]*TorrentInfo, error)
// GetProperties get torrent generic properties
GetProperties(ctx context.Context, hash string) (*TorrentProperties, error)
// GetTrackers get torrent trackers
GetTrackers(ctx context.Context, hash string) ([]*TorrentTracker, error)
// GetWebSeeds get torrent web seeds
GetWebSeeds(ctx context.Context, hash string) ([]*TorrentWebSeed, error)
// GetContents get torrent contents, indexes(optional) of the files you want to retrieve
GetContents(ctx context.Context, hash string, indexes ...int) ([]*TorrentContent, error)
// GetPiecesStates get torrent pieces states
GetPiecesStates(ctx context.Context, hash string) ([]int, error)
// GetPiecesHashes get torrent pieces hashes
GetPiecesHashes(ctx context.Context, hash string) ([]string, error)
// PauseTorrents the hashes of the torrents you want to pause
PauseTorrents(ctx context.Context, hashes []string) error
// ResumeTorrents the hashes of the torrents you want to resume
ResumeTorrents(ctx context.Context, hashes []string) error
// DeleteTorrents the hashes of the torrents you want to delete, if set deleteFile to true,
// the downloaded data will also be deleted, otherwise has no effect.
DeleteTorrents(ctx context.Context, hashes []string, deleteFile bool) error
// RecheckTorrents the hashes of the torrents you want to recheck
RecheckTorrents(ctx context.Context, hashes []string) error
// ReAnnounceTorrents the hashes of the torrents you want to reannounce
ReAnnounceTorrents(ctx context.Context, hashes []string) error
// AddNewTorrent add torrents from server local file or from URLs. http://, https://,
// magnet: and bc://bt/ links are supported, but only one onetime
AddNewTorrent(ctx context.Context, opt *TorrentAddOption) error
// AddTrackers add trackers to torrent
AddTrackers(ctx context.Context, hash string, urls []string) error
// EditTrackers edit trackers
EditTrackers(ctx context.Context, hash, origUrl, newUrl string) error
// RemoveTrackers remove trackers
RemoveTrackers(ctx context.Context, hash string, urls []string) error
// AddPeers add peers for torrent, each peer is host:port
AddPeers(ctx context.Context, hashes []string, peers []string) error
// IncreasePriority increase torrent priority
IncreasePriority(ctx context.Context, hashes []string) error
// DecreasePriority decrease torrent priority
DecreasePriority(ctx context.Context, hashes []string) error
// MaxPriority maximal torrent priority
MaxPriority(ctx context.Context, hashes []string) error
// MinPriority minimal torrent priority
MinPriority(ctx context.Context, hashes []string) error
// SetFilePriority set file priority
2024-09-24 13:26:15 +00:00
SetFilePriority(ctx context.Context, hash string, id int, priority Priority) error
2024-08-31 23:00:13 +00:00
// GetDownloadLimit get torrent download limit
GetDownloadLimit(ctx context.Context, hashes []string) (map[string]int, error)
// SetDownloadLimit set torrent download limit, limit in bytes per second, if no limit please set value zero
SetDownloadLimit(ctx context.Context, hashes []string, limit int) error
// SetShareLimit set torrent share limit, ratioLimit: the maximum seeding ratio for the torrent, -2 means the
// global limit should be used, -1 means no limit; seedingTimeLimit: the maximum seeding time (minutes) for the
// torrent, -2 means the global limit should be used, -1 means no limit; inactiveSeedingTimeLimit: the maximum
// amount of time (minutes) the torrent is allowed to seed while being inactive, -2 means the global limit should
// be used, -1 means no limit.
SetShareLimit(ctx context.Context, hashes []string, ratioLimit float64, seedingTimeLimit, inactiveSeedingTimeLimit int) error
// GetUploadLimit get torrent upload limit
GetUploadLimit(ctx context.Context, hashes []string) (map[string]int, error)
// SetUploadLimit set torrent upload limit
SetUploadLimit(ctx context.Context, hashes []string, limit int) error
// SetLocation set torrent location
SetLocation(ctx context.Context, hashes []string, location string) error
// SetName set torrent name
SetName(ctx context.Context, hash string, name string) error
// SetCategory set torrent category
SetCategory(ctx context.Context, hashes []string, category string) error
// GetCategories get all categories
GetCategories(ctx context.Context) (map[string]*TorrentCategory, error)
// AddNewCategory add new category
AddNewCategory(ctx context.Context, category, savePath string) error
// EditCategory edit category
EditCategory(ctx context.Context, category, savePath string) error
// RemoveCategories remove categories
RemoveCategories(ctx context.Context, categories []string) error
// AddTags add torrent tags
AddTags(ctx context.Context, hashes []string, tags []string) error
// RemoveTags remove torrent tags
RemoveTags(ctx context.Context, hashes []string, tags []string) error
// GetTags get all tags
GetTags(ctx context.Context) ([]string, error)
// CreateTags create tags
CreateTags(ctx context.Context, tags []string) error
// DeleteTags delete tags
DeleteTags(ctx context.Context, tags []string) error
// SetAutomaticManagement set automatic torrent management
SetAutomaticManagement(ctx context.Context, hashes []string, enable bool) error
// ToggleSequentialDownload toggle sequential download
ToggleSequentialDownload(ctx context.Context, hashes []string) error
// SetFirstLastPiecePriority set first/last piece priority
SetFirstLastPiecePriority(ctx context.Context, hashes []string) error
// SetForceStart set force start
SetForceStart(ctx context.Context, hashes []string, force bool) error
// SetSuperSeeding set super seeding
SetSuperSeeding(ctx context.Context, hashes []string, enable bool) error
// RenameFile rename file
RenameFile(ctx context.Context, hash, oldPath, newPath string) error
// RenameFolder rename folder
RenameFolder(ctx context.Context, hash, oldPath, newPath string) error
}
type TorrentOption struct {
// Filter torrent list by state. Allowed state filters: all,downloading,seeding,completed,paused,
// active,inactive,resumed,stalled,stalled_uploading,stalled_downloading,errored
Filter string `schema:"filter,omitempty"`
// Category get torrents with the given category, empty string means "without category"; no "category"
// parameter means "any category"
Category string `schema:"category,omitempty"`
// Tag get torrents with the given tag, empty string means "without tag"; no "tag" parameter means "any tag"
Tag string `schema:"tag,omitempty"`
// Sort torrents by given key, they can be sorted using any field of the response's JSON array (which are documented below) as the sort key.
Sort string `schema:"sort,omitempty"`
// Reverse enable reverse sorting. Defaults to false
Reverse bool `schema:"reverse,omitempty"`
// Limit the number of torrents returned
Limit int `schema:"limit,omitempty"`
// Offset set offset (if less than 0, offset from end)
Offset int `schema:"offset,omitempty"`
// Hashes filter by hashes
Hashes []string `schema:"-"`
}
2024-09-08 18:39:11 +00:00
type TorrentState string
const (
TorrentStateError TorrentState = "error"
TorrentStateMissingFiles TorrentState = "missingFiles"
TorrentStateUploading TorrentState = "uploading"
TorrentStatePausedUP TorrentState = "pausedUP"
TorrentStateQueuedUP TorrentState = "queuedUP"
TorrentStateStalledUP TorrentState = "stalledUP"
TorrentStateCheckingUP TorrentState = "checkingUP"
TorrentStateForcedUP TorrentState = "forcedUP"
TorrentStateAllocating TorrentState = "allocating"
TorrentStateDownloading TorrentState = "downloading"
TorrentStateMetaDL TorrentState = "metaDL"
TorrentStatePausedDL TorrentState = "pausedDL"
TorrentStateQueuedDL TorrentState = "queuedDL"
TorrentStateStalledDL TorrentState = "stalledDL"
TorrentStateCheckingDL TorrentState = "checkingDL"
TorrentStateForcedDL TorrentState = "forcedDL"
TorrentStateCheckingResumeData TorrentState = "checkingResumeData"
TorrentStateMoving TorrentState = "moving"
TorrentStateUnknown TorrentState = "unknown"
)
2024-08-31 23:00:13 +00:00
type TorrentInfo struct {
2024-09-08 18:39:11 +00:00
AddedOn int `json:"added_on"`
AmountLeft int `json:"amount_left"`
AutoTmm bool `json:"auto_tmm"`
Availability float64 `json:"availability"`
Category string `json:"category"`
Completed int `json:"completed"`
CompletionOn int `json:"completion_on"`
ContentPath string `json:"content_path"`
DlLimit int `json:"dl_limit"`
Dlspeed int `json:"dlspeed"`
DownloadPath string `json:"download_path"`
Downloaded int `json:"downloaded"`
DownloadedSession int `json:"downloaded_session"`
Eta int `json:"eta"`
FLPiecePrio bool `json:"f_l_piece_prio"`
ForceStart bool `json:"force_start"`
Hash string `json:"hash"`
InactiveSeedingTimeLimit int `json:"inactive_seeding_time_limit"`
InfohashV1 string `json:"infohash_v1"`
InfohashV2 string `json:"infohash_v2"`
LastActivity int `json:"last_activity"`
MagnetURI string `json:"magnet_uri"`
MaxInactiveSeedingTime int `json:"max_inactive_seeding_time"`
MaxRatio int `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"`
NumSeeds int `json:"num_seeds"`
Priority int `json:"priority"`
Progress float64 `json:"progress"`
Ratio float64 `json:"ratio"`
RatioLimit int `json:"ratio_limit"`
SavePath string `json:"save_path"`
SeedingTime int `json:"seeding_time"`
SeedingTimeLimit int `json:"seeding_time_limit"`
SeenComplete int `json:"seen_complete"`
SeqDl bool `json:"seq_dl"`
Size int `json:"size"`
State TorrentState `json:"state"`
SuperSeeding bool `json:"super_seeding"`
Tags string `json:"tags"`
TimeActive int `json:"time_active"`
TotalSize int `json:"total_size"`
Tracker string `json:"tracker"`
TrackersCount int `json:"trackers_count"`
UpLimit int `json:"up_limit"`
Uploaded int `json:"uploaded"`
UploadedSession int `json:"uploaded_session"`
Upspeed int `json:"upspeed"`
2024-08-31 23:00:13 +00:00
}
type TorrentProperties struct {
AdditionDate int `json:"addition_date,omitempty"`
Comment string `json:"comment,omitempty"`
CompletionDate int `json:"completion_date,omitempty"`
CreatedBy string `json:"created_by,omitempty"`
CreationDate int `json:"creation_date,omitempty"`
DlLimit int `json:"dl_limit,omitempty"`
DlSpeed int `json:"dl_speed,omitempty"`
DlSpeedAvg int `json:"dl_speed_avg,omitempty"`
DownloadPath string `json:"download_path,omitempty"`
Eta int `json:"eta,omitempty"`
Hash string `json:"hash,omitempty"`
InfohashV1 string `json:"infohash_v1,omitempty"`
InfohashV2 string `json:"infohash_v2,omitempty"`
IsPrivate bool `json:"is_private,omitempty"`
LastSeen int `json:"last_seen,omitempty"`
Name string `json:"name,omitempty"`
NbConnections int `json:"nb_connections,omitempty"`
NbConnectionsLimit int `json:"nb_connections_limit,omitempty"`
Peers int `json:"peers,omitempty"`
PeersTotal int `json:"peers_total,omitempty"`
PieceSize int `json:"piece_size,omitempty"`
PiecesHave int `json:"pieces_have,omitempty"`
PiecesNum int `json:"pieces_num,omitempty"`
Reannounce int `json:"reannounce,omitempty"`
SavePath string `json:"save_path,omitempty"`
SeedingTime int `json:"seeding_time,omitempty"`
Seeds int `json:"seeds,omitempty"`
SeedsTotal int `json:"seeds_total,omitempty"`
ShareRatio float64 `json:"share_ratio,omitempty"`
TimeElapsed int `json:"time_elapsed,omitempty"`
TotalDownloaded int64 `json:"total_downloaded,omitempty"`
TotalDownloadedSession int64 `json:"total_downloaded_session,omitempty"`
TotalSize int64 `json:"total_size,omitempty"`
TotalUploaded int64 `json:"total_uploaded,omitempty"`
TotalUploadedSession int64 `json:"total_uploaded_session,omitempty"`
TotalWasted int `json:"total_wasted,omitempty"`
UpLimit int `json:"up_limit,omitempty"`
UpSpeed int `json:"up_speed,omitempty"`
UpSpeedAvg int `json:"up_speed_avg,omitempty"`
}
type TorrentTracker struct {
Msg string `json:"msg,omitempty"`
NumDownloaded int `json:"num_downloaded,omitempty"`
NumLeeches int `json:"num_leeches,omitempty"`
NumPeers int `json:"num_peers,omitempty"`
NumSeeds int `json:"num_seeds,omitempty"`
Status int `json:"status,omitempty"`
Tier int `json:"tier,omitempty"`
URL string `json:"url,omitempty"`
}
type TorrentWebSeed struct {
URL string `json:"url"`
}
2024-09-24 13:26:15 +00:00
type Priority int
const (
PriorityDoNotDownload Priority = 0
PriorityNormal Priority = 1
PriorityHigh Priority = 6
PriorityMax Priority = 7
)
2024-08-31 23:00:13 +00:00
type TorrentContent struct {
2024-09-24 13:26:15 +00:00
Availability float64 `json:"availability,omitempty"`
Index int `json:"index,omitempty"`
IsSeed bool `json:"is_seed,omitempty"`
Name string `json:"name,omitempty"`
PieceRange []int `json:"piece_range,omitempty"`
Priority Priority `json:"priority,omitempty"`
Progress float64 `json:"progress,omitempty"`
Size int64 `json:"size,omitempty"`
2024-08-31 23:00:13 +00:00
}
type TorrentAddFileMetadata struct {
// Filename only used to distinguish two files in form-data, does not work on the server side,
// for different files, please give different identification names
Filename string
// Data read torrent file content and set to here
Data []byte
}
type TorrentAddOption struct {
URLs []string `schema:"-"` // torrents url
Torrents []*TorrentAddFileMetadata `schema:"-"` // raw data of torrent file
SavePath string `schema:"savepath,omitempty"` // download folder, optional
Cookies string `schema:"cookie,omitempty"` // cookie sent to download torrent file, optional
Category string `schema:"category,omitempty"` // category for the torrent, optional
Tags []string `schema:"-"` // tags for the torrent, optional
SkipChecking bool `schema:"skip_checking,omitempty"` // skip hash checking, optional
Paused bool `schema:"paused,omitempty"` // add torrent in the pause state, optional
RootFolder bool `schema:"root_folder,omitempty"` // create the root folder, optional
Rename string `schema:"rename,omitempty"` // rename torrent, optional
UpLimit int `schema:"upLimit,omitempty"` // set torrent upload speed, Unit in bytes/second, optional
DlLimit int `schema:"dlLimit,omitempty"` // set torrent download speed, Unit in bytes/second, optional
RatioLimit float64 `schema:"ratioLimit,omitempty"` // set torrent share ratio limit, optional
SeedingTimeLimit int `schema:"seedingTimeLimit,omitempty"` // set torrent seeding torrent limit, Unit in minutes, optional
AutoTMM bool `schema:"autoTMM,omitempty"` // whether Automatic Torrent Management should be used, optional
SequentialDownload string `schema:"sequentialDownload,omitempty"` // enable sequential download, optional
FirstLastPiecePrio string `schema:"firstLastPiecePrio,omitempty"` // prioritize download first last piece, optional
}
type TorrentCategory struct {
Name string `json:"name,omitempty"`
SavePath string `json:"savePath,omitempty"`
}
func (c *client) GetTorrents(ctx context.Context, opt *TorrentOption) ([]*TorrentInfo, error) {
2024-09-08 18:39:11 +00:00
ctx, span := trace.Start(ctx, "qbittorrent.Torrent.GetTorrents")
defer span.End()
2024-08-31 23:00:13 +00:00
var formData = url.Values{}
err := encoder.Encode(opt, formData)
if err != nil {
return nil, err
}
if len(opt.Hashes) != 0 {
formData.Add("hashes", strings.Join(opt.Hashes, "|"))
}
apiUrl := fmt.Sprintf("%s/api/v2/torrents/info?%s", c.config.Address, formData.Encode())
result, err := c.doRequest(ctx, &requestData{
url: apiUrl,
})
if err != nil {
return nil, err
}
if result.code != 200 {
return nil, errors.New("get torrents failed: " + string(result.body))
}
var mainData []*TorrentInfo
if err := json.Unmarshal(result.body, &mainData); err != nil {
return nil, err
}
return mainData, nil
}
func (c *client) GetProperties(ctx context.Context, hash string) (*TorrentProperties, error) {
2024-09-08 18:39:11 +00:00
ctx, span := trace.Start(ctx, "qbittorrent.Torrent.GetProperties")
defer span.End()
2024-08-31 23:00:13 +00:00
apiUrl := fmt.Sprintf("%s/api/v2/torrents/properties?hash=%s", c.config.Address, hash)
result, err := c.doRequest(ctx, &requestData{
url: apiUrl,
})
if err != nil {
return nil, err
}
if result.code != 200 {
return nil, errors.New("get torrent properties failed: " + string(result.body))
}
var mainData = new(TorrentProperties)
if err := json.Unmarshal(result.body, mainData); err != nil {
return nil, err
}
return mainData, nil
}
func (c *client) GetTrackers(ctx context.Context, hash string) ([]*TorrentTracker, error) {
2024-09-08 18:39:11 +00:00
ctx, span := trace.Start(ctx, "qbittorrent.Torrent.GetTrackers")
defer span.End()
2024-08-31 23:00:13 +00:00
apiUrl := fmt.Sprintf("%s/api/v2/torrents/trackers?hash=%s", c.config.Address, hash)
result, err := c.doRequest(ctx, &requestData{
url: apiUrl,
})
if err != nil {
return nil, err
}
if result.code != 200 {
return nil, errors.New("get torrent trackers failed: " + string(result.body))
}
var mainData []*TorrentTracker
if err := json.Unmarshal(result.body, &mainData); err != nil {
return nil, err
}
return mainData, nil
}
func (c *client) GetWebSeeds(ctx context.Context, hash string) ([]*TorrentWebSeed, error) {
2024-09-08 18:39:11 +00:00
ctx, span := trace.Start(ctx, "qbittorrent.Torrent.GetWebSeeds")
defer span.End()
2024-08-31 23:00:13 +00:00
apiUrl := fmt.Sprintf("%s/api/v2/torrents/webseeds?hash=%s", c.config.Address, hash)
result, err := c.doRequest(ctx, &requestData{
url: apiUrl,
})
if err != nil {
return nil, err
}
if result.code != 200 {
return nil, errors.New("get torrent web seeds failed: " + string(result.body))
}
var mainData []*TorrentWebSeed
if err := json.Unmarshal(result.body, &mainData); err != nil {
return nil, err
}
return mainData, nil
}
func sliceItoa[E constraints.Integer](in []E) []string {
out := make([]string, 0, len(in))
for _, v := range in {
out = append(out, strconv.FormatInt(int64(v), 10))
}
return out
}
func (c *client) GetContents(ctx context.Context, hash string, indexes ...int) ([]*TorrentContent, error) {
2024-09-08 18:39:11 +00:00
ctx, span := trace.Start(ctx, "qbittorrent.Torrent.GetContents")
defer span.End()
2024-08-31 23:00:13 +00:00
var apiUrl string
if len(indexes) != 0 {
apiUrl = fmt.Sprintf("%s/api/v2/torrents/files?hash=%s&indexes=%s", c.config.Address, hash, strings.Join(sliceItoa(indexes), "|"))
} else {
apiUrl = fmt.Sprintf("%s/api/v2/torrents/files?hash=%s", c.config.Address, hash)
}
result, err := c.doRequest(ctx, &requestData{
url: apiUrl,
})
if err != nil {
return nil, err
}
if result.code != 200 {
return nil, errors.New("get torrent web seeds failed: " + string(result.body))
}
var mainData []*TorrentContent
if err := json.Unmarshal(result.body, &mainData); err != nil {
return nil, err
}
return mainData, nil
}
func (c *client) GetPiecesStates(ctx context.Context, hash string) ([]int, error) {
2024-09-08 18:39:11 +00:00
ctx, span := trace.Start(ctx, "qbittorrent.Torrent.GetPiecesStates")
defer span.End()
2024-08-31 23:00:13 +00:00
var apiUrl = fmt.Sprintf("%s/api/v2/torrents/pieceStates?hash=%s", c.config.Address, hash)
result, err := c.doRequest(ctx, &requestData{
url: apiUrl,
})
if err != nil {
return nil, err
}
if result.code != 200 {
return nil, errors.New("get torrent pieces states failed: " + string(result.body))
}
var mainData []int
if err := json.Unmarshal(result.body, &mainData); err != nil {
return nil, err
}
return mainData, nil
}
func (c *client) GetPiecesHashes(ctx context.Context, hash string) ([]string, error) {
2024-09-08 18:39:11 +00:00
ctx, span := trace.Start(ctx, "qbittorrent.Torrent.GetPiecesHashes")
defer span.End()
2024-08-31 23:00:13 +00:00
var apiUrl = fmt.Sprintf("%s/api/v2/torrents/pieceHashes?hash=%s", c.config.Address, hash)
result, err := c.doRequest(ctx, &requestData{
url: apiUrl,
})
if err != nil {
return nil, err
}
if result.code != 200 {
return nil, errors.New("get torrent pieces states failed: " + string(result.body))
}
var mainData []string
if err := json.Unmarshal(result.body, &mainData); err != nil {
return nil, err
}
return mainData, nil
}
func (c *client) PauseTorrents(ctx context.Context, hashes []string) error {
2024-09-08 18:39:11 +00:00
ctx, span := trace.Start(ctx, "qbittorrent.Torrent.PauseTorrents")
defer span.End()
2024-08-31 23:00:13 +00:00
if len(hashes) == 0 {
return errors.New("no torrent hashes provided")
}
var formData = url.Values{}
formData.Add("hashes", strings.Join(hashes, "|"))
var apiUrl = fmt.Sprintf("%s/api/v2/torrents/pause", c.config.Address)
result, err := c.doRequest(ctx, &requestData{
url: apiUrl,
method: http.MethodPost,
body: strings.NewReader(formData.Encode()),
})
if err != nil {
return err
}
if result.code != 200 {
return errors.New("pause torrents failed: " + string(result.body))
}
return nil
}
func (c *client) ResumeTorrents(ctx context.Context, hashes []string) error {
2024-09-08 18:39:11 +00:00
ctx, span := trace.Start(ctx, "qbittorrent.Torrent.ResumeTorrents")
defer span.End()
2024-08-31 23:00:13 +00:00
if len(hashes) == 0 {
return errors.New("no torrent hashes provided")
}
var formData = url.Values{}
formData.Add("hashes", strings.Join(hashes, "|"))
var apiUrl = fmt.Sprintf("%s/api/v2/torrents/resume", c.config.Address)
result, err := c.doRequest(ctx, &requestData{
url: apiUrl,
method: http.MethodPost,
body: strings.NewReader(formData.Encode()),
})
if err != nil {
return err
}
if result.code != 200 {
return errors.New("resume torrents failed: " + string(result.body))
}
return nil
}
func (c *client) DeleteTorrents(ctx context.Context, hashes []string, deleteFile bool) error {
2024-09-08 18:39:11 +00:00
ctx, span := trace.Start(ctx, "qbittorrent.Torrent.DeleteTorrents")
defer span.End()
2024-08-31 23:00:13 +00:00
if len(hashes) == 0 {
return errors.New("no torrent hashes provided")
}
var formData = url.Values{}
formData.Add("hashes", strings.Join(hashes, "|"))
formData.Add("deleteFile", strconv.FormatBool(deleteFile))
2024-11-15 13:35:01 +00:00
var apiUrl = fmt.Sprintf("%s/api/v2/torrents/delete", c.config.Address)
2024-08-31 23:00:13 +00:00
result, err := c.doRequest(ctx, &requestData{
url: apiUrl,
method: http.MethodPost,
body: strings.NewReader(formData.Encode()),
})
if err != nil {
return err
}
if result.code != 200 {
return errors.New("delete torrents failed: " + string(result.body))
}
return nil
}
func (c *client) RecheckTorrents(ctx context.Context, hashes []string) error {
2024-09-08 18:39:11 +00:00
ctx, span := trace.Start(ctx, "qbittorrent.Torrent.RecheckTorrents")
defer span.End()
2024-08-31 23:00:13 +00:00
if len(hashes) == 0 {
return errors.New("no torrent hashes provided")
}
var formData = url.Values{}
formData.Add("hashes", strings.Join(hashes, "|"))
var apiUrl = fmt.Sprintf("%s/api/v2/torrents/recheck", c.config.Address)
result, err := c.doRequest(ctx, &requestData{
url: apiUrl,
method: http.MethodPost,
body: strings.NewReader(formData.Encode()),
})
if err != nil {
return err
}
if result.code != 200 {
return errors.New("recheck torrents failed: " + string(result.body))
}
return nil
}
func (c *client) ReAnnounceTorrents(ctx context.Context, hashes []string) error {
2024-09-08 18:39:11 +00:00
ctx, span := trace.Start(ctx, "qbittorrent.Torrent.ReAnnounceTorrents")
defer span.End()
2024-08-31 23:00:13 +00:00
if len(hashes) == 0 {
return errors.New("no torrent hashes provided")
}
var formData = url.Values{}
formData.Add("hashes", strings.Join(hashes, "|"))
var apiUrl = fmt.Sprintf("%s/api/v2/torrents/reannounce", c.config.Address)
result, err := c.doRequest(ctx, &requestData{
url: apiUrl,
method: http.MethodPost,
body: strings.NewReader(formData.Encode()),
})
if err != nil {
return err
}
if result.code != 200 {
return errors.New("reannounce torrents failed: " + string(result.body))
}
return nil
}
func (c *client) AddNewTorrent(ctx context.Context, opt *TorrentAddOption) error {
2024-09-08 18:39:11 +00:00
ctx, span := trace.Start(ctx, "qbittorrent.Torrent.AddNewTorrent")
defer span.End()
2024-08-31 23:00:13 +00:00
var requestBody bytes.Buffer
var writer = multipart.NewWriter(&requestBody)
if len(opt.URLs) == 0 && len(opt.Torrents) == 0 {
return errors.New("no torrent url or data provided")
}
if opt.SavePath != "" {
_ = writer.WriteField("savepath", opt.SavePath)
}
if opt.Cookies != "" {
_ = writer.WriteField("cookies", opt.Cookies)
}
if opt.Category != "" {
_ = writer.WriteField("category", opt.Category)
}
if len(opt.Tags) != 0 {
_ = writer.WriteField("tags", strings.Join(opt.Tags, ","))
}
if opt.SkipChecking {
_ = writer.WriteField("skip_checking", "true")
}
if opt.Paused {
_ = writer.WriteField("paused", "true")
}
if opt.RootFolder {
_ = writer.WriteField("root_folder", "true")
}
if opt.Rename != "" {
_ = writer.WriteField("rename", opt.Rename)
}
if opt.UpLimit != 0 {
_ = writer.WriteField("upLimit", strconv.Itoa(opt.UpLimit))
}
if opt.DlLimit != 0 {
_ = writer.WriteField("dlLimit", strconv.Itoa(opt.DlLimit))
}
if opt.RatioLimit != 0 {
_ = writer.WriteField("ratioLimit", strconv.FormatFloat(opt.RatioLimit, 'f', -1, 64))
}
if opt.SeedingTimeLimit != 0 {
_ = writer.WriteField("seedingTimeLimit", strconv.Itoa(opt.SeedingTimeLimit))
}
if opt.AutoTMM {
_ = writer.WriteField("autoTMM", "true")
}
if opt.SequentialDownload != "" {
_ = writer.WriteField("sequentialDownload", opt.SequentialDownload)
}
if opt.FirstLastPiecePrio != "" {
_ = writer.WriteField("firstLastPiecePrio", opt.FirstLastPiecePrio)
}
if len(opt.URLs) != 0 {
_ = writer.WriteField("urls", strings.Join(opt.URLs, "\n"))
} else if len(opt.Torrents) != 0 {
for _, torrent := range opt.Torrents {
formFile, err := writer.CreateFormFile("torrents", torrent.Filename)
if err != nil {
return err
}
_, err = io.Copy(formFile, bytes.NewReader(torrent.Data))
if err != nil {
return err
}
}
}
_ = writer.Close()
var apiUrl = fmt.Sprintf("%s/api/v2/torrents/add", c.config.Address)
result, err := c.doRequest(ctx, &requestData{
url: apiUrl,
method: http.MethodPost,
contentType: writer.FormDataContentType(),
body: &requestBody,
})
if err != nil {
return err
}
if result.code != 200 {
return errors.New("add torrents failed: " + string(result.body))
}
return nil
}
func (c *client) AddTrackers(ctx context.Context, hash string, urls []string) error {
2024-09-08 18:39:11 +00:00
ctx, span := trace.Start(ctx, "qbittorrent.Torrent.AddTrackers")
defer span.End()
2024-08-31 23:00:13 +00:00
if len(urls) == 0 {
return errors.New("no torrent tracker provided")
}
var formData = url.Values{}
formData.Add("urls", strings.Join(urls, "%0A"))
formData.Add("hash", hash)
var apiUrl = fmt.Sprintf("%s/api/v2/torrents/addTrackers", c.config.Address)
result, err := c.doRequest(ctx, &requestData{
url: apiUrl,
method: http.MethodPost,
body: strings.NewReader(formData.Encode()),
})
if err != nil {
return err
}
if result.code != 200 {
return errors.New("add torrent trackers failed: " + string(result.body))
}
return nil
}
func (c *client) EditTrackers(ctx context.Context, hash, origUrl, newUrl string) error {
2024-09-08 18:39:11 +00:00
ctx, span := trace.Start(ctx, "qbittorrent.Torrent.EditTrackers")
defer span.End()
2024-08-31 23:00:13 +00:00
var formData = url.Values{}
formData.Add("origUrl", origUrl)
formData.Add("newUrl", newUrl)
formData.Add("hash", hash)
var apiUrl = fmt.Sprintf("%s/api/v2/torrents/editTracker", c.config.Address)
result, err := c.doRequest(ctx, &requestData{
url: apiUrl,
method: http.MethodPost,
body: strings.NewReader(formData.Encode()),
})
if err != nil {
return err
}
if result.code != 200 {
return errors.New("edit torrent trackers failed: " + string(result.body))
}
return nil
}
func (c *client) RemoveTrackers(ctx context.Context, hash string, urls []string) error {
2024-09-08 18:39:11 +00:00
ctx, span := trace.Start(ctx, "qbittorrent.Torrent.RemoveTrackers")
defer span.End()
2024-08-31 23:00:13 +00:00
if len(urls) == 0 {
return errors.New("no torrent tracker provided")
}
var formData = url.Values{}
formData.Add("hash", hash)
formData.Add("urls", strings.Join(urls, "|"))
var apiUrl = fmt.Sprintf("%s/api/v2/torrents/removeTrackers", c.config.Address)
result, err := c.doRequest(ctx, &requestData{
url: apiUrl,
method: http.MethodPost,
body: strings.NewReader(formData.Encode()),
})
if err != nil {
return err
}
if result.code != 200 {
return errors.New("remove torrent trackers failed: " + string(result.body))
}
return nil
}
func (c *client) AddPeers(ctx context.Context, hashes []string, peers []string) error {
2024-09-08 18:39:11 +00:00
ctx, span := trace.Start(ctx, "qbittorrent.Torrent.AddPeers")
defer span.End()
2024-08-31 23:00:13 +00:00
if len(hashes) == 0 {
return errors.New("no hashes provided")
}
if len(peers) == 0 {
return errors.New("no peers provided")
}
var formData = url.Values{}
formData.Add("hashes", strings.Join(hashes, "|"))
formData.Add("peers", strings.Join(peers, "|"))
var apiUrl = fmt.Sprintf("%s/api/v2/torrents/addPeers", c.config.Address)
result, err := c.doRequest(ctx, &requestData{
url: apiUrl,
method: http.MethodPost,
body: strings.NewReader(formData.Encode()),
})
if err != nil {
return err
}
if result.code != 200 {
return errors.New("addPeers torrents failed: " + string(result.body))
}
return nil
}
func (c *client) IncreasePriority(ctx context.Context, hashes []string) error {
2024-09-08 18:39:11 +00:00
ctx, span := trace.Start(ctx, "qbittorrent.Torrent.IncreasePriority")
defer span.End()
2024-08-31 23:00:13 +00:00
if len(hashes) == 0 {
return errors.New("no hashes provided")
}
var formData = url.Values{}
formData.Add("hashes", strings.Join(hashes, "|"))
var apiUrl = fmt.Sprintf("%s/api/v2/torrents/increasePrio", c.config.Address)
result, err := c.doRequest(ctx, &requestData{
url: apiUrl,
method: http.MethodPost,
body: strings.NewReader(formData.Encode()),
})
if err != nil {
return err
}
if result.code != 200 {
return errors.New("increasePrio torrents failed: " + string(result.body))
}
return nil
}
func (c *client) DecreasePriority(ctx context.Context, hashes []string) error {
2024-09-08 18:39:11 +00:00
ctx, span := trace.Start(ctx, "qbittorrent.Torrent.DecreasePriority")
defer span.End()
2024-08-31 23:00:13 +00:00
if len(hashes) == 0 {
return errors.New("no hashes provided")
}
var formData = url.Values{}
formData.Add("hashes", strings.Join(hashes, "|"))
var apiUrl = fmt.Sprintf("%s/api/v2/torrents/decreasePrio", c.config.Address)
result, err := c.doRequest(ctx, &requestData{
url: apiUrl,
method: http.MethodPost,
body: strings.NewReader(formData.Encode()),
})
if err != nil {
return err
}
if result.code != 200 {
return errors.New("decreasePrio torrents failed: " + string(result.body))
}
return nil
}
func (c *client) MaxPriority(ctx context.Context, hashes []string) error {
2024-09-08 18:39:11 +00:00
ctx, span := trace.Start(ctx, "qbittorrent.Torrent.MaxPriority")
defer span.End()
2024-08-31 23:00:13 +00:00
if len(hashes) == 0 {
return errors.New("no hashes provided")
}
var formData = url.Values{}
formData.Add("hashes", strings.Join(hashes, "|"))
var apiUrl = fmt.Sprintf("%s/api/v2/torrents/topPrio", c.config.Address)
result, err := c.doRequest(ctx, &requestData{
url: apiUrl,
method: http.MethodPost,
body: strings.NewReader(formData.Encode()),
})
if err != nil {
return err
}
if result.code != 200 {
return errors.New("topPrio torrents failed: " + string(result.body))
}
return nil
}
func (c *client) MinPriority(ctx context.Context, hashes []string) error {
2024-09-08 18:39:11 +00:00
ctx, span := trace.Start(ctx, "qbittorrent.Torrent.MinPriority")
defer span.End()
2024-08-31 23:00:13 +00:00
if len(hashes) == 0 {
return errors.New("no hashes provided")
}
var formData = url.Values{}
formData.Add("hashes", strings.Join(hashes, "|"))
var apiUrl = fmt.Sprintf("%s/api/v2/torrents/bottomPrio", c.config.Address)
result, err := c.doRequest(ctx, &requestData{
url: apiUrl,
method: http.MethodPost,
body: strings.NewReader(formData.Encode()),
})
if err != nil {
return err
}
if result.code != 200 {
return errors.New("bottomPrio torrents failed: " + string(result.body))
}
return nil
}
2024-09-24 13:26:15 +00:00
func (c *client) SetFilePriority(ctx context.Context, hash string, id int, priority Priority) error {
2024-09-08 18:39:11 +00:00
ctx, span := trace.Start(ctx, "qbittorrent.Torrent.SetFilePriority")
defer span.End()
2024-08-31 23:00:13 +00:00
var formData = url.Values{}
formData.Add("hash", hash)
2024-09-24 13:26:15 +00:00
formData.Add("id", strconv.Itoa(id))
formData.Add("priority", strconv.Itoa(int(priority)))
2024-08-31 23:00:13 +00:00
var apiUrl = fmt.Sprintf("%s/api/v2/torrents/filePrio", c.config.Address)
result, err := c.doRequest(ctx, &requestData{
url: apiUrl,
method: http.MethodPost,
body: strings.NewReader(formData.Encode()),
})
if err != nil {
return err
}
if result.code != 200 {
return errors.New("filePrio torrents failed: " + string(result.body))
}
return nil
}
func (c *client) GetDownloadLimit(ctx context.Context, hashes []string) (map[string]int, error) {
2024-09-08 18:39:11 +00:00
ctx, span := trace.Start(ctx, "qbittorrent.Torrent.GetDownloadLimit")
defer span.End()
2024-08-31 23:00:13 +00:00
if len(hashes) == 0 {
return nil, errors.New("no hashes provided")
}
var formData = url.Values{}
formData.Add("hashes", strings.Join(hashes, "|"))
var apiUrl = fmt.Sprintf("%s/api/v2/torrents/downloadLimit", c.config.Address)
result, err := c.doRequest(ctx, &requestData{
url: apiUrl,
method: http.MethodPost,
body: strings.NewReader(formData.Encode()),
})
if err != nil {
return nil, err
}
if result.code != 200 {
return nil, errors.New("get torrents download limit failed: " + string(result.body))
}
var data = make(map[string]int)
err = json.Unmarshal(result.body, &data)
return data, err
}
func (c *client) SetDownloadLimit(ctx context.Context, hashes []string, limit int) error {
2024-09-08 18:39:11 +00:00
ctx, span := trace.Start(ctx, "qbittorrent.Torrent.SetDownloadLimit")
defer span.End()
2024-08-31 23:00:13 +00:00
if len(hashes) == 0 {
return errors.New("no hashes provided")
}
var formData = url.Values{}
formData.Add("hashes", strings.Join(hashes, "|"))
formData.Add("limit", strconv.Itoa(limit))
var apiUrl = fmt.Sprintf("%s/api/v2/torrents/setDownloadLimit", c.config.Address)
result, err := c.doRequest(ctx, &requestData{
url: apiUrl,
method: http.MethodPost,
body: strings.NewReader(formData.Encode()),
})
if err != nil {
return err
}
if result.code != 200 {
return errors.New("set torrents download limit failed: " + string(result.body))
}
return err
}
func (c *client) SetShareLimit(ctx context.Context, hashes []string, ratioLimit float64, seedingTimeLimit, inactiveSeedingTimeLimit int) error {
2024-09-08 18:39:11 +00:00
ctx, span := trace.Start(ctx, "qbittorrent.Torrent.SetShareLimit")
defer span.End()
2024-08-31 23:00:13 +00:00
if len(hashes) == 0 {
return errors.New("no hashes provided")
}
var formData = url.Values{}
formData.Add("hashes", strings.Join(hashes, "|"))
formData.Add("ratioLimit", strconv.FormatFloat(ratioLimit, 'f', -1, 64))
formData.Add("seedingTimeLimit", strconv.Itoa(seedingTimeLimit))
formData.Add("inactiveSeedingTimeLimit", strconv.Itoa(inactiveSeedingTimeLimit))
var apiUrl = fmt.Sprintf("%s/api/v2/torrents/setShareLimits", c.config.Address)
result, err := c.doRequest(ctx, &requestData{
url: apiUrl,
method: http.MethodPost,
body: strings.NewReader(formData.Encode()),
})
if err != nil {
return err
}
if result.code != 200 {
return errors.New("set torrents share limit failed: " + string(result.body))
}
return err
}
func (c *client) GetUploadLimit(ctx context.Context, hashes []string) (map[string]int, error) {
2024-09-08 18:39:11 +00:00
ctx, span := trace.Start(ctx, "qbittorrent.Torrent.GetUploadLimit")
defer span.End()
2024-08-31 23:00:13 +00:00
if len(hashes) == 0 {
return nil, errors.New("no hashes provided")
}
var formData = url.Values{}
formData.Add("hashes", strings.Join(hashes, "|"))
var apiUrl = fmt.Sprintf("%s/api/v2/torrents/uploadLimit", c.config.Address)
result, err := c.doRequest(ctx, &requestData{
url: apiUrl,
method: http.MethodPost,
body: strings.NewReader(formData.Encode()),
})
if err != nil {
return nil, err
}
if result.code != 200 {
return nil, errors.New("get torrents upload limit failed: " + string(result.body))
}
var data = make(map[string]int)
err = json.Unmarshal(result.body, &data)
return data, err
}
func (c *client) SetUploadLimit(ctx context.Context, hashes []string, limit int) error {
2024-09-08 18:39:11 +00:00
ctx, span := trace.Start(ctx, "qbittorrent.Torrent.SetUploadLimit")
defer span.End()
2024-08-31 23:00:13 +00:00
if len(hashes) == 0 {
return errors.New("no hashes provided")
}
var formData = url.Values{}
formData.Add("hashes", strings.Join(hashes, "|"))
formData.Add("limit", strconv.Itoa(limit))
var apiUrl = fmt.Sprintf("%s/api/v2/torrents/setUploadLimit", c.config.Address)
result, err := c.doRequest(ctx, &requestData{
url: apiUrl,
method: http.MethodPost,
body: strings.NewReader(formData.Encode()),
})
if err != nil {
return err
}
if result.code != 200 {
return errors.New("set torrents upload limit failed: " + string(result.body))
}
return err
}
func (c *client) SetLocation(ctx context.Context, hashes []string, location string) error {
2024-09-08 18:39:11 +00:00
ctx, span := trace.Start(ctx, "qbittorrent.Torrent.SetLocation")
defer span.End()
2024-08-31 23:00:13 +00:00
if len(hashes) == 0 {
return errors.New("no hashes provided")
}
var formData = url.Values{}
formData.Add("hashes", strings.Join(hashes, "|"))
formData.Add("location", location)
var apiUrl = fmt.Sprintf("%s/api/v2/torrents/setLocation", c.config.Address)
result, err := c.doRequest(ctx, &requestData{
url: apiUrl,
method: http.MethodPost,
body: strings.NewReader(formData.Encode()),
})
if err != nil {
return err
}
if result.code != 200 {
return errors.New("set torrents location failed: " + string(result.body))
}
return err
}
func (c *client) SetName(ctx context.Context, hash string, name string) error {
2024-09-08 18:39:11 +00:00
ctx, span := trace.Start(ctx, "qbittorrent.Torrent.SetName")
defer span.End()
2024-08-31 23:00:13 +00:00
var formData = url.Values{}
formData.Add("hash", hash)
formData.Add("name", name)
var apiUrl = fmt.Sprintf("%s/api/v2/torrents/rename", c.config.Address)
result, err := c.doRequest(ctx, &requestData{
url: apiUrl,
method: http.MethodPost,
body: strings.NewReader(formData.Encode()),
})
if err != nil {
return err
}
if result.code != 200 {
return errors.New("set torrents name failed: " + string(result.body))
}
return err
}
func (c *client) SetCategory(ctx context.Context, hashes []string, category string) error {
2024-09-08 18:39:11 +00:00
ctx, span := trace.Start(ctx, "qbittorrent.Torrent.SetCategory")
defer span.End()
2024-08-31 23:00:13 +00:00
if len(hashes) == 0 {
return errors.New("no hashes provided")
}
var formData = url.Values{}
formData.Add("hashes", strings.Join(hashes, "|"))
formData.Add("category", category)
var apiUrl = fmt.Sprintf("%s/api/v2/torrents/setCategory", c.config.Address)
result, err := c.doRequest(ctx, &requestData{
url: apiUrl,
method: http.MethodPost,
body: strings.NewReader(formData.Encode()),
})
if err != nil {
return err
}
if result.code != 200 {
return errors.New("set torrents category failed: " + string(result.body))
}
return err
}
func (c *client) GetCategories(ctx context.Context) (map[string]*TorrentCategory, error) {
2024-09-08 18:39:11 +00:00
ctx, span := trace.Start(ctx, "qbittorrent.Torrent.GetCategories")
defer span.End()
2024-08-31 23:00:13 +00:00
var apiUrl = fmt.Sprintf("%s/api/v2/torrents/categories", c.config.Address)
result, err := c.doRequest(ctx, &requestData{
url: apiUrl,
method: http.MethodPost,
})
if err != nil {
return nil, err
}
if result.code != 200 {
return nil, errors.New("get torrents upload limit failed: " + string(result.body))
}
var data = make(map[string]*TorrentCategory)
err = json.Unmarshal(result.body, &data)
return data, err
}
func (c *client) AddNewCategory(ctx context.Context, category, savePath string) error {
2024-09-08 18:39:11 +00:00
ctx, span := trace.Start(ctx, "qbittorrent.Torrent.AddNewCategory")
defer span.End()
2024-08-31 23:00:13 +00:00
var formData = url.Values{}
formData.Add("category", category)
formData.Add("savePath", savePath)
var apiUrl = fmt.Sprintf("%s/api/v2/torrents/createCategory", c.config.Address)
result, err := c.doRequest(ctx, &requestData{
url: apiUrl,
method: http.MethodPost,
body: strings.NewReader(formData.Encode()),
})
if err != nil {
return err
}
if result.code != 200 {
return errors.New("add new category failed: " + string(result.body))
}
return err
}
func (c *client) EditCategory(ctx context.Context, category, savePath string) error {
2024-09-08 18:39:11 +00:00
ctx, span := trace.Start(ctx, "qbittorrent.Torrent.EditCategory")
defer span.End()
2024-08-31 23:00:13 +00:00
var formData = url.Values{}
formData.Add("category", category)
formData.Add("savePath", savePath)
var apiUrl = fmt.Sprintf("%s/api/v2/torrents/editCategory", c.config.Address)
result, err := c.doRequest(ctx, &requestData{
url: apiUrl,
method: http.MethodPost,
body: strings.NewReader(formData.Encode()),
})
if err != nil {
return err
}
if result.code != 200 {
return errors.New("add new category failed: " + string(result.body))
}
return err
}
func (c *client) RemoveCategories(ctx context.Context, categories []string) error {
2024-09-08 18:39:11 +00:00
ctx, span := trace.Start(ctx, "qbittorrent.Torrent.RemoveCategories")
defer span.End()
2024-08-31 23:00:13 +00:00
var formData = url.Values{}
formData.Add("categories", strings.Join(categories, "\n"))
var apiUrl = fmt.Sprintf("%s/api/v2/torrents/removeCategories", c.config.Address)
result, err := c.doRequest(ctx, &requestData{
url: apiUrl,
method: http.MethodPost,
body: strings.NewReader(formData.Encode()),
})
if err != nil {
return err
}
if result.code != 200 {
return errors.New("remove categories failed: " + string(result.body))
}
return err
}
func (c *client) AddTags(ctx context.Context, hashes []string, tags []string) error {
var formData = url.Values{}
formData.Add("hashes", strings.Join(hashes, "|"))
formData.Add("tags", strings.Join(tags, ","))
var apiUrl = fmt.Sprintf("%s/api/v2/torrents/addTags", c.config.Address)
result, err := c.doRequest(ctx, &requestData{
url: apiUrl,
method: http.MethodPost,
body: strings.NewReader(formData.Encode()),
})
if err != nil {
return err
}
if result.code != 200 {
return errors.New("add torrent tags failed: " + string(result.body))
}
return err
}
func (c *client) RemoveTags(ctx context.Context, hashes []string, tags []string) error {
2024-09-08 18:39:11 +00:00
ctx, span := trace.Start(ctx, "qbittorrent.Torrent.RemoveTags")
defer span.End()
2024-08-31 23:00:13 +00:00
var formData = url.Values{}
formData.Add("hashes", strings.Join(hashes, "|"))
formData.Add("tags", strings.Join(tags, ","))
var apiUrl = fmt.Sprintf("%s/api/v2/torrents/removeTags", c.config.Address)
result, err := c.doRequest(ctx, &requestData{
url: apiUrl,
method: http.MethodPost,
body: strings.NewReader(formData.Encode()),
})
if err != nil {
return err
}
if result.code != 200 {
return errors.New("remove torrent tags failed: " + string(result.body))
}
return err
}
func (c *client) GetTags(ctx context.Context) ([]string, error) {
2024-09-08 18:39:11 +00:00
ctx, span := trace.Start(ctx, "qbittorrent.Torrent.GetTags")
defer span.End()
2024-08-31 23:00:13 +00:00
var apiUrl = fmt.Sprintf("%s/api/v2/torrents/tags", c.config.Address)
result, err := c.doRequest(ctx, &requestData{
url: apiUrl,
method: http.MethodGet,
})
if err != nil {
return nil, err
}
if result.code != 200 {
return nil, errors.New("get tags failed: " + string(result.body))
}
var data []string
err = json.Unmarshal(result.body, &data)
return data, err
}
func (c *client) CreateTags(ctx context.Context, tags []string) error {
2024-09-08 18:39:11 +00:00
ctx, span := trace.Start(ctx, "qbittorrent.Torrent.CreateTags")
defer span.End()
2024-08-31 23:00:13 +00:00
var formData = url.Values{}
formData.Add("tags", strings.Join(tags, ","))
var apiUrl = fmt.Sprintf("%s/api/v2/torrents/createTags", c.config.Address)
result, err := c.doRequest(ctx, &requestData{
url: apiUrl,
method: http.MethodPost,
body: strings.NewReader(formData.Encode()),
})
if err != nil {
return err
}
if result.code != 200 {
return errors.New("create tags failed: " + string(result.body))
}
return err
}
func (c *client) DeleteTags(ctx context.Context, tags []string) error {
2024-09-08 18:39:11 +00:00
ctx, span := trace.Start(ctx, "qbittorrent.Torrent.DeleteTags")
defer span.End()
2024-08-31 23:00:13 +00:00
var formData = url.Values{}
formData.Add("tags", strings.Join(tags, ","))
var apiUrl = fmt.Sprintf("%s/api/v2/torrents/deleteTags", c.config.Address)
result, err := c.doRequest(ctx, &requestData{
url: apiUrl,
method: http.MethodPost,
body: strings.NewReader(formData.Encode()),
})
if err != nil {
return err
}
if result.code != 200 {
return errors.New("delete tags failed: " + string(result.body))
}
return err
}
func (c *client) SetAutomaticManagement(ctx context.Context, hashes []string, enable bool) error {
2024-09-08 18:39:11 +00:00
ctx, span := trace.Start(ctx, "qbittorrent.Torrent.SetAutomaticManagement")
defer span.End()
2024-08-31 23:00:13 +00:00
var formData = url.Values{}
formData.Add("hashes", strings.Join(hashes, "|"))
formData.Add("enable", strconv.FormatBool(enable))
var apiUrl = fmt.Sprintf("%s/api/v2/torrents/setAutoManagement", c.config.Address)
result, err := c.doRequest(ctx, &requestData{
url: apiUrl,
method: http.MethodPost,
body: strings.NewReader(formData.Encode()),
})
if err != nil {
return err
}
if result.code != 200 {
return errors.New("set automatic management failed: " + string(result.body))
}
return err
}
func (c *client) ToggleSequentialDownload(ctx context.Context, hashes []string) error {
2024-09-08 18:39:11 +00:00
ctx, span := trace.Start(ctx, "qbittorrent.Torrent.ToggleSequentialDownload")
defer span.End()
2024-08-31 23:00:13 +00:00
var formData = url.Values{}
formData.Add("hashes", strings.Join(hashes, "|"))
var apiUrl = fmt.Sprintf("%s/api/v2/torrents/toggleSequentialDownload", c.config.Address)
result, err := c.doRequest(ctx, &requestData{
url: apiUrl,
method: http.MethodPost,
body: strings.NewReader(formData.Encode()),
})
if err != nil {
return err
}
if result.code != 200 {
return errors.New("toggle sequential download failed: " + string(result.body))
}
return err
}
func (c *client) SetFirstLastPiecePriority(ctx context.Context, hashes []string) error {
2024-09-08 18:39:11 +00:00
ctx, span := trace.Start(ctx, "qbittorrent.Torrent.SetFirstLastPiecePriority")
defer span.End()
2024-08-31 23:00:13 +00:00
var formData = url.Values{}
formData.Add("hashes", strings.Join(hashes, "|"))
var apiUrl = fmt.Sprintf("%s/api/v2/torrents/toggleFirstLastPiecePrio", c.config.Address)
result, err := c.doRequest(ctx, &requestData{
url: apiUrl,
method: http.MethodPost,
body: strings.NewReader(formData.Encode()),
})
if err != nil {
return err
}
if result.code != 200 {
return errors.New("toggle first last piece prio failed: " + string(result.body))
}
return err
}
func (c *client) SetForceStart(ctx context.Context, hashes []string, force bool) error {
2024-09-08 18:39:11 +00:00
ctx, span := trace.Start(ctx, "qbittorrent.Torrent.SetForceStart")
defer span.End()
2024-08-31 23:00:13 +00:00
var formData = url.Values{}
formData.Add("hashes", strings.Join(hashes, "|"))
formData.Add("value", strconv.FormatBool(force))
var apiUrl = fmt.Sprintf("%s/api/v2/torrents/setForceStart", c.config.Address)
result, err := c.doRequest(ctx, &requestData{
url: apiUrl,
method: http.MethodPost,
body: strings.NewReader(formData.Encode()),
})
if err != nil {
return err
}
if result.code != 200 {
return errors.New("set force start failed: " + string(result.body))
}
return err
}
func (c *client) SetSuperSeeding(ctx context.Context, hashes []string, enable bool) error {
2024-09-08 18:39:11 +00:00
ctx, span := trace.Start(ctx, "qbittorrent.Torrent.SetSuperSeeding")
defer span.End()
2024-08-31 23:00:13 +00:00
var formData = url.Values{}
formData.Add("hashes", strings.Join(hashes, "|"))
formData.Add("value", strconv.FormatBool(enable))
var apiUrl = fmt.Sprintf("%s/api/v2/torrents/setSuperSeeding", c.config.Address)
result, err := c.doRequest(ctx, &requestData{
url: apiUrl,
method: http.MethodPost,
body: strings.NewReader(formData.Encode()),
})
if err != nil {
return err
}
if result.code != 200 {
return errors.New("set super seeding failed: " + string(result.body))
}
return err
}
func (c *client) RenameFile(ctx context.Context, hash, oldPath, newPath string) error {
2024-09-08 18:39:11 +00:00
ctx, span := trace.Start(ctx, "qbittorrent.Torrent.RenameFile")
defer span.End()
2024-08-31 23:00:13 +00:00
var formData = url.Values{}
formData.Add("oldPath", oldPath)
formData.Add("newPath", newPath)
formData.Add("hash", hash)
var apiUrl = fmt.Sprintf("%s/api/v2/torrents/renameFile", c.config.Address)
result, err := c.doRequest(ctx, &requestData{
url: apiUrl,
method: http.MethodPost,
body: strings.NewReader(formData.Encode()),
})
if err != nil {
return err
}
if result.code != 200 {
return errors.New("rename file failed: " + string(result.body))
}
return nil
}
func (c *client) RenameFolder(ctx context.Context, hash, oldPath, newPath string) error {
2024-09-08 18:39:11 +00:00
ctx, span := trace.Start(ctx, "qbittorrent.Torrent.RenameFolder")
defer span.End()
2024-08-31 23:00:13 +00:00
var formData = url.Values{}
formData.Add("oldPath", oldPath)
formData.Add("newPath", newPath)
formData.Add("hash", hash)
var apiUrl = fmt.Sprintf("%s/api/v2/torrents/renameFolder", c.config.Address)
result, err := c.doRequest(ctx, &requestData{
url: apiUrl,
method: http.MethodPost,
body: strings.NewReader(formData.Encode()),
})
if err != nil {
return err
}
if result.code != 200 {
return errors.New("rename folder failed: " + string(result.body))
}
return nil
}