mirror of
https://github.com/simon-ding/polaris.git
synced 2026-02-06 23:21:00 +08:00
267 lines
5.2 KiB
Go
267 lines
5.2 KiB
Go
package qbittorrent
|
|
|
|
import (
|
|
"fmt"
|
|
"io/fs"
|
|
"os"
|
|
"path/filepath"
|
|
"polaris/pkg"
|
|
"polaris/pkg/go-qbittorrent/qbt"
|
|
"polaris/pkg/utils"
|
|
"strings"
|
|
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
const btCategory = "polaris"
|
|
|
|
type Info struct {
|
|
URL string
|
|
User string
|
|
Password string
|
|
}
|
|
|
|
type Client struct {
|
|
c *qbt.Client
|
|
category string
|
|
Info
|
|
}
|
|
|
|
func NewClient(url, user, pass string) (*Client, error) {
|
|
// connect to qbittorrent client
|
|
qb := qbt.NewClient(url)
|
|
|
|
// login to the client
|
|
loginOpts := qbt.LoginOptions{
|
|
Username: user,
|
|
Password: pass,
|
|
}
|
|
err := qb.Login(loginOpts)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &Client{c: qb, category: btCategory, Info: Info{URL: url, User: user, Password: pass}}, nil
|
|
}
|
|
|
|
func (c *Client) GetAll() ([]pkg.Torrent, error) {
|
|
tt, err := c.c.Torrents(qbt.TorrentsOptions{Category: &c.category})
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "get torrents")
|
|
}
|
|
var res []pkg.Torrent
|
|
for _, t := range tt {
|
|
t1 := &Torrent{
|
|
c: c.c,
|
|
hash: t.Hash,
|
|
//Info: c.Info,
|
|
}
|
|
res = append(res, t1)
|
|
}
|
|
return res, nil
|
|
}
|
|
|
|
func (c *Client) GetListenPort() (int, error) {
|
|
pref, err := c.c.Preferences()
|
|
if err != nil {
|
|
return 0, errors.Wrap(err, "get preferences")
|
|
}
|
|
|
|
return pref.ListenPort, nil
|
|
}
|
|
|
|
func (c *Client) SetListenPort(port int) error {
|
|
ok, err := c.c.SetPreferences(map[string]any{"listen_port": port})
|
|
if !ok || err != nil {
|
|
return errors.Wrap(err, "set preferences")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (c *Client) Download(link, hash, dir string) (pkg.Torrent, error) {
|
|
err := c.c.DownloadLinks([]string{link}, qbt.DownloadOptions{Savepath: &dir, Category: &c.category})
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "qbt download")
|
|
}
|
|
return &Torrent{hash: hash, c: c.c}, nil
|
|
|
|
}
|
|
|
|
func NewTorrentHash(info Info, hash string) (*Torrent, error) {
|
|
c, err := NewClient(info.URL, info.User, info.Password)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
t := &Torrent{
|
|
c: c.c,
|
|
hash: hash,
|
|
}
|
|
if !t.Exists() {
|
|
return nil, errors.Errorf("torrent not exist: %v", hash)
|
|
}
|
|
return t, nil
|
|
}
|
|
|
|
func NewTorrent(info Info, link string) (*Torrent, error) {
|
|
magnet, err := utils.Link2Magnet(link)
|
|
if err != nil {
|
|
return nil, errors.Errorf("converting link to magnet error, link: %v, error: %v", link, err)
|
|
}
|
|
|
|
hash, err := utils.MagnetHash(magnet)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return NewTorrentHash(info, hash)
|
|
}
|
|
|
|
type Torrent struct {
|
|
c *qbt.Client
|
|
hash string
|
|
//info Info
|
|
}
|
|
|
|
func (t *Torrent) GetHash() string {
|
|
return t.hash
|
|
}
|
|
|
|
func (t *Torrent) getTorrent() (*qbt.TorrentInfo, error) {
|
|
all, err := t.c.Torrents(qbt.TorrentsOptions{Hashes: []string{t.hash}})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if len(all) == 0 {
|
|
return nil, fmt.Errorf("no such torrent: %v", t.hash)
|
|
}
|
|
return &all[0], nil
|
|
}
|
|
|
|
func (t *Torrent) Name() (string, error) {
|
|
dir, err := t.getTorrentBaseNameOrDir()
|
|
if err != nil { //use torrent name
|
|
qb, err := t.getTorrent()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return qb.Name, nil
|
|
}
|
|
return dir, nil
|
|
}
|
|
|
|
// https://github.com/qbittorrent/qBittorrent/issues/13572
|
|
func (t *Torrent) getTorrentBaseNameOrDir() (string, error) {
|
|
files, err := t.c.TorrentFiles(t.hash)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
if len(files) == 0 {
|
|
return "", errors.Wrap(err, "no file")
|
|
}
|
|
name := files[0].Name
|
|
dir := strings.Split(name, string(os.PathSeparator))[0]
|
|
return dir, nil
|
|
}
|
|
|
|
func (t *Torrent) Progress() (int, error) {
|
|
qb, err := t.getTorrent()
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
p := qb.Progress * 100
|
|
if p >= 100 {
|
|
return 100, nil
|
|
}
|
|
if int(p) == 100 {
|
|
return 99, nil
|
|
}
|
|
|
|
return int(p), nil
|
|
}
|
|
|
|
func (t *Torrent) Stop() error {
|
|
return t.c.Pause([]string{t.hash})
|
|
}
|
|
|
|
func (t *Torrent) Start() error {
|
|
ok, err := t.c.Resume([]string{t.hash})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !ok {
|
|
return fmt.Errorf("status not 200")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (t *Torrent) Remove() error {
|
|
ok, err := t.c.Delete([]string{t.hash}, true)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !ok {
|
|
return fmt.Errorf("status not 200")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (t *Torrent) Exists() bool {
|
|
_, err := t.getTorrent()
|
|
return err == nil
|
|
}
|
|
|
|
func (t *Torrent) SeedRatio() (float64, error) {
|
|
qb, err := t.getTorrent()
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return qb.Ratio, nil
|
|
}
|
|
|
|
func (t *Torrent) Walk(f func(string) error) error {
|
|
files, err := t.c.TorrentFiles(t.hash)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, file := range files {
|
|
if err := f(file.Name); err != nil {
|
|
return errors.Errorf("proccess file (%s) error: %v", file.Name, err)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (t *Torrent) WalkFunc() func(fn func(path string, info fs.FileInfo) error) error {
|
|
files, err := t.c.TorrentFiles(t.hash)
|
|
if err != nil {
|
|
return func(fn func(path string, info fs.FileInfo) error) error {
|
|
return err
|
|
}
|
|
}
|
|
path, err := t.c.DefaultSavePath()
|
|
if err != nil {
|
|
return func(fn func(path string, info fs.FileInfo) error) error {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return func(fn func(path string, info fs.FileInfo) error) error {
|
|
for _, file := range files {
|
|
name := filepath.Join(path, file.Name)
|
|
info, err := os.Stat(name)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := fn(name, info); err != nil {
|
|
return errors.Errorf("proccess file (%s) error: %v", file.Name, err)
|
|
}
|
|
}
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|