Skip to content

Instantly share code, notes, and snippets.

@yegle
Created December 3, 2024 04:07
Show Gist options
  • Save yegle/25e3d02a65a73f31438ef5162005c373 to your computer and use it in GitHub Desktop.
Save yegle/25e3d02a65a73f31438ef5162005c373 to your computer and use it in GitHub Desktop.
qBittorrent: list torrents of which all trackers are failing
package main
import (
"context"
"encoding/json"
"io"
"log"
"net/http"
"strconv"
"time"
"github.com/pkg/errors"
)
const api = "http://qbittorrent:8080/api/v2"
type Torrent struct {
Hash string
Name string
}
const (
DISABLED = iota
NOT_CONNECTED
WORKING
UPDATING
NOT_WORKING
)
type Status int
func (s *Status) MarshalJSON() ([]byte, error) {
return json.Marshal(strconv.Itoa(int(*s)))
}
func (s *Status) UnmarshalJSON(data []byte) error {
i, err := strconv.Atoi(string(data))
*s = Status(i)
return err
}
func (s Status) String() string {
return map[Status]string{
0: "DISABLED",
1: "NOT_CONTACTED",
2: "WORKING",
3: "UPDATING",
4: "NOT_WORKING",
}[s]
}
type Tracker struct {
URL string
Status Status
Msg string
}
var client = http.DefaultClient
func torrents(ctx context.Context) ([]Torrent, error) {
req, err := http.NewRequestWithContext(ctx, "GET", api+"/torrents/info", nil)
if err != nil {
return nil, errors.WithStack(err)
}
resp, err := client.Do(req)
if err != nil {
return nil, errors.WithStack(err)
}
defer resp.Body.Close()
var ret []Torrent
if bytes, err := io.ReadAll(resp.Body); err != nil {
return nil, errors.WithStack(err)
} else if err := json.Unmarshal(bytes, &ret); err != nil {
return nil, errors.WithStack(err)
}
return ret, nil
}
func (t Torrent) Trackers(ctx context.Context) ([]Tracker, error) {
req, err := http.NewRequestWithContext(ctx, "GET", api+"/torrents/trackers?hash="+t.Hash, nil)
if err != nil {
return nil, errors.WithStack(err)
}
resp, err := client.Do(req)
if err != nil {
return nil, errors.WithStack(err)
}
defer resp.Body.Close()
var ret []Tracker
if bytes, err := io.ReadAll(resp.Body); err != nil {
return nil, errors.WithStack(err)
} else if err := json.Unmarshal(bytes, &ret); err != nil {
return nil, errors.WithStack(err)
}
return ret, nil
}
func run() error {
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
defer cancel()
ts, err := torrents(ctx)
if err != nil {
return err
}
for _, t := range ts {
trackers, err := t.Trackers(ctx)
if err != nil {
return err
}
working := false
for _, tracker := range trackers {
if tracker.Status == WORKING {
working = true
break
}
}
if !working {
log.Printf("%s", t.Name)
for _, tracker := range trackers {
log.Printf("\t%v", tracker)
}
}
}
return nil
}
func main() {
if err := run(); err != nil {
log.Fatalf("%+v", err)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment