Skip to content

Commit

Permalink
refactor: add and batch pause resume (#33)
Browse files Browse the repository at this point in the history
  • Loading branch information
xel86 authored Feb 7, 2022
1 parent 5cad64e commit 3c4c6b4
Show file tree
Hide file tree
Showing 4 changed files with 218 additions and 55 deletions.
72 changes: 68 additions & 4 deletions cmd/pause.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package cmd
import (
"fmt"
"os"
"log"
"time"

"github.com/ludviglundgren/qbittorrent-cli/internal/config"
"github.com/ludviglundgren/qbittorrent-cli/pkg/qbittorrent"
Expand All @@ -12,12 +14,34 @@ import (

// RunPause cmd to pause torrents
func RunPause() *cobra.Command {
var (
pauseAll bool
hashes bool
names bool
)

var command = &cobra.Command{
Use: "pause",
Short: "Pause all torrents",
Long: `Pause all torrents`,
Short: "Pause specified torrents",
Long: `Pauses torrents indicated by hash, name or a prefix of either;
whitespace indicates next prefix unless argument is surrounded by quotes`,
}

command.Flags().BoolVar(&pauseAll, "all", false, "Pauses all torrents")
command.Flags().BoolVar(&hashes, "hashes", false, "Provided arguments will be read as torrent hashes")
command.Flags().BoolVar(&names, "names", false, "Provided arguments will be read as torrent names")

command.Run = func(cmd *cobra.Command, args []string) {
if !pauseAll && len(args) < 1 {
log.Printf("Please provide atleast one torrent hash/name as an argument")
return
}

if !pauseAll && !hashes && !names {
log.Printf("Please specifiy if arguments are to be read as hashes or names (--hashes / --names)")
return
}

config.InitConfig()
qbtSettings := qbittorrent.Settings{
Hostname: config.Qbit.Host,
Expand All @@ -33,11 +57,51 @@ func RunPause() *cobra.Command {
os.Exit(1)
}

err = qb.Pause(nil)
if pauseAll {
qb.Pause([]string{"all"})
if err != nil {
fmt.Fprintf(os.Stderr, "ERROR: could not pause torrents: %v\n", err)
os.Exit(1)
}

log.Printf("All torrents paused successfully")
return
}

foundTorrents, err := qb.GetTorrentsByPrefixes(args, hashes, names)
if err != nil {
fmt.Fprintf(os.Stderr, "ERROR: could not pause torrents %v\n", err)
fmt.Fprintf(os.Stderr, "ERROR: failed to retrieve torrents: %v\n", err)
os.Exit(1)
}

hashesToPause := []string{}
for _, torrent := range foundTorrents {
hashesToPause = append(hashesToPause, torrent.Hash)
}

if len(hashesToPause) < 1 {
log.Printf("No torrents found to pause with provided search terms")
return
}

// Split the hashes to pause into groups of 20 to avoid flooding qbittorrent
batch := 20
for i := 0; i < len(hashesToPause); i += batch {
j := i + batch
if j > len(hashesToPause) {
j = len(hashesToPause)
}

qb.Pause(hashesToPause[i:j])
if err != nil {
fmt.Fprintf(os.Stderr, "ERROR: could not pause torrents: %v\n", err)
os.Exit(1)
}

time.Sleep(time.Second * 1)
}

log.Printf("torrent(s) successfully paused")
}

return command
Expand Down
67 changes: 34 additions & 33 deletions cmd/remove.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ package cmd
import (
"fmt"
"os"
"strings"
"log"
"time"

"github.com/ludviglundgren/qbittorrent-cli/internal/config"
"github.com/ludviglundgren/qbittorrent-cli/pkg/qbittorrent"
Expand Down Expand Up @@ -58,48 +58,49 @@ func RunRemove() *cobra.Command {
fmt.Fprintf(os.Stderr, "ERROR: connection failed: %v\n", err)
os.Exit(1)
}

if removeAll {
qb.DeleteTorrents([]string{"all"}, deleteFiles)
if err != nil {
fmt.Fprintf(os.Stderr, "ERROR: could not delete torrents: %v\n", err)
os.Exit(1)
}

log.Printf("All torrents removed successfully")
return
}

torrents, err := qb.GetTorrents()
foundTorrents, err := qb.GetTorrentsByPrefixes(args, hashes, names)
if err != nil {
fmt.Fprintf(os.Stderr, "ERROR: could not retrieve torrents: %v\n", err)
fmt.Fprintf(os.Stderr, "ERROR: failed to retrieve torrents: %v\n", err)
os.Exit(1)
}

foundHashes := map[string]bool{}
for _, torrent := range torrents {
if removeAll {
foundHashes[torrent.Hash] = true
continue
}
hashesToRemove := []string{}
for _, torrent := range foundTorrents {
hashesToRemove = append(hashesToRemove, torrent.Hash)
}

if hashes {
for _, targetHash := range args {
if strings.HasPrefix(torrent.Hash, targetHash) {
foundHashes[torrent.Hash] = true
break
}
}
}
if len(hashesToRemove) < 1 {
log.Printf("No torrents found to remove with provided search terms")
return
}

if names {
for _, targetName := range args {
if strings.HasPrefix(torrent.Name, targetName) {
foundHashes[torrent.Hash] = true
break
}
}
// Split the hashes to remove into groups of 20 to avoid flooding qbittorrent
batch := 20
for i := 0; i < len(hashesToRemove); i += batch {
j := i + batch
if j > len(hashesToRemove) {
j = len(hashesToRemove)
}
}

hashesToRemove := []string{}
for hash := range foundHashes {
hashesToRemove = append(hashesToRemove, hash)
}
qb.DeleteTorrents(hashesToRemove[i:j], deleteFiles)
if err != nil {
fmt.Fprintf(os.Stderr, "ERROR: could not delete torrents: %v\n", err)
os.Exit(1)
}

err = qb.DeleteTorrents(hashesToRemove, deleteFiles)
if err != nil {
fmt.Fprintf(os.Stderr, "ERROR: could not delete torrents: %v\n", err)
os.Exit(1)
time.Sleep(time.Second * 1)
}

log.Printf("torrent(s) successfully deleted")
Expand Down
72 changes: 68 additions & 4 deletions cmd/resume.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package cmd
import (
"fmt"
"os"
"log"
"time"

"github.com/ludviglundgren/qbittorrent-cli/internal/config"
"github.com/ludviglundgren/qbittorrent-cli/pkg/qbittorrent"
Expand All @@ -12,13 +14,34 @@ import (

// RunResume cmd to resume torrents
func RunResume() *cobra.Command {
var (
resumeAll bool
hashes bool
names bool
)

var command = &cobra.Command{
Use: "resume",
Short: "Resume all torrents",
Long: `Resume all torrents`,
Short: "resume specified torrents",
Long: `resumes torrents indicated by hash, name or a prefix of either;
whitespace indicates next prefix unless argument is surrounded by quotes`,
}

command.Flags().BoolVar(&resumeAll, "all", false, "resumes all torrents")
command.Flags().BoolVar(&hashes, "hashes", false, "Provided arguments will be read as torrent hashes")
command.Flags().BoolVar(&names, "names", false, "Provided arguments will be read as torrent names")

command.Run = func(cmd *cobra.Command, args []string) {
if !resumeAll && len(args) < 1 {
log.Printf("Please provide atleast one torrent hash/name as an argument")
return
}

if !resumeAll && !hashes && !names {
log.Printf("Please specifiy if arguments are to be read as hashes or names (--hashes / --names)")
return
}

config.InitConfig()
qbtSettings := qbittorrent.Settings{
Hostname: config.Qbit.Host,
Expand All @@ -34,11 +57,52 @@ func RunResume() *cobra.Command {
os.Exit(1)
}

err = qb.Resume(nil)

if resumeAll {
qb.Resume([]string{"all"})
if err != nil {
fmt.Fprintf(os.Stderr, "ERROR: could not resume torrents: %v\n", err)
os.Exit(1)
}

log.Printf("All torrents resumed successfully")
return
}

foundTorrents, err := qb.GetTorrentsByPrefixes(args, hashes, names)
if err != nil {
fmt.Fprintf(os.Stderr, "ERROR: could not resume torrents %v\n", err)
fmt.Fprintf(os.Stderr, "ERROR: failed to retrieve torrents: %v\n", err)
os.Exit(1)
}

hashesToResume := []string{}
for _, torrent := range foundTorrents {
hashesToResume = append(hashesToResume, torrent.Hash)
}

if len(hashesToResume) < 1 {
log.Printf("No torrents found to resume with provided search terms")
return
}

// Split the hashes to resume into groups of 20 to avoid flooding qbittorrent
batch := 20
for i := 0; i < len(hashesToResume); i += batch {
j := i + batch
if j > len(hashesToResume) {
j = len(hashesToResume)
}

qb.Resume(hashesToResume[i:j])
if err != nil {
fmt.Fprintf(os.Stderr, "ERROR: could not resume torrents: %v\n", err)
os.Exit(1)
}

time.Sleep(time.Second * 1)
}

log.Printf("torrent(s) successfully resumed")
}

return command
Expand Down
62 changes: 48 additions & 14 deletions pkg/qbittorrent/methods.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,46 @@ func (c *Client) GetTorrentByHash(hash string) (string, error) {
return string(data), nil
}

// Search for torrents using provided prefixes; checks against either hashes, names, or both
func (c *Client) GetTorrentsByPrefixes(terms []string, hashes bool, names bool) ([]Torrent, error) {
torrents, err := c.GetTorrents()
if err != nil {
log.Fatalf("ERROR: could not retrieve torrents: %v\n", err)
}

matchedTorrents := map[Torrent]bool{}
for _, torrent := range torrents {
if hashes {
for _, targetHash := range terms {
if strings.HasPrefix(torrent.Hash, targetHash) {
matchedTorrents[torrent] = true
break
}
}

if matchedTorrents[torrent] {
continue
}
}

if names {
for _, targetName := range terms {
if strings.HasPrefix(torrent.Name, targetName) {
matchedTorrents[torrent] = true
break
}
}
}
}

var foundTorrents []Torrent
for torrent := range matchedTorrents {
foundTorrents = append(foundTorrents, torrent)
}

return foundTorrents, nil
}

func (c *Client) GetTorrentTrackers(hash string) ([]TorrentTracker, error) {
var trackers []TorrentTracker

Expand Down Expand Up @@ -253,15 +293,12 @@ func (c *Client) ReAnnounceTorrents(hashes []string) error {

func (c *Client) Pause(hashes []string) error {
v := url.Values{}
encodedHashes := "all"

if len(hashes) > 0 {
// Add hashes together with | separator
encodedHashes = strings.Join(hashes, "|")
}
// Add hashes together with | separator
hv := strings.Join(hashes, "|")
v.Add("hashes", hv)

v.Add("hashes", encodedHashes)
encodedHashes = v.Encode()
encodedHashes := v.Encode()

resp, err := c.get("torrents/pause?"+encodedHashes, nil)
if err != nil {
Expand All @@ -277,15 +314,12 @@ func (c *Client) Pause(hashes []string) error {

func (c *Client) Resume(hashes []string) error {
v := url.Values{}
encodedHashes := "all"

if len(hashes) > 0 {
// Add hashes together with | separator
encodedHashes = strings.Join(hashes, "|")
}
// Add hashes together with | separator
hv := strings.Join(hashes, "|")
v.Add("hashes", hv)

v.Add("hashes", encodedHashes)
encodedHashes = v.Encode()
encodedHashes := v.Encode()

resp, err := c.get("torrents/resume?"+encodedHashes, nil)
if err != nil {
Expand Down

0 comments on commit 3c4c6b4

Please sign in to comment.