rsync with progress

This commit is contained in:
Celogeek 2023-08-15 20:17:10 +02:00
parent 4c8fdd956f
commit 8827aca192
Signed by: celogeek
SSH Key Fingerprint: SHA256:DEDfxIK2nUWXbslbRkww3zsauDjhWHlTXar+ak4lDJ4
3 changed files with 148 additions and 21 deletions

62
main.go
View File

@ -2,31 +2,49 @@ package main
import (
"flag"
"fmt"
"log"
"time"
)
type Options struct {
Qbittorent QBitTorrentOptions
type RsyncOptions struct {
Username string
Hostname string
}
func (r *RsyncOptions) Uri(path string) string {
result := fmt.Sprintf("%s:%s", r.Hostname, path)
if r.Username == "" {
return result
}
return fmt.Sprintf("%s@%s", r.Username, result)
}
func main() {
options := &Options{}
flag.StringVar(&options.Qbittorent.Uri, "qbittorrent-uri", "http://localhost:8080", "URI of qbittorrent")
flag.StringVar(&options.Qbittorent.Username, "qbittorrent-username", "", "Username of qbittorrent")
flag.StringVar(&options.Qbittorent.Password, "qbittorrent-password", "", "Password of qbittorrent")
flag.StringVar(&options.Qbittorent.SyncTag, "qbittorrent-sync-tag", "Sync", "Tag of qbittorrent to copy")
flag.StringVar(&options.Qbittorent.SyncedTag, "qbittorrent-synced-tag", "", "Tag of qbittorrent when copy finished")
qbitoptions := &QBitTorrentOptions{}
rsyncoptions := &RsyncOptions{}
dest := ""
flag.StringVar(&qbitoptions.Uri, "qbittorrent-uri", "http://localhost:8080", "URI of qbittorrent")
flag.StringVar(&qbitoptions.Username, "qbittorrent-username", "", "Username of qbittorrent")
flag.StringVar(&qbitoptions.Password, "qbittorrent-password", "", "Password of qbittorrent")
flag.StringVar(&qbitoptions.SyncTag, "qbittorrent-sync-tag", "Sync", "Tag of qbittorrent to copy")
flag.StringVar(&qbitoptions.SyncedTag, "qbittorrent-synced-tag", "", "Tag of qbittorrent when copy finished")
flag.StringVar(&rsyncoptions.Hostname, "rsync-hostname", "", "Rsync host")
flag.StringVar(&rsyncoptions.Username, "rsync-username", "", "Rsync username")
flag.StringVar(&dest, "dest", ".", "Destination directory")
flag.Parse()
if options.Qbittorent.Uri == "" ||
options.Qbittorent.Username == "" ||
options.Qbittorent.Password == "" ||
options.Qbittorent.SyncTag == "" {
if qbitoptions.Uri == "" ||
qbitoptions.Username == "" ||
qbitoptions.Password == "" ||
qbitoptions.SyncTag == "" {
log.Fatal("missing qbittorrent parameters")
}
qcli, err := NewQBittorrentCli(&options.Qbittorent)
if rsyncoptions.Hostname == "" {
log.Fatal("missing rsync parameters")
}
qcli, err := NewQBittorrentCli(qbitoptions)
if err != nil {
log.Fatal(err)
}
@ -38,12 +56,20 @@ func main() {
}
for _, t := range torrents {
for p := 0; p <= 100; p += 20 {
rtask := NewRsync(
rsyncoptions.Uri(t.Path),
dest,
func(p int) {
qcli.SetProgress(t, p)
},
)
if err := rtask.Run(); err != nil {
qcli.ClearTags()
qcli.SetProgress(&t, p)
time.Sleep(time.Second)
log.Fatal(err)
}
qcli.SetDone(&t)
qcli.SetDone(t)
}
}

View File

@ -2,6 +2,7 @@ package main
import (
"fmt"
"log"
"strings"
"github.com/go-resty/resty/v2"
@ -60,8 +61,8 @@ func (c *QBittorrentCli) Logout() error {
return err
}
func (c *QBittorrentCli) List() ([]Torrent, error) {
result := make([]Torrent, 0)
func (c *QBittorrentCli) List() ([]*Torrent, error) {
result := make([]*Torrent, 0)
_, err := c.cli.R().
SetQueryParam("filter", "completed").
@ -152,8 +153,19 @@ func (c *QBittorrentCli) SetProgress(t *Torrent, p int) error {
return nil
}
err := c.ClearTags()
if err != nil {
return err
}
c.SetTag(t, fmt.Sprintf("Progress:%d%%", p))
if err != nil {
return err
}
t.Progress = p
return c.SetTag(t, fmt.Sprintf("Progress:%d%%", p))
log.Printf("Downloading [%s]: %d%%", t.Name, p)
return nil
}
func (c *QBittorrentCli) SetDone(t *Torrent) error {

89
rsync.go Normal file
View File

@ -0,0 +1,89 @@
package main
import (
"bufio"
"bytes"
"os/exec"
"regexp"
"strconv"
"sync"
)
type Rsync struct {
Source string
Destination string
OnProgress func(p int)
progress int
}
func NewRsync(source, destination string, onProgress func(p int)) *Rsync {
return &Rsync{
Source: source,
Destination: destination,
OnProgress: onProgress,
progress: -1,
}
}
func ScanCR(data []byte, atEOF bool) (advance int, token []byte, err error) {
if atEOF && len(data) == 0 {
return 0, nil, nil
}
if i := bytes.IndexByte(data, '\r'); i >= 0 {
// We have a full newline-terminated line.
return i + 1, data[0:i], nil
}
// If we're at EOF, we have a final, non-terminated line. Return it.
if atEOF {
return len(data), data, nil
}
// Request more data.
return 0, nil, nil
}
func (r *Rsync) Run() error {
cmd := exec.Command(
"rsync",
"--archive",
"--partial",
"--inplace",
"--no-inc-recursive",
"--info=progress2",
r.Source,
r.Destination,
)
out, err := cmd.StdoutPipe()
if err != nil {
return err
}
defer out.Close()
wg := &sync.WaitGroup{}
wg.Add(1)
go func() {
defer wg.Done()
scanner := bufio.NewScanner(out)
scanner.Split(ScanCR)
progressMatch := regexp.MustCompile(`(\d+)%`)
for scanner.Scan() {
progress := scanner.Text()
if progressMatch.MatchString(progress) {
m := progressMatch.FindStringSubmatch(progress)
if p, err := strconv.Atoi(m[1]); err == nil {
r.OnProgress(p)
}
}
}
}()
if err := cmd.Start(); err != nil {
return err
}
err = cmd.Wait()
if err != nil {
return err
}
wg.Wait()
return nil
}