mirror of
https://github.com/taigrr/wtf
synced 2025-01-18 04:03:14 -08:00
Vendoring dependencies
This commit is contained in:
14
vendor/github.com/hekmon/cunits/.gitignore
generated
vendored
Normal file
14
vendor/github.com/hekmon/cunits/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
# Test binary, build with `go test -c`
|
||||
*.test
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
|
||||
# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736
|
||||
.glide/
|
||||
21
vendor/github.com/hekmon/cunits/LICENSE
generated
vendored
Normal file
21
vendor/github.com/hekmon/cunits/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018 Edouard Hur
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
60
vendor/github.com/hekmon/cunits/README.md
generated
vendored
Normal file
60
vendor/github.com/hekmon/cunits/README.md
generated
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
# ComputerUnits
|
||||
|
||||
[](https://godoc.org/github.com/hekmon/cunits)
|
||||
|
||||
ComputerUnits allows to manipulate binary and decimal representations of bits and bytes.
|
||||
|
||||
## Constants example
|
||||
|
||||
```golang
|
||||
fmt.Println(cunits.Kbit)
|
||||
fmt.Println(cunits.Kibit)
|
||||
fmt.Println(cunits.KB)
|
||||
fmt.Println(cunits.KiB)
|
||||
fmt.Printf("1000 MiB = %f MB\n", float64(1000)*cunits.MiB/cunits.MB)
|
||||
```
|
||||
|
||||
will output:
|
||||
|
||||
```
|
||||
1000
|
||||
1024
|
||||
8000
|
||||
8192
|
||||
1000 MiB = 1048.576000 MB
|
||||
```
|
||||
|
||||
## Custom type example
|
||||
|
||||
```golang
|
||||
size := cunits.Bit(58) * cunits.MiB
|
||||
fmt.Println(size.Mbit())
|
||||
fmt.Println(size.GiBString())
|
||||
```
|
||||
|
||||
will output:
|
||||
|
||||
```
|
||||
486.539264
|
||||
0.06 GiB
|
||||
```
|
||||
|
||||
## Parsing example
|
||||
|
||||
```golang
|
||||
size, err := cunits.Parse("7632 MiB")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(size)
|
||||
fmt.Println(size.KiB())
|
||||
fmt.Println(size.KbitString())
|
||||
```
|
||||
|
||||
will output:
|
||||
|
||||
```
|
||||
7.45 GiB
|
||||
7.815168e+06
|
||||
64021856.26 Kbit
|
||||
```
|
||||
424
vendor/github.com/hekmon/cunits/bits.go
generated
vendored
Normal file
424
vendor/github.com/hekmon/cunits/bits.go
generated
vendored
Normal file
@@ -0,0 +1,424 @@
|
||||
package cunits
|
||||
|
||||
import "fmt"
|
||||
|
||||
// Bits represent a size in bits
|
||||
type Bits uint64
|
||||
|
||||
// String allows direct reprensetation of Bit by calling GetHumanSizeRepresentation()
|
||||
func (size Bits) String() string {
|
||||
return size.GetHumanSizeRepresentation()
|
||||
}
|
||||
|
||||
// GetHumanSizeRepresentation returns the size in a human readable binary prefix of bytes format
|
||||
func (size Bits) GetHumanSizeRepresentation() string {
|
||||
// if size >= YiB {
|
||||
// return size.YiBString()
|
||||
// }
|
||||
// if size >= ZiB {
|
||||
// return size.ZiBString()
|
||||
// }
|
||||
if size >= EiB {
|
||||
return size.EiBString()
|
||||
}
|
||||
if size >= PiB {
|
||||
return size.PiBString()
|
||||
}
|
||||
if size >= TiB {
|
||||
return size.TiBString()
|
||||
}
|
||||
if size >= GiB {
|
||||
return size.GiBString()
|
||||
}
|
||||
if size >= MiB {
|
||||
return size.MiBString()
|
||||
}
|
||||
if size >= KiB {
|
||||
return size.KiBString()
|
||||
}
|
||||
return size.ByteString()
|
||||
}
|
||||
|
||||
// GetHumanSpeedRepresentation returns the size in a human readable decimal prefix of bits format
|
||||
func (size Bits) GetHumanSpeedRepresentation() string {
|
||||
// if size >= Ybit {
|
||||
// return size.YbitString()
|
||||
// }
|
||||
// if size >= Zbit {
|
||||
// return size.ZbitString()
|
||||
// }
|
||||
if size >= Ebit {
|
||||
return size.EbitString()
|
||||
}
|
||||
if size >= Pbit {
|
||||
return size.PbitString()
|
||||
}
|
||||
if size >= Tbit {
|
||||
return size.TbitString()
|
||||
}
|
||||
if size >= Gbit {
|
||||
return size.GbitString()
|
||||
}
|
||||
if size >= Mbit {
|
||||
return size.MbitString()
|
||||
}
|
||||
if size >= Kbit {
|
||||
return size.KbitString()
|
||||
}
|
||||
return size.BitString()
|
||||
}
|
||||
|
||||
/*
|
||||
Base forms
|
||||
*/
|
||||
|
||||
// BitString returns the size in bit with unit suffix
|
||||
func (size Bits) BitString() string {
|
||||
return fmt.Sprintf("%d b", size)
|
||||
}
|
||||
|
||||
// Byte returns the size in byte
|
||||
func (size Bits) Byte() float64 {
|
||||
return float64(size) / 8
|
||||
}
|
||||
|
||||
// ByteString returns the size in byte with unit suffix
|
||||
func (size Bits) ByteString() string {
|
||||
return fmt.Sprintf("%.2f B", size.Byte())
|
||||
}
|
||||
|
||||
/*
|
||||
Decimal prefix of Bit
|
||||
*/
|
||||
|
||||
// Kbit returns the size in kilobit
|
||||
func (size Bits) Kbit() float64 {
|
||||
return float64(size) / Kbit
|
||||
}
|
||||
|
||||
// KbitString returns the size in kilobit with unit suffix
|
||||
func (size Bits) KbitString() string {
|
||||
return fmt.Sprintf("%.2f Kb", size.Kbit())
|
||||
}
|
||||
|
||||
// Mbit returns the size in megabit
|
||||
func (size Bits) Mbit() float64 {
|
||||
return float64(size) / Mbit
|
||||
}
|
||||
|
||||
// MbitString returns the size in megabit with unit suffix
|
||||
func (size Bits) MbitString() string {
|
||||
return fmt.Sprintf("%.2f Mb", size.Mbit())
|
||||
}
|
||||
|
||||
// Gbit returns the size in gigabit
|
||||
func (size Bits) Gbit() float64 {
|
||||
return float64(size) / Gbit
|
||||
}
|
||||
|
||||
// GbitString returns the size in gigabit with unit suffix
|
||||
func (size Bits) GbitString() string {
|
||||
return fmt.Sprintf("%.2f Gb", size.Gbit())
|
||||
}
|
||||
|
||||
// Tbit returns the size in terabit
|
||||
func (size Bits) Tbit() float64 {
|
||||
return float64(size) / Tbit
|
||||
}
|
||||
|
||||
// TbitString returns the size in terabit with unit suffix
|
||||
func (size Bits) TbitString() string {
|
||||
return fmt.Sprintf("%.2f Tb", size.Tbit())
|
||||
}
|
||||
|
||||
// Pbit returns the size in petabit
|
||||
func (size Bits) Pbit() float64 {
|
||||
return float64(size) / Pbit
|
||||
}
|
||||
|
||||
// PbitString returns the size in petabit with unit suffix
|
||||
func (size Bits) PbitString() string {
|
||||
return fmt.Sprintf("%.2f Pb", size.Pbit())
|
||||
}
|
||||
|
||||
// Ebit returns the size in exabit
|
||||
func (size Bits) Ebit() float64 {
|
||||
return float64(size) / Ebit
|
||||
}
|
||||
|
||||
// EbitString returns the size in exabit with unit suffix
|
||||
func (size Bits) EbitString() string {
|
||||
return fmt.Sprintf("%.2f Eb", size.Ebit())
|
||||
}
|
||||
|
||||
// Zbit returns the size in zettabit
|
||||
func (size Bits) Zbit() float64 {
|
||||
return float64(size) / Zbit
|
||||
}
|
||||
|
||||
// ZbitString returns the size in zettabit with unit suffix (carefull with sub zeros !)
|
||||
func (size Bits) ZbitString() string {
|
||||
return fmt.Sprintf("%f Zb", size.Zbit())
|
||||
}
|
||||
|
||||
// Ybit returns the size in yottabit
|
||||
func (size Bits) Ybit() float64 {
|
||||
return float64(size) / Ybit
|
||||
}
|
||||
|
||||
// YbitString returns the size in yottabit with unit suffix (carefull with sub zeros !)
|
||||
func (size Bits) YbitString() string {
|
||||
return fmt.Sprintf("%f Yb", size.Ybit())
|
||||
}
|
||||
|
||||
/*
|
||||
Binary prefix of Bit
|
||||
*/
|
||||
|
||||
// Kibit returns the size in kibibit
|
||||
func (size Bits) Kibit() float64 {
|
||||
return float64(size) / Kibit
|
||||
}
|
||||
|
||||
// KibitString returns the size in kibibit with unit suffix
|
||||
func (size Bits) KibitString() string {
|
||||
return fmt.Sprintf("%.2f Kib", size.Kibit())
|
||||
}
|
||||
|
||||
// Mibit returns the size in mebibit
|
||||
func (size Bits) Mibit() float64 {
|
||||
return float64(size) / Mibit
|
||||
}
|
||||
|
||||
// MibitString returns the size in mebibit with unit suffix
|
||||
func (size Bits) MibitString() string {
|
||||
return fmt.Sprintf("%.2f Mib", size.Mibit())
|
||||
}
|
||||
|
||||
// Gibit returns the size in gibibit
|
||||
func (size Bits) Gibit() float64 {
|
||||
return float64(size) / Gibit
|
||||
}
|
||||
|
||||
// GibitString returns the size in gibibit with unit suffix
|
||||
func (size Bits) GibitString() string {
|
||||
return fmt.Sprintf("%.2f Gib", size.Gibit())
|
||||
}
|
||||
|
||||
// Tibit returns the size in tebibit
|
||||
func (size Bits) Tibit() float64 {
|
||||
return float64(size) / Tibit
|
||||
}
|
||||
|
||||
// TibitString returns the size in tebibit with unit suffix
|
||||
func (size Bits) TibitString() string {
|
||||
return fmt.Sprintf("%.2f Tib", size.Tibit())
|
||||
}
|
||||
|
||||
// Pibit returns the size in pebibit
|
||||
func (size Bits) Pibit() float64 {
|
||||
return float64(size) / Pibit
|
||||
}
|
||||
|
||||
// PibitString returns the size in pebibit with unit suffix
|
||||
func (size Bits) PibitString() string {
|
||||
return fmt.Sprintf("%.2f Pib", size.Pibit())
|
||||
}
|
||||
|
||||
// Eibit returns the size in exbibit
|
||||
func (size Bits) Eibit() float64 {
|
||||
return float64(size) / Eibit
|
||||
}
|
||||
|
||||
// EibitString returns the size in exbibit with unit suffix
|
||||
func (size Bits) EibitString() string {
|
||||
return fmt.Sprintf("%.2f Eib", size.Eibit())
|
||||
}
|
||||
|
||||
// Zibit returns the size in zebibit
|
||||
func (size Bits) Zibit() float64 {
|
||||
return float64(size) / Zibit
|
||||
}
|
||||
|
||||
// ZibitString returns the size in zebibit with unit suffix (carefull with sub zeros !)
|
||||
func (size Bits) ZibitString() string {
|
||||
return fmt.Sprintf("%f Zib", size.Zibit())
|
||||
}
|
||||
|
||||
// Yibit returns the size in yobibit
|
||||
func (size Bits) Yibit() float64 {
|
||||
return float64(size) / Yibit
|
||||
}
|
||||
|
||||
// YibitString returns the size in yobibit with unit suffix (carefull with sub zeros !)
|
||||
func (size Bits) YibitString() string {
|
||||
return fmt.Sprintf("%f Yib", size.Yibit())
|
||||
}
|
||||
|
||||
/*
|
||||
Decimal prefix of bytes
|
||||
*/
|
||||
|
||||
// KB returns the size in kilobyte
|
||||
func (size Bits) KB() float64 {
|
||||
return float64(size) / KB
|
||||
}
|
||||
|
||||
// KBString returns the size in kilobyte with unit suffix
|
||||
func (size Bits) KBString() string {
|
||||
return fmt.Sprintf("%.2f KB", size.KB())
|
||||
}
|
||||
|
||||
// MB returns the size in megabyte
|
||||
func (size Bits) MB() float64 {
|
||||
return float64(size) / MB
|
||||
}
|
||||
|
||||
// MBString returns the size in megabyte with unit suffix
|
||||
func (size Bits) MBString() string {
|
||||
return fmt.Sprintf("%.2f MB", size.MB())
|
||||
}
|
||||
|
||||
// GB returns the size in gigabyte
|
||||
func (size Bits) GB() float64 {
|
||||
return float64(size) / GB
|
||||
}
|
||||
|
||||
// GBString returns the size in gigabyte with unit suffix
|
||||
func (size Bits) GBString() string {
|
||||
return fmt.Sprintf("%.2f GB", size.GB())
|
||||
}
|
||||
|
||||
// TB returns the size in terabyte
|
||||
func (size Bits) TB() float64 {
|
||||
return float64(size) / TB
|
||||
}
|
||||
|
||||
// TBString returns the size in terabyte with unit suffix
|
||||
func (size Bits) TBString() string {
|
||||
return fmt.Sprintf("%.2f TB", size.TB())
|
||||
}
|
||||
|
||||
// PB returns the size in petabyte
|
||||
func (size Bits) PB() float64 {
|
||||
return float64(size) / PB
|
||||
}
|
||||
|
||||
// PBString returns the size in petabyte with unit suffix
|
||||
func (size Bits) PBString() string {
|
||||
return fmt.Sprintf("%.2f PB", size.PB())
|
||||
}
|
||||
|
||||
// EB returns the size in exabyte
|
||||
func (size Bits) EB() float64 {
|
||||
return float64(size) / EB
|
||||
}
|
||||
|
||||
// EBString returns the size in exabyte with unit suffix
|
||||
func (size Bits) EBString() string {
|
||||
return fmt.Sprintf("%.2f EB", size.EB())
|
||||
}
|
||||
|
||||
// ZB returns the size in zettabyte
|
||||
func (size Bits) ZB() float64 {
|
||||
return float64(size) / ZB
|
||||
}
|
||||
|
||||
// ZBString returns the size in zettabyte with unit suffix (carefull with sub zeros !)
|
||||
func (size Bits) ZBString() string {
|
||||
return fmt.Sprintf("%f ZB", size.ZB())
|
||||
}
|
||||
|
||||
// YB returns the size in yottabyte
|
||||
func (size Bits) YB() float64 {
|
||||
return float64(size) / YB
|
||||
}
|
||||
|
||||
// YBString returns the size in yottabyte with unit suffix (carefull with sub zeros !)
|
||||
func (size Bits) YBString() string {
|
||||
return fmt.Sprintf("%f YB", size.YB())
|
||||
}
|
||||
|
||||
/*
|
||||
Binary prefix of bytes
|
||||
*/
|
||||
|
||||
// KiB returns the size in kibibyte
|
||||
func (size Bits) KiB() float64 {
|
||||
return float64(size) / KiB
|
||||
}
|
||||
|
||||
// KiBString returns the size in kibibyte with unit suffix
|
||||
func (size Bits) KiBString() string {
|
||||
return fmt.Sprintf("%.2f KiB", size.KiB())
|
||||
}
|
||||
|
||||
// MiB returns the size in mebibyte
|
||||
func (size Bits) MiB() float64 {
|
||||
return float64(size) / MiB
|
||||
}
|
||||
|
||||
// MiBString returns the size in mebibyte with unit suffix
|
||||
func (size Bits) MiBString() string {
|
||||
return fmt.Sprintf("%.2f MiB", size.MiB())
|
||||
}
|
||||
|
||||
// GiB returns the size in gibibyte
|
||||
func (size Bits) GiB() float64 {
|
||||
return float64(size) / GiB
|
||||
}
|
||||
|
||||
// GiBString returns the size in gibibyte with unit suffix
|
||||
func (size Bits) GiBString() string {
|
||||
return fmt.Sprintf("%.2f GiB", size.GiB())
|
||||
}
|
||||
|
||||
// TiB returns the size in tebibyte
|
||||
func (size Bits) TiB() float64 {
|
||||
return float64(size) / TiB
|
||||
}
|
||||
|
||||
// TiBString returns the size in tebibyte wit unit suffix
|
||||
func (size Bits) TiBString() string {
|
||||
return fmt.Sprintf("%.2f TiB", size.TiB())
|
||||
}
|
||||
|
||||
// PiB returns the size in pebibyte
|
||||
func (size Bits) PiB() float64 {
|
||||
return float64(size) / PiB
|
||||
}
|
||||
|
||||
// PiBString returns the size in pebibyte with unit suffix
|
||||
func (size Bits) PiBString() string {
|
||||
return fmt.Sprintf("%.2f PiB", size.PiB())
|
||||
}
|
||||
|
||||
// EiB returns the size in exbibyte
|
||||
func (size Bits) EiB() float64 {
|
||||
return float64(size) / EiB
|
||||
}
|
||||
|
||||
// EiBString returns the size in exbibyte with unit suffix (carefull with sub zeros !)
|
||||
func (size Bits) EiBString() string {
|
||||
return fmt.Sprintf("%f EiB", size.EiB())
|
||||
}
|
||||
|
||||
// ZiB returns the size in zebibyte
|
||||
func (size Bits) ZiB() float64 {
|
||||
return float64(size) / ZiB
|
||||
}
|
||||
|
||||
// ZiBString returns the size in zebibyte with unit suffix (carefull with sub zeros !)
|
||||
func (size Bits) ZiBString() string {
|
||||
return fmt.Sprintf("%f ZiB", size.ZiB())
|
||||
}
|
||||
|
||||
// YiB returns the size in yobibyte
|
||||
func (size Bits) YiB() float64 {
|
||||
return float64(size) / YiB
|
||||
}
|
||||
|
||||
// YiBString returns the size in yobibyte with unit suffix (carefull with sub zeros !)
|
||||
func (size Bits) YiBString() string {
|
||||
return fmt.Sprintf("%f YiB", size.YiB())
|
||||
}
|
||||
84
vendor/github.com/hekmon/cunits/consts.go
generated
vendored
Normal file
84
vendor/github.com/hekmon/cunits/consts.go
generated
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
package cunits
|
||||
|
||||
// Byte represent a byte
|
||||
const Byte = 8
|
||||
|
||||
// Decimal prefix of bits
|
||||
const (
|
||||
// Kbit represents a kilobit
|
||||
Kbit = 1000
|
||||
// Mbit represents a megabit
|
||||
Mbit = 1000000
|
||||
// Gbit represents a gigabit
|
||||
Gbit = 1000000000
|
||||
// Tbit represents a terabit
|
||||
Tbit = 1000000000000
|
||||
// Pbit represents a petabit
|
||||
Pbit = 1000000000000000
|
||||
// Ebit represents an exabit
|
||||
Ebit = 1000000000000000000
|
||||
// Zbit represents a zettabit (overflows int64)
|
||||
Zbit = 1000000000000000000000
|
||||
// Ybit represents a yottabit (overflows int64)
|
||||
Ybit = 1000000000000000000000000
|
||||
)
|
||||
|
||||
// Binary prefix of bits
|
||||
const (
|
||||
// Kibit represents a kibibit
|
||||
Kibit = 1 << 10
|
||||
// Mibit represents a mebibit
|
||||
Mibit = 1 << 20
|
||||
// Gibit represents a gibibit
|
||||
Gibit = 1 << 30
|
||||
// Tibit represents a tebibit
|
||||
Tibit = 1 << 40
|
||||
// Pibit represents a pebibit
|
||||
Pibit = 1 << 50
|
||||
// Eibit represents an exbibit
|
||||
Eibit = 1 << 60
|
||||
// Zibit represents a zebibit (overflows int64)
|
||||
Zibit = 1 << 70
|
||||
// Yibit represents a yobibit (overflows int64)
|
||||
Yibit = 1 << 80
|
||||
)
|
||||
|
||||
// Decimal prefix of bytes
|
||||
const (
|
||||
// KB represents a kilobyte
|
||||
KB = Kbit * Byte
|
||||
// MB represents a megabyte
|
||||
MB = Mbit * Byte
|
||||
// GB represents a gigabyte
|
||||
GB = Gbit * Byte
|
||||
// TB represents a terabyte
|
||||
TB = Tbit * Byte
|
||||
// PB represents a petabyte
|
||||
PB = Pbit * Byte
|
||||
// EB represents an exabyte
|
||||
EB = Ebit * Byte
|
||||
// ZB represents a zettabyte (overflows int64)
|
||||
ZB = Zbit * Byte
|
||||
// YB represents a yottabyte (overflows int64)
|
||||
YB = Ybit * Byte
|
||||
)
|
||||
|
||||
// Binary prefix of bytes
|
||||
const (
|
||||
// KiB represents a kibibyte
|
||||
KiB = Kibit * Byte
|
||||
// MiB represents a mebibyte
|
||||
MiB = Mibit * Byte
|
||||
// GiB represents a gibibyte
|
||||
GiB = Gibit * Byte
|
||||
// TiB represents a tebibyte
|
||||
TiB = Tibit * Byte
|
||||
// PiB represents a pebibyte
|
||||
PiB = Pibit * Byte
|
||||
// EiB represents an exbibyte (overflows int64)
|
||||
EiB = Eibit * Byte
|
||||
// ZiB represents a zebibyte (overflows int64)
|
||||
ZiB = Zbit * Byte
|
||||
// YiB represents a yobibyte (overflows int64)
|
||||
YiB = Ybit * Byte
|
||||
)
|
||||
292
vendor/github.com/hekmon/cunits/import.go
generated
vendored
Normal file
292
vendor/github.com/hekmon/cunits/import.go
generated
vendored
Normal file
@@ -0,0 +1,292 @@
|
||||
package cunits
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const parseRegex = "^([0-9,]+(\\.[0-9]+)?) ?(([KMGTPEZY]i?)?(B|bit))$"
|
||||
|
||||
var sizeMatch = regexp.MustCompile(parseRegex)
|
||||
|
||||
// Parse parses un string representation of a number with a suffix
|
||||
func Parse(sizeSuffix string) (size Bits, err error) {
|
||||
// Does it match ?
|
||||
match := sizeMatch.FindSubmatch([]byte(sizeSuffix))
|
||||
if match == nil {
|
||||
err = fmt.Errorf("string does not match the parsing regex: %s", parseRegex)
|
||||
return
|
||||
}
|
||||
if len(match) < 4 {
|
||||
err = fmt.Errorf("regex matching did not return enough groups")
|
||||
return
|
||||
}
|
||||
// Extract number
|
||||
num, err := strconv.ParseFloat(strings.Replace(string(match[1]), ",", "", -1), 64)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("extracted number '%s' can't be parsed as float64: %v", string(match[1]), err)
|
||||
return
|
||||
}
|
||||
// Findout the unit
|
||||
switch string(match[3]) {
|
||||
case "bit":
|
||||
size = Bits(num)
|
||||
case "B":
|
||||
size = ImportInByte(num)
|
||||
// Decimal prefix of bits
|
||||
case "Kbit":
|
||||
size = ImportInKbit(num)
|
||||
case "Mbit":
|
||||
size = ImportInMbit(num)
|
||||
case "Gbit":
|
||||
size = ImportInGbit(num)
|
||||
case "Tbit":
|
||||
size = ImportInTbit(num)
|
||||
case "Pbit":
|
||||
size = ImportInPbit(num)
|
||||
case "Ebit":
|
||||
size = ImportInEbit(num)
|
||||
case "Zbit":
|
||||
size = ImportInZbit(num)
|
||||
case "Ybit":
|
||||
size = ImportInYbit(num)
|
||||
// Binary prefix of bits
|
||||
case "Kibit":
|
||||
size = ImportInKibit(num)
|
||||
case "Mibit":
|
||||
size = ImportInMibit(num)
|
||||
case "Gibit":
|
||||
size = ImportInGibit(num)
|
||||
case "Tibit":
|
||||
size = ImportInTibit(num)
|
||||
case "Pibit":
|
||||
size = ImportInPibit(num)
|
||||
case "Eibit":
|
||||
size = ImportInEibit(num)
|
||||
case "Zibit":
|
||||
size = ImportInZibit(num)
|
||||
case "Yibit":
|
||||
size = ImportInYibit(num)
|
||||
// Decimal prefix of bytes
|
||||
case "KB":
|
||||
size = ImportInKB(num)
|
||||
case "MB":
|
||||
size = ImportInMB(num)
|
||||
case "GB":
|
||||
size = ImportInGB(num)
|
||||
case "TB":
|
||||
size = ImportInTB(num)
|
||||
case "PB":
|
||||
size = ImportInPB(num)
|
||||
case "EB":
|
||||
size = ImportInEB(num)
|
||||
case "ZB":
|
||||
size = ImportInZB(num)
|
||||
case "YB":
|
||||
size = ImportInYB(num)
|
||||
// Binary prefix of bytes
|
||||
case "KiB":
|
||||
size = ImportInKiB(num)
|
||||
case "MiB":
|
||||
size = ImportInMiB(num)
|
||||
case "GiB":
|
||||
size = ImportInGiB(num)
|
||||
case "TiB":
|
||||
size = ImportInTiB(num)
|
||||
case "PiB":
|
||||
size = ImportInPiB(num)
|
||||
case "EiB":
|
||||
size = ImportInEiB(num)
|
||||
case "ZiB":
|
||||
size = ImportInZiB(num)
|
||||
case "YiB":
|
||||
size = ImportInYiB(num)
|
||||
// or not
|
||||
default:
|
||||
err = fmt.Errorf("extracted unit '%s' is unknown", string(match[3]))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ImportInByte imports a number in byte
|
||||
func ImportInByte(sizeInByte float64) Bits {
|
||||
return Bits(sizeInByte * Byte)
|
||||
}
|
||||
|
||||
/*
|
||||
Decimal prefix of bits
|
||||
*/
|
||||
|
||||
// ImportInKbit imports a number in kilobit
|
||||
func ImportInKbit(sizeInKbit float64) Bits {
|
||||
return Bits(sizeInKbit * Kbit)
|
||||
}
|
||||
|
||||
// ImportInMbit imports a number in megabit
|
||||
func ImportInMbit(sizeInMbit float64) Bits {
|
||||
return Bits(sizeInMbit * Mbit)
|
||||
}
|
||||
|
||||
// ImportInGbit imports a number in gigabit
|
||||
func ImportInGbit(sizeInGbit float64) Bits {
|
||||
return Bits(sizeInGbit * Gbit)
|
||||
}
|
||||
|
||||
// ImportInTbit imports a number in terabit
|
||||
func ImportInTbit(sizeInTbit float64) Bits {
|
||||
return Bits(sizeInTbit * Tbit)
|
||||
}
|
||||
|
||||
// ImportInPbit imports a number in petabit
|
||||
func ImportInPbit(sizeInPbit float64) Bits {
|
||||
return Bits(sizeInPbit * Pbit)
|
||||
}
|
||||
|
||||
// ImportInEbit imports a number in exabit
|
||||
func ImportInEbit(sizeInEbit float64) Bits {
|
||||
return Bits(sizeInEbit * Ebit)
|
||||
}
|
||||
|
||||
// ImportInZbit imports a number in zettabit (sizeInZbit better < 1)
|
||||
func ImportInZbit(sizeInZbit float64) Bits {
|
||||
return Bits(sizeInZbit * Zbit)
|
||||
}
|
||||
|
||||
// ImportInYbit imports a number in yottabit (sizeInYbit better < 1)
|
||||
func ImportInYbit(sizeInYbit float64) Bits {
|
||||
return Bits(sizeInYbit * Ybit)
|
||||
}
|
||||
|
||||
/*
|
||||
Binary prefix of bits
|
||||
*/
|
||||
|
||||
// ImportInKibit imports a number in kibibit
|
||||
func ImportInKibit(sizeInKibit float64) Bits {
|
||||
return Bits(sizeInKibit * Kibit)
|
||||
}
|
||||
|
||||
// ImportInMibit imports a number in mebibit
|
||||
func ImportInMibit(sizeInMibit float64) Bits {
|
||||
return Bits(sizeInMibit * Mibit)
|
||||
}
|
||||
|
||||
// ImportInGibit imports a number in gibibit
|
||||
func ImportInGibit(sizeInGibit float64) Bits {
|
||||
return Bits(sizeInGibit * Gibit)
|
||||
}
|
||||
|
||||
// ImportInTibit imports a number in tebibit
|
||||
func ImportInTibit(sizeInTibit float64) Bits {
|
||||
return Bits(sizeInTibit * Tibit)
|
||||
}
|
||||
|
||||
// ImportInPibit imports a number in pebibit
|
||||
func ImportInPibit(sizeInPibit float64) Bits {
|
||||
return Bits(sizeInPibit * Pibit)
|
||||
}
|
||||
|
||||
// ImportInEibit imports a number in exbibit
|
||||
func ImportInEibit(sizeInEibit float64) Bits {
|
||||
return Bits(sizeInEibit * Eibit)
|
||||
}
|
||||
|
||||
// ImportInZibit imports a number in zebibit (sizeInZibit better < 1)
|
||||
func ImportInZibit(sizeInZibit float64) Bits {
|
||||
return Bits(sizeInZibit * Zibit)
|
||||
}
|
||||
|
||||
// ImportInYibit imports a number in yobibit (sizeInYibit better < 1)
|
||||
func ImportInYibit(sizeInYibit float64) Bits {
|
||||
return Bits(sizeInYibit * Yibit)
|
||||
}
|
||||
|
||||
/*
|
||||
Decimal prefix of bytes
|
||||
*/
|
||||
|
||||
// ImportInKB imports a number in kilobyte
|
||||
func ImportInKB(sizeInKB float64) Bits {
|
||||
return Bits(sizeInKB * KB)
|
||||
}
|
||||
|
||||
// ImportInMB imports a number in megabyte
|
||||
func ImportInMB(sizeInMB float64) Bits {
|
||||
return Bits(sizeInMB * MB)
|
||||
}
|
||||
|
||||
// ImportInGB imports a number in gigabyte
|
||||
func ImportInGB(sizeInGB float64) Bits {
|
||||
return Bits(sizeInGB * GB)
|
||||
}
|
||||
|
||||
// ImportInTB imports a number in terabyte
|
||||
func ImportInTB(sizeInTB float64) Bits {
|
||||
return Bits(sizeInTB * TB)
|
||||
}
|
||||
|
||||
// ImportInPB imports a number in petabyte
|
||||
func ImportInPB(sizeInPB float64) Bits {
|
||||
return Bits(sizeInPB * PB)
|
||||
}
|
||||
|
||||
// ImportInEB imports a number in exabyte
|
||||
func ImportInEB(sizeInEB float64) Bits {
|
||||
return Bits(sizeInEB * EB)
|
||||
}
|
||||
|
||||
// ImportInZB imports a number in zettabyte (sizeInZB better < 1)
|
||||
func ImportInZB(sizeInZB float64) Bits {
|
||||
return Bits(sizeInZB * ZB)
|
||||
}
|
||||
|
||||
// ImportInYB imports a number in yottabyte (sizeInYB better < 1)
|
||||
func ImportInYB(sizeInYB float64) Bits {
|
||||
return Bits(sizeInYB * YB)
|
||||
}
|
||||
|
||||
/*
|
||||
Binary prefix of bytes
|
||||
*/
|
||||
|
||||
// ImportInKiB imports a number in kilobyte
|
||||
func ImportInKiB(sizeInKiB float64) Bits {
|
||||
return Bits(sizeInKiB * KiB)
|
||||
}
|
||||
|
||||
// ImportInMiB imports a number in megabyte
|
||||
func ImportInMiB(sizeInMiB float64) Bits {
|
||||
return Bits(sizeInMiB * MiB)
|
||||
}
|
||||
|
||||
// ImportInGiB imports a number in gigabyte
|
||||
func ImportInGiB(sizeInGiB float64) Bits {
|
||||
return Bits(sizeInGiB * GiB)
|
||||
}
|
||||
|
||||
// ImportInTiB imports a number in terabyte
|
||||
func ImportInTiB(sizeInTiB float64) Bits {
|
||||
return Bits(sizeInTiB * TiB)
|
||||
}
|
||||
|
||||
// ImportInPiB imports a number in petabyte
|
||||
func ImportInPiB(sizeInPiB float64) Bits {
|
||||
return Bits(sizeInPiB * PiB)
|
||||
}
|
||||
|
||||
// ImportInEiB imports a number in exabyte (sizeInEiB better < 1)
|
||||
func ImportInEiB(sizeInEiB float64) Bits {
|
||||
return Bits(sizeInEiB * EiB)
|
||||
}
|
||||
|
||||
// ImportInZiB imports a number in zettabyte (sizeInZiB better < 1)
|
||||
func ImportInZiB(sizeInZiB float64) Bits {
|
||||
return Bits(sizeInZiB * ZiB)
|
||||
}
|
||||
|
||||
// ImportInYiB imports a number in yottabyte (sizeInYiB better < 1)
|
||||
func ImportInYiB(sizeInYiB float64) Bits {
|
||||
return Bits(sizeInYiB * YiB)
|
||||
}
|
||||
22
vendor/github.com/hekmon/transmissionrpc/.gitignore
generated
vendored
Normal file
22
vendor/github.com/hekmon/transmissionrpc/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
# VSCode local settings
|
||||
.vscode
|
||||
|
||||
# Vendoring
|
||||
vendor/
|
||||
|
||||
## Github ignore
|
||||
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
# Test binary, build with `go test -c`
|
||||
*.test
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
|
||||
# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736
|
||||
.glide/
|
||||
28
vendor/github.com/hekmon/transmissionrpc/Gopkg.lock
generated
vendored
Normal file
28
vendor/github.com/hekmon/transmissionrpc/Gopkg.lock
generated
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
||||
|
||||
|
||||
[[projects]]
|
||||
digest = "1:af105c7c5dc0b4ae41991f122cae860b9600f7d226072c2a83127048c991660c"
|
||||
name = "github.com/hashicorp/go-cleanhttp"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "eda1e5db218aad1db63ca4642c8906b26bcf2744"
|
||||
version = "v0.5.1"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:9715d8b13bda6dada8327ad7d4791ec8d4c2be49a660c8a9293361f80daac913"
|
||||
name = "github.com/hekmon/cunits"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "3c45cb0124f153c1e8bbd2e965cf657c59331d49"
|
||||
version = "v2.0.0"
|
||||
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
input-imports = [
|
||||
"github.com/hashicorp/go-cleanhttp",
|
||||
"github.com/hekmon/cunits",
|
||||
]
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
||||
38
vendor/github.com/hekmon/transmissionrpc/Gopkg.toml
generated
vendored
Normal file
38
vendor/github.com/hekmon/transmissionrpc/Gopkg.toml
generated
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
# Gopkg.toml example
|
||||
#
|
||||
# Refer to https://golang.github.io/dep/docs/Gopkg.toml.html
|
||||
# for detailed Gopkg.toml documentation.
|
||||
#
|
||||
# required = ["github.com/user/thing/cmd/thing"]
|
||||
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
|
||||
#
|
||||
# [[constraint]]
|
||||
# name = "github.com/user/project"
|
||||
# version = "1.0.0"
|
||||
#
|
||||
# [[constraint]]
|
||||
# name = "github.com/user/project2"
|
||||
# branch = "dev"
|
||||
# source = "github.com/myfork/project2"
|
||||
#
|
||||
# [[override]]
|
||||
# name = "github.com/x/y"
|
||||
# version = "2.4.0"
|
||||
#
|
||||
# [prune]
|
||||
# non-go = false
|
||||
# go-tests = true
|
||||
# unused-packages = true
|
||||
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/hekmon/cunits"
|
||||
version = "2.0.0"
|
||||
|
||||
[prune]
|
||||
go-tests = true
|
||||
unused-packages = true
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/hashicorp/go-cleanhttp"
|
||||
version = "0.5.1"
|
||||
21
vendor/github.com/hekmon/transmissionrpc/LICENSE
generated
vendored
Normal file
21
vendor/github.com/hekmon/transmissionrpc/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018 Edouard Hur
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
387
vendor/github.com/hekmon/transmissionrpc/README.md
generated
vendored
Normal file
387
vendor/github.com/hekmon/transmissionrpc/README.md
generated
vendored
Normal file
@@ -0,0 +1,387 @@
|
||||
# TransmissionRPC
|
||||
|
||||
[](https://godoc.org/github.com/hekmon/transmissionrpc) [](https://goreportcard.com/report/github.com/hekmon/transmissionrpc)
|
||||
|
||||
Golang bindings to Transmission (bittorent) RPC interface (Work in Progress).
|
||||
|
||||
Even if there is some high level wrappers/helpers, the goal of this lib is to stay close to the original API in terms of methods and payloads while enhancing certain types to be more "golangish": timestamps are converted from/to time.Time, numeric durations in time.Duration, booleans in numeric form are converted to real bool, etc...
|
||||
|
||||
Also payload generation aims to be precise: when several values can be added to a payload, only instanciated values will be forwarded (and kept !) to the final payload. This means that the default JSON marshalling (with omitempty) can't always be used and therefor a manual, reflect based, approach is used to build the final payload and accurately send what the user have instanciated, even if a value is at its default type value.
|
||||
|
||||
This lib follow the [transmission v15 RPC specification](https://github.com/transmission/transmission/blob/2.9x/extras/rpc-spec.txt#L639).
|
||||
|
||||
## Getting started
|
||||
|
||||
First the main client object must be instantiated with [New()](https://godoc.org/github.com/hekmon/transmissionrpc#New). In its basic form only host/ip, username and password must be provided. Default will apply for port (`9091`) rpc URI (`/transmission/rpc`) and others values.
|
||||
|
||||
```golang
|
||||
transmissionbt := transmissionrpc.New("127.0.0.1", "rpcuser", "rpcpass", nil)
|
||||
```
|
||||
|
||||
But advanced values can also be configured to your liking using [AdvancedConfig](https://godoc.org/github.com/hekmon/transmissionrpc#AdvancedConfig).
|
||||
Each value of `AdvancedConfig` with a type default value will be replaced by the lib default value, so you can set only the ones you want:
|
||||
|
||||
```golang
|
||||
transmissionbt := transmissionrpc.New("bt.mydomain.net", "rpcuser", "rpcpass",
|
||||
&transmissionrpc.AdvancedConfig{
|
||||
HTTPS: true,
|
||||
Port: 443,
|
||||
})
|
||||
```
|
||||
|
||||
The remote RPC version can be checked against this library before starting to operate:
|
||||
|
||||
```golang
|
||||
ok, serverVersion, serverMinimumVersion, err := transmission.RPCVersion()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("Remote transmission RPC version (v%d) is incompatible with the transmission library (v%d): remote needs at least v%d",
|
||||
serverVersion, transmissionrpc.RPCVersion, serverMinimumVersion))
|
||||
}
|
||||
fmt.Printf("Remote transmission RPC version (v%d) is compatible with our transmissionrpc library (v%d)\n",
|
||||
serverVersion, transmissionrpc.RPCVersion)
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
* [Torrent Requests](#torrent-requests)
|
||||
* [Torrent Action Requests](#torrent-action-requests)
|
||||
* [x] torrent-start
|
||||
* [x] torrent-start-now
|
||||
* [x] torrent-stop
|
||||
* [x] torrent-verify
|
||||
* [x] torrent-reannounce
|
||||
* [Torrent Mutators](#torrent-mutators)
|
||||
* [x] torrent-set
|
||||
* [Torrent Accessors](#torrent-accessors)
|
||||
* [x] torrent-get
|
||||
* [Adding a Torrent](#adding-a-torrent)
|
||||
* [x] torrent-add
|
||||
* [Removing a Torrent](#removing-a-torrent)
|
||||
* [x] torrent-remove
|
||||
* [Moving a Torrent](#moving-a-torrent)
|
||||
* [x] torrent-set-location
|
||||
* [Renaming a Torrent path](#renaming-a-torrent-path)
|
||||
* [x] torrent-rename-path
|
||||
* [Session Requests](#session-requests)
|
||||
* [Session Arguments](#session-arguments)
|
||||
* [x] session-set
|
||||
* [x] session-get
|
||||
* [Session Statistics](#session-statistics)
|
||||
* [x] session-stats
|
||||
* [Blocklist](#blocklist)
|
||||
* [ ] blocklist-update
|
||||
* [Port Checking](#port-checking)
|
||||
* [x] port-test
|
||||
* [Session Shutdown](#session-shutdown)
|
||||
* [ ] session-close
|
||||
* [Queue Movement Requests](#queue-movement-requests)
|
||||
* [ ] queue-move-top
|
||||
* [ ] queue-move-up
|
||||
* [ ] queue-move-down
|
||||
* [ ] queue-move-bottom
|
||||
* [Free Space](#free-space)
|
||||
* [x] free-space
|
||||
|
||||
### Torrent Requests
|
||||
|
||||
#### Torrent Action Requests
|
||||
|
||||
Each rpc methods here can work with ID list, hash list or `recently-active` magic word. Therefor, there is 3 golang method variants for each of them.
|
||||
|
||||
```golang
|
||||
transmissionbt.TorrentXXXXIDs(...)
|
||||
transmissionbt.TorrentXXXXHashes(...)
|
||||
transmissionbt.TorrentXXXXRecentlyActive()
|
||||
```
|
||||
|
||||
* torrent-start
|
||||
|
||||
Check [TorrentStartIDs()](https://godoc.org/github.com/hekmon/transmissionrpc#Client.TorrentStartIDs), [TorrentStartHashes()](https://godoc.org/github.com/hekmon/transmissionrpc#Client.TorrentStartHashes) and [TorrentStartRecentlyActive()](https://godoc.org/github.com/hekmon/transmissionrpc#Client.TorrentStartRecentlyActive).
|
||||
|
||||
Ex:
|
||||
|
||||
```golang
|
||||
err := transmissionbt.TorrentStartIDs([]int64{55})
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
} else {
|
||||
fmt.Println("yay")
|
||||
}
|
||||
```
|
||||
|
||||
* torrent-start-now
|
||||
|
||||
Check [TorrentStartNowIDs()](https://godoc.org/github.com/hekmon/transmissionrpc#Client.TorrentStartNowIDs), [TorrentStartNowHashes()](https://godoc.org/github.com/hekmon/transmissionrpc#Client.TorrentStartNowHashes) and [TorrentStartNowRecentlyActive()](https://godoc.org/github.com/hekmon/transmissionrpc#Client.TorrentStartNowRecentlyActive).
|
||||
|
||||
Ex:
|
||||
|
||||
```golang
|
||||
err := transmissionbt.TorrentStartNowHashes([]string{"f07e0b0584745b7bcb35e98097488d34e68623d0"})
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
} else {
|
||||
fmt.Println("yay")
|
||||
}
|
||||
```
|
||||
|
||||
* torrent-stop
|
||||
|
||||
Check [TorrentStopIDs()](https://godoc.org/github.com/hekmon/transmissionrpc#Client.TorrentStopIDs), [TorrentStopHashes()](https://godoc.org/github.com/hekmon/transmissionrpc#Client.TorrentStopHashes) and [TorrentStopRecentlyActive()](https://godoc.org/github.com/hekmon/transmissionrpc#Client.TorrentStopRecentlyActive).
|
||||
|
||||
Ex:
|
||||
|
||||
```golang
|
||||
err := transmissionbt.TorrentStopIDs([]int64{55})
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
} else {
|
||||
fmt.Println("yay")
|
||||
}
|
||||
```
|
||||
|
||||
* torrent-verify
|
||||
|
||||
Check [TorrentVerifyIDs()](https://godoc.org/github.com/hekmon/transmissionrpc#Client.TorrentVerifyIDs), [TorrentVerifyHashes()](https://godoc.org/github.com/hekmon/transmissionrpc#Client.TorrentVerifyHashes) and [TorrentVerifyRecentlyActive()](https://godoc.org/github.com/hekmon/transmissionrpc#Client.TorrentVerifyRecentlyActive).
|
||||
|
||||
Ex:
|
||||
|
||||
```golang
|
||||
err := transmissionbt.TorrentVerifyHashes([]string{"f07e0b0584745b7bcb35e98097488d34e68623d0"})
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
} else {
|
||||
fmt.Println("yay")
|
||||
}
|
||||
```
|
||||
|
||||
* torrent-reannounce
|
||||
|
||||
Check [TorrentReannounceIDs()](https://godoc.org/github.com/hekmon/transmissionrpc#Client.TorrentReannounceIDs), [TorrentReannounceHashes()](https://godoc.org/github.com/hekmon/transmissionrpc#Client.TorrentReannounceHashes) and [TorrentReannounceRecentlyActive()](https://godoc.org/github.com/hekmon/transmissionrpc#Client.TorrentReannounceRecentlyActive).
|
||||
|
||||
Ex:
|
||||
|
||||
```golang
|
||||
err := transmissionbt.TorrentReannounceRecentlyActive()
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
} else {
|
||||
fmt.Println("yay")
|
||||
}
|
||||
```
|
||||
|
||||
#### Torrent Mutators
|
||||
|
||||
* torrent-set
|
||||
|
||||
Mapped as [TorrentSet()](https://godoc.org/github.com/hekmon/transmissionrpc#Client.TorrentSet).
|
||||
|
||||
Ex: apply a 1 MB/s limit to a torrent.
|
||||
|
||||
```golang
|
||||
uploadLimited := true
|
||||
uploadLimitKBps := int64(1000)
|
||||
err := transmissionbt.TorrentSet(&transmissionrpc.TorrentSetPayload{
|
||||
IDs: []int64{55},
|
||||
UploadLimited: &uploadLimited,
|
||||
UploadLimit: &uploadLimitKBps,
|
||||
})
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
} else {
|
||||
fmt.Println("yay")
|
||||
}
|
||||
```
|
||||
|
||||
There is a lot more [mutators](https://godoc.org/github.com/hekmon/transmissionrpc#TorrentSetPayload) available.
|
||||
|
||||
#### Torrent Accessors
|
||||
|
||||
* torrent-get
|
||||
|
||||
All fields for all torrents with [TorrentGetAll()](https://godoc.org/github.com/hekmon/transmissionrpc#Client.TorrentGetAll):
|
||||
|
||||
```golang
|
||||
torrents, err := transmissionbt.TorrentGetAll()
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
} else {
|
||||
fmt.Println(torrents) // meh it's full of pointers
|
||||
}
|
||||
```
|
||||
|
||||
All fields for a restricted list of ids with [TorrentGetAllFor()](https://godoc.org/github.com/hekmon/transmissionrpc#Client.TorrentGetAll):
|
||||
|
||||
```golang
|
||||
torrents, err := transmissionbt.TorrentGetAllFor([]int64{31})
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
} else {
|
||||
fmt.Println(torrents) // meh it's still full of pointers
|
||||
}
|
||||
```
|
||||
|
||||
Some fields for some torrents with the low level accessor [TorrentGet()](https://godoc.org/github.com/hekmon/transmissionrpc#Client.TorrentGet):
|
||||
|
||||
```golang
|
||||
torrents, err := transmissionbt.TorrentGet([]string{"status"}, []int64{54, 55})
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
} else {
|
||||
for _, torrent := range torrents {
|
||||
fmt.Println(torrent.Status) // the only instanciated field, as requested
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Some fields for all torrents, still with the low level accessor [TorrentGet()](https://godoc.org/github.com/hekmon/transmissionrpc#Client.TorrentGet):
|
||||
|
||||
```golang
|
||||
torrents, err := transmissionbt.TorrentGet([]string{"id", "name", "hashString"}, nil)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
} else {
|
||||
for _, torrent := range torrents {
|
||||
fmt.Println(torrent.ID)
|
||||
fmt.Println(torrent.Name)
|
||||
fmt.Println(torrent.HashString)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Valid fields name can be found as JSON tag on the [Torrent](https://godoc.org/github.com/hekmon/transmissionrpc#Torrent) struct.
|
||||
|
||||
#### Adding a Torrent
|
||||
|
||||
* torrent-add
|
||||
|
||||
Adding a torrent from a file (using [TorrentAddFile](https://godoc.org/github.com/hekmon/transmissionrpc#Client.TorrentAddFile) wrapper):
|
||||
|
||||
```golang
|
||||
filepath := "/home/hekmon/Downloads/ubuntu-17.10.1-desktop-amd64.iso.torrent"
|
||||
torrent, err := transmissionbt.TorrentAddFile(filepath)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
} else {
|
||||
// Only 3 fields will be returned/set in the Torrent struct
|
||||
fmt.Println(*torrent.ID)
|
||||
fmt.Println(*torrent.Name)
|
||||
fmt.Println(*torrent.HashString)
|
||||
}
|
||||
```
|
||||
|
||||
Adding a torrent from an URL (ex: a magnet) with the real [TorrentAdd](https://godoc.org/github.com/hekmon/transmissionrpc#Client.TorrentAdd) method:
|
||||
|
||||
```golang
|
||||
magnet := "magnet:?xt=urn:btih:f07e0b0584745b7bcb35e98097488d34e68623d0&dn=ubuntu-17.10.1-desktop-amd64.iso&tr=http%3A%2F%2Ftorrent.ubuntu.com%3A6969%2Fannounce&tr=http%3A%2F%2Fipv6.torrent.ubuntu.com%3A6969%2Fannounce"
|
||||
torrent, err := btserv.TorrentAdd(&transmissionrpc.TorrentAddPayload{
|
||||
Filename: &magnet,
|
||||
})
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
} else {
|
||||
// Only 3 fields will be returned/set in the Torrent struct
|
||||
fmt.Println(*torrent.ID)
|
||||
fmt.Println(*torrent.Name)
|
||||
fmt.Println(*torrent.HashString)
|
||||
}
|
||||
```
|
||||
|
||||
Which would output:
|
||||
|
||||
```raw
|
||||
55
|
||||
ubuntu-17.10.1-desktop-amd64.iso
|
||||
f07e0b0584745b7bcb35e98097488d34e68623d0
|
||||
```
|
||||
|
||||
#### Removing a Torrent
|
||||
|
||||
* torrent-remove
|
||||
|
||||
Mapped as [TorrentRemove()](https://godoc.org/github.com/hekmon/transmissionrpc#Client.TorrentRemove).
|
||||
|
||||
#### Moving a Torrent
|
||||
|
||||
* torrent-set-location
|
||||
|
||||
Mapped as [TorrentSetLocation()](https://godoc.org/github.com/hekmon/transmissionrpc#Client.TorrentSetLocation).
|
||||
|
||||
#### Renaming a Torrent path
|
||||
|
||||
* torrent-rename-path
|
||||
|
||||
Mapped as [TorrentRenamePath()](https://godoc.org/github.com/hekmon/transmissionrpc#Client.TorrentRenamePath).
|
||||
|
||||
### Session Requests
|
||||
|
||||
#### Session Arguments
|
||||
|
||||
* session-set
|
||||
|
||||
Mapped as [SessionArgumentsSet()](https://godoc.org/github.com/hekmon/transmissionrpc#Client.SessionArgumentsSet).
|
||||
|
||||
* session-get
|
||||
|
||||
Mapped as [SessionArgumentsGet()](https://godoc.org/github.com/hekmon/transmissionrpc#Client.SessionArgumentsGet).
|
||||
|
||||
#### Session Statistics
|
||||
|
||||
* session-stats
|
||||
|
||||
Mapped as [SessionStats()](https://godoc.org/github.com/hekmon/transmissionrpc#Client.SessionStats).
|
||||
|
||||
#### Blocklist
|
||||
|
||||
* blocklist-update _(to do)_
|
||||
|
||||
#### Port Checking
|
||||
|
||||
* port-test
|
||||
|
||||
Mapped as [PortTest()](https://godoc.org/github.com/hekmon/transmissionrpc#Client.PortTest).
|
||||
|
||||
Ex:
|
||||
|
||||
```golang
|
||||
st, err := transmissionbt.PortTest()
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
}
|
||||
if st {
|
||||
fmt.Println("Open!")
|
||||
}
|
||||
```
|
||||
|
||||
#### Session Shutdown
|
||||
|
||||
* session-close _(to do)_
|
||||
|
||||
#### Queue Movement Requests
|
||||
|
||||
* queue-move-top _(to do)_
|
||||
* queue-move-up _(to do)_
|
||||
* queue-move-down _(to do)_
|
||||
* queue-move-bottom _(to do)_
|
||||
|
||||
#### Free Space
|
||||
|
||||
* free-space
|
||||
|
||||
Mappped as [FreeSpace()](https://godoc.org/github.com/hekmon/transmissionrpc#Client.FreeSpace).
|
||||
|
||||
Ex: Get the space available for /data.
|
||||
|
||||
```golang
|
||||
freeSpace, err := transmissionbt.FreeSpace("/data")
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
} else {
|
||||
fmt.Printd("%s | %d | %v", freeSpace, freeSpace, freeSpace)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
For more information about the freeSpace type, check the [ComputerUnits](https://github.com/hekmon/cunits) library.
|
||||
97
vendor/github.com/hekmon/transmissionrpc/controller.go
generated
vendored
Normal file
97
vendor/github.com/hekmon/transmissionrpc/controller.go
generated
vendored
Normal file
@@ -0,0 +1,97 @@
|
||||
package transmissionrpc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
cleanhttp "github.com/hashicorp/go-cleanhttp"
|
||||
)
|
||||
|
||||
const (
|
||||
// RPCVersion indicates the exact transmission RPC version this library is build against
|
||||
RPCVersion = 15
|
||||
defaultPort = 9091
|
||||
defaultRPCPath = "/transmission/rpc"
|
||||
defaultTimeout = 30 * time.Second
|
||||
defaultUserAgent = "github.com/hekmon/transmissionrpc"
|
||||
)
|
||||
|
||||
// Client is the base object to interract with a remote transmission rpc endpoint.
|
||||
// It must be created with New().
|
||||
type Client struct {
|
||||
url string
|
||||
user string
|
||||
password string
|
||||
sessionID string
|
||||
sessionIDAccess sync.RWMutex
|
||||
userAgent string
|
||||
rnd *rand.Rand
|
||||
httpC *http.Client
|
||||
}
|
||||
|
||||
// AdvancedConfig handles options that are not mandatory for New().
|
||||
// Default value for HTTPS is false, default port is 9091, default RPC URI is
|
||||
// '/transmission/rpc', default HTTPTimeout is 30s.
|
||||
type AdvancedConfig struct {
|
||||
HTTPS bool
|
||||
Port uint16
|
||||
RPCURI string
|
||||
HTTPTimeout time.Duration
|
||||
UserAgent string
|
||||
}
|
||||
|
||||
// New returns an initialized and ready to use Controller
|
||||
func New(host, user, password string, conf *AdvancedConfig) (c *Client, err error) {
|
||||
// Config
|
||||
if conf != nil {
|
||||
// Check custom config
|
||||
if conf.Port == 0 {
|
||||
conf.Port = defaultPort
|
||||
}
|
||||
if conf.RPCURI == "" {
|
||||
conf.RPCURI = defaultRPCPath
|
||||
}
|
||||
if conf.HTTPTimeout == 0 {
|
||||
conf.HTTPTimeout = defaultTimeout
|
||||
}
|
||||
if conf.UserAgent == "" {
|
||||
conf.UserAgent = defaultUserAgent
|
||||
}
|
||||
} else {
|
||||
// Spawn default config
|
||||
conf = &AdvancedConfig{
|
||||
// HTTPS false by default
|
||||
Port: defaultPort,
|
||||
RPCURI: defaultRPCPath,
|
||||
HTTPTimeout: defaultTimeout,
|
||||
UserAgent: defaultUserAgent,
|
||||
}
|
||||
}
|
||||
// Build & validate URL
|
||||
var scheme string
|
||||
if conf.HTTPS {
|
||||
scheme = "https"
|
||||
} else {
|
||||
scheme = "http"
|
||||
}
|
||||
remoteURL, err := url.Parse(fmt.Sprintf("%s://%s:%d%s", scheme, host, conf.Port, conf.RPCURI))
|
||||
if err != nil {
|
||||
err = fmt.Errorf("can't build a valid URL: %v", err)
|
||||
return
|
||||
}
|
||||
// Initialize & return ready to use client
|
||||
c = &Client{
|
||||
url: remoteURL.String(),
|
||||
user: user,
|
||||
password: password,
|
||||
userAgent: conf.UserAgent,
|
||||
rnd: rand.New(rand.NewSource(time.Now().Unix())),
|
||||
httpC: cleanhttp.DefaultPooledClient(),
|
||||
}
|
||||
c.httpC.Timeout = conf.HTTPTimeout
|
||||
return
|
||||
}
|
||||
39
vendor/github.com/hekmon/transmissionrpc/free_space.go
generated
vendored
Normal file
39
vendor/github.com/hekmon/transmissionrpc/free_space.go
generated
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
package transmissionrpc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/hekmon/cunits"
|
||||
)
|
||||
|
||||
/*
|
||||
Free Space
|
||||
https://github.com/transmission/transmission/blob/2.9x/extras/rpc-spec.txt#L618
|
||||
*/
|
||||
|
||||
// FreeSpace allow to see how much free space is available in a client-specified folder.
|
||||
func (c *Client) FreeSpace(path string) (freeSpace cunits.Bits, err error) {
|
||||
payload := &transmissionFreeSpacePayload{Path: path}
|
||||
var space TransmissionFreeSpace
|
||||
if err = c.rpcCall("free-space", payload, &space); err == nil {
|
||||
if space.Path == path {
|
||||
freeSpace = cunits.ImportInByte(float64(space.Size))
|
||||
} else {
|
||||
err = fmt.Errorf("returned path '%s' does not match with requested path '%s'", space.Path, path)
|
||||
}
|
||||
} else {
|
||||
err = fmt.Errorf("'free-space' rpc method failed: %v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type transmissionFreeSpacePayload struct {
|
||||
Path string `json:"path"`
|
||||
}
|
||||
|
||||
// TransmissionFreeSpace represents the freespace available in bytes for a specific path.
|
||||
// https://github.com/transmission/transmission/blob/2.9x/extras/rpc-spec.txt#L631
|
||||
type TransmissionFreeSpace struct {
|
||||
Path string `json:"path"`
|
||||
Size int64 `json:"size-bytes"`
|
||||
}
|
||||
26
vendor/github.com/hekmon/transmissionrpc/port_checking.go
generated
vendored
Normal file
26
vendor/github.com/hekmon/transmissionrpc/port_checking.go
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
package transmissionrpc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
/*
|
||||
Port Checking
|
||||
https://github.com/transmission/transmission/blob/2.9x/extras/rpc-spec.txt#L584
|
||||
*/
|
||||
|
||||
// PortTest allows tests to see if your incoming peer port is accessible from the outside world.
|
||||
func (c *Client) PortTest() (open bool, err error) {
|
||||
var result portTestAnswer
|
||||
// Send request
|
||||
if err = c.rpcCall("port-test", nil, &result); err == nil {
|
||||
open = result.PortOpen
|
||||
} else {
|
||||
err = fmt.Errorf("'port-test' rpc method failed: %v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type portTestAnswer struct {
|
||||
PortOpen bool `json:"port-is-open"`
|
||||
}
|
||||
140
vendor/github.com/hekmon/transmissionrpc/request.go
generated
vendored
Normal file
140
vendor/github.com/hekmon/transmissionrpc/request.go
generated
vendored
Normal file
@@ -0,0 +1,140 @@
|
||||
package transmissionrpc
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"sync"
|
||||
)
|
||||
|
||||
const csrfHeader = "X-Transmission-Session-Id"
|
||||
|
||||
type requestPayload struct {
|
||||
Method string `json:"method"`
|
||||
Arguments interface{} `json:"arguments,omitempty"`
|
||||
Tag int `json:"tag,omitempty"`
|
||||
}
|
||||
|
||||
type answerPayload struct {
|
||||
Arguments interface{} `json:"arguments"`
|
||||
Result string `json:"result"`
|
||||
Tag *int `json:"tag"`
|
||||
}
|
||||
|
||||
func (c *Client) rpcCall(method string, arguments interface{}, result interface{}) (err error) {
|
||||
return c.request(method, arguments, result, true)
|
||||
}
|
||||
|
||||
func (c *Client) request(method string, arguments interface{}, result interface{}, retry bool) (err error) {
|
||||
// Let's avoid crashing
|
||||
if c.httpC == nil {
|
||||
err = errors.New("this controller is not initialized, please use the New() function")
|
||||
return
|
||||
}
|
||||
// Prepare the pipeline between payload generation and request
|
||||
pOut, pIn := io.Pipe()
|
||||
// Prepare the request
|
||||
var req *http.Request
|
||||
if req, err = http.NewRequest("POST", c.url, pOut); err != nil {
|
||||
err = fmt.Errorf("can't prepare request for '%s' method: %v", method, err)
|
||||
return
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("User-Agent", c.userAgent)
|
||||
req.Header.Set(csrfHeader, c.getSessionID())
|
||||
req.SetBasicAuth(c.user, c.password)
|
||||
// Prepare the marshalling goroutine
|
||||
var tag int
|
||||
var encErr error
|
||||
var mg sync.WaitGroup
|
||||
mg.Add(1)
|
||||
go func() {
|
||||
tag = c.rnd.Int()
|
||||
encErr = json.NewEncoder(pIn).Encode(&requestPayload{
|
||||
Method: method,
|
||||
Arguments: arguments,
|
||||
Tag: tag,
|
||||
})
|
||||
pIn.Close()
|
||||
mg.Done()
|
||||
}()
|
||||
// Execute request
|
||||
var resp *http.Response
|
||||
if resp, err = c.httpC.Do(req); err != nil {
|
||||
mg.Wait()
|
||||
if encErr != nil {
|
||||
err = fmt.Errorf("request error: %v | json payload marshall error: %v", err, encErr)
|
||||
} else {
|
||||
err = fmt.Errorf("request error: %v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
// Let's test the enc result, just in case
|
||||
mg.Wait()
|
||||
if encErr != nil {
|
||||
err = fmt.Errorf("request payload JSON marshalling failed: %v", encErr)
|
||||
return
|
||||
}
|
||||
// Is the CRSF token invalid ?
|
||||
if resp.StatusCode == http.StatusConflict {
|
||||
// Recover new token and save it
|
||||
c.updateSessionID(resp.Header.Get(csrfHeader))
|
||||
// Retry request if first try
|
||||
if retry {
|
||||
return c.request(method, arguments, result, false)
|
||||
}
|
||||
err = errors.New("CSRF token invalid 2 times in a row: stopping to avoid infinite loop")
|
||||
return
|
||||
}
|
||||
// Is request successful ?
|
||||
if resp.StatusCode != 200 {
|
||||
err = fmt.Errorf("HTTP error %d: %s", resp.StatusCode, http.StatusText(resp.StatusCode))
|
||||
return
|
||||
}
|
||||
// // Debug
|
||||
// {
|
||||
// var data []byte
|
||||
// data, err = ioutil.ReadAll(resp.Body)
|
||||
// fmt.Println(string(data))
|
||||
// return
|
||||
// }
|
||||
// Decode body
|
||||
answer := answerPayload{
|
||||
Arguments: result,
|
||||
}
|
||||
if err = json.NewDecoder(resp.Body).Decode(&answer); err != nil {
|
||||
err = fmt.Errorf("can't unmarshall request answer body: %v", err)
|
||||
return
|
||||
}
|
||||
// fmt.Println("DEBUG >", answer.Result)
|
||||
// Final checks
|
||||
if answer.Tag == nil {
|
||||
err = errors.New("http answer does not have a tag within it's payload")
|
||||
return
|
||||
}
|
||||
if *answer.Tag != tag {
|
||||
err = errors.New("http request tag and answer payload tag do not match")
|
||||
return
|
||||
}
|
||||
if answer.Result != "success" {
|
||||
err = fmt.Errorf("http request ok but payload does not indicate success: %s", answer.Result)
|
||||
return
|
||||
}
|
||||
// All good
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Client) getSessionID() string {
|
||||
defer c.sessionIDAccess.RUnlock()
|
||||
c.sessionIDAccess.RLock()
|
||||
return c.sessionID
|
||||
}
|
||||
|
||||
func (c *Client) updateSessionID(newID string) {
|
||||
defer c.sessionIDAccess.Unlock()
|
||||
c.sessionIDAccess.Lock()
|
||||
c.sessionID = newID
|
||||
}
|
||||
164
vendor/github.com/hekmon/transmissionrpc/session_arguments.go
generated
vendored
Normal file
164
vendor/github.com/hekmon/transmissionrpc/session_arguments.go
generated
vendored
Normal file
@@ -0,0 +1,164 @@
|
||||
package transmissionrpc
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/hekmon/cunits"
|
||||
)
|
||||
|
||||
/*
|
||||
Session Arguments
|
||||
hhttps://github.com/transmission/transmission/blob/2.9x/extras/rpc-spec.txt#L461
|
||||
*/
|
||||
|
||||
// RPCVersion returns true if the lib RPC version is greater or equals to the remote server rpc minimum version.
|
||||
func (c *Client) RPCVersion() (ok bool, serverVersion int64, serverMinimumVersion int64, err error) {
|
||||
payload, err := c.SessionArgumentsGet()
|
||||
if err != nil {
|
||||
err = fmt.Errorf("can't get session values: %v", err)
|
||||
return
|
||||
}
|
||||
if payload.RPCVersion == nil {
|
||||
err = fmt.Errorf("payload RPC Version is nil")
|
||||
return
|
||||
}
|
||||
if payload.RPCVersionMinimum == nil {
|
||||
err = fmt.Errorf("payload RPC Version minimum is nil")
|
||||
return
|
||||
}
|
||||
serverVersion = *payload.RPCVersion
|
||||
serverMinimumVersion = *payload.RPCVersionMinimum
|
||||
ok = RPCVersion >= serverMinimumVersion
|
||||
return
|
||||
}
|
||||
|
||||
// SessionArgumentsSet allows to modify global/session values.
|
||||
// https://github.com/transmission/transmission/blob/2.9x/extras/rpc-spec.txt#L534
|
||||
func (c *Client) SessionArgumentsSet(payload *SessionArguments) (err error) {
|
||||
// Checks
|
||||
if payload == nil {
|
||||
err = fmt.Errorf("payload can't be nil")
|
||||
return
|
||||
}
|
||||
payload.BlocklistSize = nil
|
||||
payload.ConfigDir = nil
|
||||
payload.RPCVersion = nil
|
||||
payload.RPCVersionMinimum = nil
|
||||
payload.Version = nil
|
||||
// Exec
|
||||
if err = c.rpcCall("session-set", payload, nil); err != nil {
|
||||
err = fmt.Errorf("'session-set' rpc method failed: %v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// SessionArgumentsGet returns global/session values.
|
||||
// https://github.com/transmission/transmission/blob/2.9x/extras/rpc-spec.txt#L542
|
||||
func (c *Client) SessionArgumentsGet() (sessionArgs *SessionArguments, err error) {
|
||||
if err = c.rpcCall("session-get", nil, &sessionArgs); err != nil {
|
||||
err = fmt.Errorf("'session-get' rpc method failed: %v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// SessionArguments represents all the global/session values.
|
||||
// https://github.com/transmission/transmission/blob/2.9x/extras/rpc-spec.txt#L461
|
||||
type SessionArguments struct {
|
||||
AltSpeedDown *int64 `json:"alt-speed-down"` // max global download speed (KBps)
|
||||
AltSpeedEnabled *bool `json:"alt-speed-enabled"` // true means use the alt speeds
|
||||
AltSpeedTimeBegin *int64 `json:"alt-speed-time-begin"` // when to turn on alt speeds (units: minutes after midnight)
|
||||
AltSpeedTimeEnabled *bool `json:"alt-speed-time-enabled"` // true means the scheduled on/off times are used
|
||||
AltSpeedTimeEnd *int64 `json:"alt-speed-time-end"` // when to turn off alt speeds (units: same)
|
||||
AltSpeedTimeDay *int64 `json:"alt-speed-time-day"` // what day(s) to turn on alt speeds (look at tr_sched_day)
|
||||
AltSpeedUp *int64 `json:"alt-speed-up"` // max global upload speed (KBps)
|
||||
BlocklistURL *string `json:"blocklist-url"` // location of the blocklist to use for "blocklist-update"
|
||||
BlocklistEnabled *bool `json:"blocklist-enabled"` // true means enabled
|
||||
BlocklistSize *int64 `json:"blocklist-size"` // number of rules in the blocklist
|
||||
CacheSizeMB *int64 `json:"cache-size-mb"` // maximum size of the disk cache (MB)
|
||||
ConfigDir *string `json:"config-dir"` // location of transmission's configuration directory
|
||||
DownloadDir *string `json:"download-dir"` // default path to download torrents
|
||||
DownloadQueueSize *int64 `json:"download-queue-size"` // max number of torrents to download at once (see download-queue-enabled)
|
||||
DownloadQueueEnabled *bool `json:"download-queue-enabled"` // if true, limit how many torrents can be downloaded at once
|
||||
DHTEnabled *bool `json:"dht-enabled"` // true means allow dht in public torrents
|
||||
Encryption *string `json:"encryption"` // "required", "preferred", "tolerated"
|
||||
IdleSeedingLimit *int64 `json:"idle-seeding-limit"` // torrents we're seeding will be stopped if they're idle for this long
|
||||
IdleSeedingLimitEnabled *bool `json:"idle-seeding-limit-enabled"` // true if the seeding inactivity limit is honored by default
|
||||
IncompleteDir *string `json:"incomplete-dir"` // path for incomplete torrents, when enabled
|
||||
IncompleteDirEnabled *bool `json:"incomplete-dir-enabled"` // true means keep torrents in incomplete-dir until done
|
||||
LPDEnabled *bool `json:"lpd-enabled"` // true means allow Local Peer Discovery in public torrents
|
||||
PeerLimitGlobal *int64 `json:"peer-limit-global"` // maximum global number of peers
|
||||
PeerLimitPerTorrent *int64 `json:"peer-limit-per-torrent"` // maximum global number of peers
|
||||
PEXEnabled *bool `json:"pex-enabled"` // true means allow pex in public torrents
|
||||
PeerPort *int64 `json:"peer-port"` // port number
|
||||
PeerPortRandomOnStart *bool `json:"peer-port-random-on-start"` // true means pick a random peer port on launch
|
||||
PortForwardingEnabled *bool `json:"port-forwarding-enabled"` // true means enabled
|
||||
QueueStalledEnabled *bool `json:"queue-stalled-enabled"` // whether or not to consider idle torrents as stalled
|
||||
QueueStalledMinutes *int64 `json:"queue-stalled-minutes"` // torrents that are idle for N minuets aren't counted toward seed-queue-size or download-queue-size
|
||||
RenamePartialFiles *bool `json:"rename-partial-files"` // true means append ".part" to incomplete files
|
||||
RPCVersion *int64 `json:"rpc-version"` // the current RPC API version
|
||||
RPCVersionMinimum *int64 `json:"rpc-version-minimum"` // the minimum RPC API version supported
|
||||
ScriptTorrentDoneFilename *string `json:"script-torrent-done-filename"` // filename of the script to run
|
||||
ScriptTorrentDoneEnabled *bool `json:"script-torrent-done-enabled"` // whether or not to call the "done" script
|
||||
SeedRatioLimit *float64 `json:"seedRatioLimit"` // the default seed ratio for torrents to use
|
||||
SeedRatioLimited *bool `json:"seedRatioLimited"` // true if seedRatioLimit is honored by default
|
||||
SeedQueueSize *int64 `json:"seed-queue-size"` // max number of torrents to uploaded at once (see seed-queue-enabled)
|
||||
SeedQueueEnabled *bool `json:"seed-queue-enabled"` // if true, limit how many torrents can be uploaded at once
|
||||
SpeedLimitDown *int64 `json:"speed-limit-down"` // max global download speed (KBps)
|
||||
SpeedLimitDownEnabled *bool `json:"speed-limit-down-enabled"` // true means enabled
|
||||
SpeedLimitUp *int64 `json:"speed-limit-up"` // max global upload speed (KBps)
|
||||
SpeedLimitUpEnabled *bool `json:"speed-limit-up-enabled"` // true means enabled
|
||||
StartAddedTorrents *bool `json:"start-added-torrents"` // true means added torrents will be started right away
|
||||
TrashOriginalTorrentFiles *bool `json:"trash-original-torrent-files"` // true means the .torrent file of added torrents will be deleted
|
||||
Units *Units `json:"units"` // see units below
|
||||
UTPEnabled *bool `json:"utp-enabled"` // true means allow utp
|
||||
Version *string `json:"version"` // long version string "$version ($revision)"
|
||||
}
|
||||
|
||||
// MarshalJSON allows to marshall into JSON only the non nil fields.
|
||||
// It differs from 'omitempty' which also skip default values
|
||||
// (as 0 or false which can be valid here).
|
||||
func (sa *SessionArguments) MarshalJSON() (data []byte, err error) {
|
||||
// Build a payload with only the non nil fields
|
||||
tspv := reflect.ValueOf(*sa)
|
||||
tspt := tspv.Type()
|
||||
cleanPayload := make(map[string]interface{}, tspt.NumField())
|
||||
var currentValue reflect.Value
|
||||
var currentStructField reflect.StructField
|
||||
for i := 0; i < tspv.NumField(); i++ {
|
||||
currentValue = tspv.Field(i)
|
||||
currentStructField = tspt.Field(i)
|
||||
if !currentValue.IsNil() {
|
||||
cleanPayload[currentStructField.Tag.Get("json")] = currentValue.Interface()
|
||||
}
|
||||
}
|
||||
// Marshall the clean payload
|
||||
return json.Marshal(cleanPayload)
|
||||
}
|
||||
|
||||
// Units is subset of SessionArguments.
|
||||
// https://github.com/transmission/transmission/blob/2.9x/extras/rpc-spec.txt#L514
|
||||
type Units struct {
|
||||
SpeedUnits []string `json:"speed-units"` // 4 strings: KB/s, MB/s, GB/s, TB/s
|
||||
SpeedBytes int64 `json:"speed-bytes"` // number of bytes in a KB (1000 for kB; 1024 for KiB)
|
||||
SizeUnits []string `json:"size-units"` // 4 strings: KB/s, MB/s, GB/s, TB/s
|
||||
SizeBytes int64 `json:"size-bytes"` // number of bytes in a KB (1000 for kB; 1024 for KiB)
|
||||
MemoryUnits []string `json:"memory-units"` // 4 strings: KB/s, MB/s, GB/s, TB/s
|
||||
MemoryBytes int64 `json:"memory-bytes"` // number of bytes in a KB (1000 for kB; 1024 for KiB)
|
||||
}
|
||||
|
||||
// GetSpeed returns the speed in a handy format
|
||||
func (u *Units) GetSpeed() (speed cunits.Bits) {
|
||||
return cunits.ImportInByte(float64(u.SpeedBytes))
|
||||
}
|
||||
|
||||
// GetSize returns the size in a handy format
|
||||
func (u *Units) GetSize() (size cunits.Bits) {
|
||||
return cunits.ImportInByte(float64(u.SizeBytes))
|
||||
}
|
||||
|
||||
// GetMemory returns the memory in a handy format
|
||||
func (u *Units) GetMemory() (memory cunits.Bits) {
|
||||
return cunits.ImportInByte(float64(u.MemoryBytes))
|
||||
}
|
||||
73
vendor/github.com/hekmon/transmissionrpc/session_stats.go
generated
vendored
Normal file
73
vendor/github.com/hekmon/transmissionrpc/session_stats.go
generated
vendored
Normal file
@@ -0,0 +1,73 @@
|
||||
package transmissionrpc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/hekmon/cunits"
|
||||
)
|
||||
|
||||
/*
|
||||
Session Statistics
|
||||
https://github.com/transmission/transmission/blob/2.9x/extras/rpc-spec.txt#L546
|
||||
*/
|
||||
|
||||
// SessionStats returns all (current/cumulative) statistics.
|
||||
// https://github.com/transmission/transmission/blob/2.9x/extras/rpc-spec.txt#L548
|
||||
func (c *Client) SessionStats() (stats *SessionStats, err error) {
|
||||
if err = c.rpcCall("session-stats", nil, &stats); err != nil {
|
||||
err = fmt.Errorf("'session-stats' rpc method failed: %v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// SessionStats represents all (current/cumulative) statistics.
|
||||
// https://github.com/transmission/transmission/blob/2.9x/extras/rpc-spec.txt#L554
|
||||
type SessionStats struct {
|
||||
ActiveTorrentCount int64 `json:"activeTorrentCount"`
|
||||
CumulativeStats *CumulativeStats `json:"cumulative-stats"`
|
||||
CurrentStats *CurrentStats `json:"current-stats"`
|
||||
DownloadSpeed int64 `json:"downloadSpeed"`
|
||||
PausedTorrentCount int64 `json:"pausedTorrentCount"`
|
||||
TorrentCount int64 `json:"torrentCount"`
|
||||
UploadSpeed int64 `json:"uploadSpeed"`
|
||||
}
|
||||
|
||||
// CumulativeStats is subset of SessionStats.
|
||||
// https://github.com/transmission/transmission/blob/2.9x/extras/rpc-spec.txt#L562
|
||||
type CumulativeStats struct {
|
||||
DownloadedBytes int64 `json:"downloadedBytes"`
|
||||
FilesAdded int64 `json:"filesAdded"`
|
||||
SecondsActive int64 `json:"secondsActive"`
|
||||
SessionCount int64 `json:"sessionCount"`
|
||||
UploadedBytes int64 `json:"uploadedBytes"`
|
||||
}
|
||||
|
||||
// GetDownloaded returns cumulative stats downloaded size in a handy format
|
||||
func (cs *CumulativeStats) GetDownloaded() (downloaded cunits.Bits) {
|
||||
return cunits.ImportInByte(float64(cs.DownloadedBytes))
|
||||
}
|
||||
|
||||
// GetUploaded returns cumulative stats uploaded size in a handy format
|
||||
func (cs *CumulativeStats) GetUploaded() (uploaded cunits.Bits) {
|
||||
return cunits.ImportInByte(float64(cs.UploadedBytes))
|
||||
}
|
||||
|
||||
// CurrentStats is subset of SessionStats.
|
||||
// https://github.com/transmission/transmission/blob/2.9x/extras/rpc-spec.txt#L570
|
||||
type CurrentStats struct {
|
||||
DownloadedBytes int64 `json:"downloadedBytes"`
|
||||
FilesAdded int64 `json:"filesAdded"`
|
||||
SecondsActive int64 `json:"secondsActive"`
|
||||
SessionCount int64 `json:"sessionCount"`
|
||||
UploadedBytes int64 `json:"uploadedBytes"`
|
||||
}
|
||||
|
||||
// GetDownloaded returns current stats downloaded size in a handy format
|
||||
func (cs *CurrentStats) GetDownloaded() (downloaded cunits.Bits) {
|
||||
return cunits.ImportInByte(float64(cs.DownloadedBytes))
|
||||
}
|
||||
|
||||
// GetUploaded returns current stats uploaded size in a handy format
|
||||
func (cs *CurrentStats) GetUploaded() (uploaded cunits.Bits) {
|
||||
return cunits.ImportInByte(float64(cs.UploadedBytes))
|
||||
}
|
||||
609
vendor/github.com/hekmon/transmissionrpc/torrent_accessors.go
generated
vendored
Normal file
609
vendor/github.com/hekmon/transmissionrpc/torrent_accessors.go
generated
vendored
Normal file
@@ -0,0 +1,609 @@
|
||||
package transmissionrpc
|
||||
|
||||
/*
|
||||
Torrent Accessors
|
||||
https://github.com/transmission/transmission/blob/2.9x/extras/rpc-spec.txt#L142
|
||||
*/
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
"github.com/hekmon/cunits"
|
||||
)
|
||||
|
||||
var validTorrentFields []string
|
||||
|
||||
func init() {
|
||||
torrentType := reflect.TypeOf(Torrent{})
|
||||
for i := 0; i < torrentType.NumField(); i++ {
|
||||
validTorrentFields = append(validTorrentFields, torrentType.Field(i).Tag.Get("json"))
|
||||
}
|
||||
}
|
||||
|
||||
// TorrentGetAll returns all the known fields for all the torrents.
|
||||
func (c *Client) TorrentGetAll() (torrents []*Torrent, err error) {
|
||||
// Send already validated fields to the low level fx
|
||||
return c.torrentGet(validTorrentFields, nil)
|
||||
}
|
||||
|
||||
// TorrentGetAllFor returns all known fields for the given torrent's ids.
|
||||
func (c *Client) TorrentGetAllFor(ids []int64) (torrents []*Torrent, err error) {
|
||||
return c.torrentGet(validTorrentFields, ids)
|
||||
}
|
||||
|
||||
// TorrentGetAllForHashes returns all known fields for the given torrent's ids by string (usually hash).
|
||||
func (c *Client) TorrentGetAllForHashes(hashes []string) (torrents []*Torrent, err error) {
|
||||
return c.torrentGetHash(validTorrentFields, hashes)
|
||||
}
|
||||
|
||||
// TorrentGet returns the given of fields (mandatory) for each ids (optionnal).
|
||||
// https://github.com/transmission/transmission/blob/2.9x/extras/rpc-spec.txt#L144
|
||||
func (c *Client) TorrentGet(fields []string, ids []int64) (torrents []*Torrent, err error) {
|
||||
if err = c.validateFields(fields); err != nil {
|
||||
return
|
||||
}
|
||||
return c.torrentGet(fields, ids)
|
||||
}
|
||||
|
||||
// TorrentGetHashes returns the given of fields (mandatory) for each ids (optionnal).
|
||||
// https://github.com/transmission/transmission/blob/2.9x/extras/rpc-spec.txt#L144
|
||||
func (c *Client) TorrentGetHashes(fields []string, hashes []string) (torrents []*Torrent, err error) {
|
||||
if err = c.validateFields(fields); err != nil {
|
||||
return
|
||||
}
|
||||
return c.torrentGetHash(fields, hashes)
|
||||
}
|
||||
|
||||
func (c *Client) validateFields(fields []string) (err error) {
|
||||
// Validate fields
|
||||
var fieldInvalid bool
|
||||
var knownField string
|
||||
for _, inputField := range fields {
|
||||
fieldInvalid = true
|
||||
for _, knownField = range validTorrentFields {
|
||||
if inputField == knownField {
|
||||
fieldInvalid = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if fieldInvalid {
|
||||
err = fmt.Errorf("field '%s' is invalid", inputField)
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Client) torrentGet(fields []string, ids []int64) (torrents []*Torrent, err error) {
|
||||
var result torrentGetResults
|
||||
if err = c.rpcCall("torrent-get", &torrentGetParams{
|
||||
Fields: fields,
|
||||
IDs: ids,
|
||||
}, &result); err != nil {
|
||||
err = fmt.Errorf("'torrent-get' rpc method failed: %v", err)
|
||||
return
|
||||
}
|
||||
torrents = result.Torrents
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Client) torrentGetHash(fields []string, hashes []string) (torrents []*Torrent, err error) {
|
||||
var result torrentGetResults
|
||||
if err = c.rpcCall("torrent-get", &torrentGetHashParams{
|
||||
Fields: fields,
|
||||
Hashes: hashes,
|
||||
}, &result); err != nil {
|
||||
err = fmt.Errorf("'torrent-get' rpc method failed: %v", err)
|
||||
return
|
||||
}
|
||||
torrents = result.Torrents
|
||||
return
|
||||
}
|
||||
|
||||
type torrentGetParams struct {
|
||||
Fields []string `json:"fields"`
|
||||
IDs []int64 `json:"ids,omitempty"`
|
||||
}
|
||||
|
||||
type torrentGetHashParams struct {
|
||||
Fields []string `json:"fields"`
|
||||
Hashes []string `json:"ids,omitempty"`
|
||||
}
|
||||
|
||||
type torrentGetResults struct {
|
||||
Torrents []*Torrent `json:"torrents"`
|
||||
}
|
||||
|
||||
// Torrent represents all the possible fields of data for a torrent.
|
||||
// All fields are pointers to detect if the value is nil (field not requested) or default real default value.
|
||||
// https://github.com/transmission/transmission/blob/2.9x/extras/rpc-spec.txt#L163
|
||||
type Torrent struct {
|
||||
ActivityDate *time.Time `json:"activityDate"`
|
||||
AddedDate *time.Time `json:"addedDate"`
|
||||
BandwidthPriority *int64 `json:"bandwidthPriority"`
|
||||
Comment *string `json:"comment"`
|
||||
CorruptEver *int64 `json:"corruptEver"`
|
||||
Creator *string `json:"creator"`
|
||||
DateCreated *time.Time `json:"dateCreated"`
|
||||
DesiredAvailable *int64 `json:"desiredAvailable"`
|
||||
DoneDate *time.Time `json:"doneDate"`
|
||||
DownloadDir *string `json:"downloadDir"`
|
||||
DownloadedEver *int64 `json:"downloadedEver"`
|
||||
DownloadLimit *int64 `json:"downloadLimit"`
|
||||
DownloadLimited *bool `json:"downloadLimited"`
|
||||
Error *int64 `json:"error"`
|
||||
ErrorString *string `json:"errorString"`
|
||||
Eta *int64 `json:"eta"`
|
||||
EtaIdle *int64 `json:"etaIdle"`
|
||||
Files []*TorrentFile `json:"files"`
|
||||
FileStats []*TorrentFileStat `json:"fileStats"`
|
||||
HashString *string `json:"hashString"`
|
||||
HaveUnchecked *int64 `json:"haveUnchecked"`
|
||||
HaveValid *int64 `json:"haveValid"`
|
||||
HonorsSessionLimits *bool `json:"honorsSessionLimits"`
|
||||
ID *int64 `json:"id"`
|
||||
IsFinished *bool `json:"isFinished"`
|
||||
IsPrivate *bool `json:"isPrivate"`
|
||||
IsStalled *bool `json:"isStalled"`
|
||||
LeftUntilDone *int64 `json:"leftUntilDone"`
|
||||
MagnetLink *string `json:"magnetLink"`
|
||||
ManualAnnounceTime *int64 `json:"manualAnnounceTime"`
|
||||
MaxConnectedPeers *int64 `json:"maxConnectedPeers"`
|
||||
MetadataPercentComplete *float64 `json:"metadataPercentComplete"`
|
||||
Name *string `json:"name"`
|
||||
PeerLimit *int64 `json:"peer-limit"`
|
||||
Peers []*Peer `json:"peers"`
|
||||
PeersConnected *int64 `json:"peersConnected"`
|
||||
PeersFrom *TorrentPeersFrom `json:"peersFrom"`
|
||||
PeersGettingFromUs *int64 `json:"peersGettingFromUs"`
|
||||
PeersSendingToUs *int64 `json:"peersSendingToUs"`
|
||||
PercentDone *float64 `json:"percentDone"`
|
||||
Pieces *string `json:"pieces"` // https://github.com/transmission/transmission/blob/2.9x/extras/rpc-spec.txt#L279
|
||||
PieceCount *int64 `json:"pieceCount"`
|
||||
PieceSize *cunits.Bits `json:"pieceSize"`
|
||||
Priorities []int64 `json:"priorities"` // https://github.com/transmission/transmission/blob/2.9x/extras/rpc-spec.txt#L285
|
||||
QueuePosition *int64 `json:"queuePosition"`
|
||||
RateDownload *int64 `json:"rateDownload"` // B/s
|
||||
RateUpload *int64 `json:"rateUpload"` // B/s
|
||||
RecheckProgress *float64 `json:"recheckProgress"`
|
||||
SecondsDownloading *int64 `json:"secondsDownloading"`
|
||||
SecondsSeeding *time.Duration `json:"secondsSeeding"`
|
||||
SeedIdleLimit *int64 `json:"seedIdleLimit"`
|
||||
SeedIdleMode *int64 `json:"seedIdleMode"`
|
||||
SeedRatioLimit *float64 `json:"seedRatioLimit"`
|
||||
SeedRatioMode *SeedRatioMode `json:"seedRatioMode"`
|
||||
SizeWhenDone *cunits.Bits `json:"sizeWhenDone"`
|
||||
StartDate *time.Time `json:"startDate"`
|
||||
Status *TorrentStatus `json:"status"`
|
||||
Trackers []*Tracker `json:"trackers"`
|
||||
TrackerStats []*TrackerStats `json:"trackerStats"`
|
||||
TotalSize *cunits.Bits `json:"totalSize"`
|
||||
TorrentFile *string `json:"torrentFile"`
|
||||
UploadedEver *int64 `json:"uploadedEver"`
|
||||
UploadLimit *int64 `json:"uploadLimit"`
|
||||
UploadLimited *bool `json:"uploadLimited"`
|
||||
UploadRatio *float64 `json:"uploadRatio"`
|
||||
Wanted []bool `json:"wanted"` //https://github.com/transmission/transmission/blob/2.9x/extras/rpc-spec.txt#L325
|
||||
WebSeeds []string `json:"webseeds"` // https://github.com/transmission/transmission/blob/2.9x/extras/rpc-spec.txt#L329
|
||||
WebSeedsSendingToUs *int64 `json:"webseedsSendingToUs"`
|
||||
}
|
||||
|
||||
// ConvertDownloadSpeed will return the download speed as cunits.Bitss/second
|
||||
func (t *Torrent) ConvertDownloadSpeed() (speed cunits.Bits) {
|
||||
if t.RateDownload != nil {
|
||||
speed = cunits.ImportInByte(float64(*t.RateDownload))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ConvertUploadSpeed will return the upload speed as cunits.Bitss/second
|
||||
func (t *Torrent) ConvertUploadSpeed() (speed cunits.Bits) {
|
||||
if t.RateUpload != nil {
|
||||
speed = cunits.ImportInByte(float64(*t.RateUpload))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// UnmarshalJSON allows to convert timestamps to golang time.Time values.
|
||||
func (t *Torrent) UnmarshalJSON(data []byte) (err error) {
|
||||
// Shadow real type for regular unmarshalling
|
||||
type RawTorrent Torrent
|
||||
tmp := &struct {
|
||||
ActivityDate *int64 `json:"activityDate"`
|
||||
AddedDate *int64 `json:"addedDate"`
|
||||
DateCreated *int64 `json:"dateCreated"`
|
||||
DoneDate *int64 `json:"doneDate"`
|
||||
PieceSize *int64 `json:"pieceSize"`
|
||||
SecondsSeeding *int64 `json:"secondsSeeding"`
|
||||
SizeWhenDone *int64 `json:"sizeWhenDone"`
|
||||
StartDate *int64 `json:"startDate"`
|
||||
TotalSize *int64 `json:"totalSize"`
|
||||
Wanted []int64 `json:"wanted"` // boolean in number form
|
||||
*RawTorrent
|
||||
}{
|
||||
RawTorrent: (*RawTorrent)(t),
|
||||
}
|
||||
// Unmarshall (with timestamps as number)
|
||||
if err = json.Unmarshal(data, &tmp); err != nil {
|
||||
return
|
||||
}
|
||||
// Create the real time & duration from timsteamps and seconds
|
||||
if tmp.ActivityDate != nil {
|
||||
ad := time.Unix(*tmp.ActivityDate, 0)
|
||||
t.ActivityDate = &ad
|
||||
}
|
||||
if tmp.AddedDate != nil {
|
||||
ad := time.Unix(*tmp.AddedDate, 0)
|
||||
t.AddedDate = &ad
|
||||
}
|
||||
if tmp.DateCreated != nil {
|
||||
dc := time.Unix(*tmp.DateCreated, 0)
|
||||
t.DateCreated = &dc
|
||||
}
|
||||
if tmp.DoneDate != nil {
|
||||
dd := time.Unix(*tmp.DoneDate, 0)
|
||||
t.DoneDate = &dd
|
||||
}
|
||||
if tmp.PieceSize != nil {
|
||||
ps := cunits.ImportInByte(float64(*tmp.PieceSize))
|
||||
t.PieceSize = &ps
|
||||
}
|
||||
if tmp.SecondsSeeding != nil {
|
||||
dur := time.Duration(*tmp.SecondsSeeding) * time.Second
|
||||
t.SecondsSeeding = &dur
|
||||
}
|
||||
if tmp.SizeWhenDone != nil {
|
||||
swd := cunits.ImportInByte(float64(*tmp.SizeWhenDone))
|
||||
t.SizeWhenDone = &swd
|
||||
}
|
||||
if tmp.StartDate != nil {
|
||||
st := time.Unix(*tmp.StartDate, 0)
|
||||
t.StartDate = &st
|
||||
}
|
||||
if tmp.TotalSize != nil {
|
||||
ts := cunits.ImportInByte(float64(*tmp.TotalSize))
|
||||
t.TotalSize = &ts
|
||||
}
|
||||
// Boolean slice in decimal form
|
||||
if tmp.Wanted != nil {
|
||||
t.Wanted = make([]bool, len(tmp.Wanted))
|
||||
for index, value := range tmp.Wanted {
|
||||
if value == 1 {
|
||||
t.Wanted[index] = true
|
||||
} else if value != 0 {
|
||||
return fmt.Errorf("Can't convert Wanted index %d value '%d' as boolean", index, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// MarshalJSON allows to convert back golang values to original payload values.
|
||||
func (t *Torrent) MarshalJSON() (data []byte, err error) {
|
||||
// Shadow real type for regular unmarshalling
|
||||
type RawTorrent Torrent
|
||||
tmp := &struct {
|
||||
ActivityDate *int64 `json:"activityDate"`
|
||||
AddedDate *int64 `json:"addedDate"`
|
||||
DateCreated *int64 `json:"dateCreated"`
|
||||
DoneDate *int64 `json:"doneDate"`
|
||||
SecondsSeeding *int64 `json:"secondsSeeding"`
|
||||
StartDate *int64 `json:"startDate"`
|
||||
Wanted []int64 `json:"wanted"` // boolean in number form
|
||||
*RawTorrent
|
||||
}{
|
||||
RawTorrent: (*RawTorrent)(t),
|
||||
}
|
||||
// Timestamps & Duration
|
||||
if t.ActivityDate != nil {
|
||||
ad := t.ActivityDate.Unix()
|
||||
tmp.ActivityDate = &ad
|
||||
}
|
||||
if t.AddedDate != nil {
|
||||
ad := t.AddedDate.Unix()
|
||||
tmp.AddedDate = &ad
|
||||
}
|
||||
if t.DateCreated != nil {
|
||||
dc := t.DateCreated.Unix()
|
||||
tmp.DateCreated = &dc
|
||||
}
|
||||
if t.DoneDate != nil {
|
||||
dd := t.DoneDate.Unix()
|
||||
tmp.DoneDate = &dd
|
||||
}
|
||||
if t.SecondsSeeding != nil {
|
||||
ss := int64(*t.SecondsSeeding / time.Second)
|
||||
tmp.SecondsSeeding = &ss
|
||||
}
|
||||
if t.StartDate != nil {
|
||||
st := t.StartDate.Unix()
|
||||
tmp.StartDate = &st
|
||||
}
|
||||
// Boolean as number
|
||||
if t.Wanted != nil {
|
||||
tmp.Wanted = make([]int64, len(t.Wanted))
|
||||
for index, value := range t.Wanted {
|
||||
if value {
|
||||
tmp.Wanted[index] = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
// Marshall original values within the tmp payload
|
||||
return json.Marshal(&tmp)
|
||||
}
|
||||
|
||||
// TorrentFile represent one file from a Torrent.
|
||||
// https://github.com/transmission/transmission/blob/2.9x/extras/rpc-spec.txt#L236
|
||||
type TorrentFile struct {
|
||||
BytesCompleted int64 `json:"bytesCompleted"`
|
||||
Length int64 `json:"length"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
// TorrentFileStat represents the metadata of a torrent's file.
|
||||
// https://github.com/transmission/transmission/blob/2.9x/extras/rpc-spec.txt#L242
|
||||
type TorrentFileStat struct {
|
||||
BytesCompleted int64 `json:"bytesCompleted"`
|
||||
Wanted bool `json:"wanted"`
|
||||
Priority int64 `json:"priority"`
|
||||
}
|
||||
|
||||
// Peer represent a peer metadata of a torrent's peer list.
|
||||
// https://github.com/transmission/transmission/blob/2.9x/extras/rpc-spec.txt#L250
|
||||
type Peer struct {
|
||||
Address string `json:"address"`
|
||||
ClientName string `json:"clientName"`
|
||||
ClientIsChoked bool `json:"clientIsChoked"`
|
||||
ClientIsint64erested bool `json:"clientIsint64erested"`
|
||||
FlagStr string `json:"flagStr"`
|
||||
IsDownloadingFrom bool `json:"isDownloadingFrom"`
|
||||
IsEncrypted bool `json:"isEncrypted"`
|
||||
IsIncoming bool `json:"isIncoming"`
|
||||
IsUploadingTo bool `json:"isUploadingTo"`
|
||||
IsUTP bool `json:"isUTP"`
|
||||
PeerIsChoked bool `json:"peerIsChoked"`
|
||||
PeerIsint64erested bool `json:"peerIsint64erested"`
|
||||
Port int64 `json:"port"`
|
||||
Progress float64 `json:"progress"`
|
||||
RateToClient int64 `json:"rateToClient"` // B/s
|
||||
RateToPeer int64 `json:"rateToPeer"` // B/s
|
||||
}
|
||||
|
||||
// ConvertDownloadSpeed will return the download speed from peer as cunits.Bits/second
|
||||
func (p *Peer) ConvertDownloadSpeed() (speed cunits.Bits) {
|
||||
return cunits.ImportInByte(float64(p.RateToClient))
|
||||
}
|
||||
|
||||
// ConvertUploadSpeed will return the upload speed to peer as cunits.Bits/second
|
||||
func (p *Peer) ConvertUploadSpeed() (speed cunits.Bits) {
|
||||
return cunits.ImportInByte(float64(p.RateToPeer))
|
||||
}
|
||||
|
||||
// TorrentPeersFrom represents the peers statistics of a torrent.
|
||||
// https://github.com/transmission/transmission/blob/2.9x/extras/rpc-spec.txt#L269
|
||||
type TorrentPeersFrom struct {
|
||||
FromCache int64 `json:"fromCache"`
|
||||
FromDHT int64 `json:"fromDht"`
|
||||
FromIncoming int64 `json:"fromIncoming"`
|
||||
FromLPD int64 `json:"fromLpd"`
|
||||
FromLTEP int64 `json:"fromLtep"`
|
||||
FromPEX int64 `json:"fromPex"`
|
||||
FromTracker int64 `json:"fromTracker"`
|
||||
}
|
||||
|
||||
// SeedRatioMode represents a torrent current seeding mode
|
||||
type SeedRatioMode int64
|
||||
|
||||
const (
|
||||
// SeedRatioModeGlobal represents the use of the global ratio for a torrent
|
||||
SeedRatioModeGlobal SeedRatioMode = 0
|
||||
// SeedRatioModeCustom represents the use of a custom ratio for a torrent
|
||||
SeedRatioModeCustom SeedRatioMode = 1
|
||||
// SeedRatioModeNoRatio represents the absence of ratio for a torrent
|
||||
SeedRatioModeNoRatio SeedRatioMode = 2
|
||||
)
|
||||
|
||||
func (srm SeedRatioMode) String() string {
|
||||
switch srm {
|
||||
case SeedRatioModeGlobal:
|
||||
return "global"
|
||||
case SeedRatioModeCustom:
|
||||
return "custom"
|
||||
case SeedRatioModeNoRatio:
|
||||
return "no ratio"
|
||||
default:
|
||||
return "<unknown>"
|
||||
}
|
||||
}
|
||||
|
||||
// GoString implements the GoStringer interface from the stdlib fmt package
|
||||
func (srm SeedRatioMode) GoString() string {
|
||||
switch srm {
|
||||
case SeedRatioModeGlobal:
|
||||
return fmt.Sprintf("global (%d)", srm)
|
||||
case SeedRatioModeCustom:
|
||||
return fmt.Sprintf("custom (%d)", srm)
|
||||
case SeedRatioModeNoRatio:
|
||||
return fmt.Sprintf("no ratio (%d)", srm)
|
||||
default:
|
||||
return fmt.Sprintf("<unknown> (%d)", srm)
|
||||
}
|
||||
}
|
||||
|
||||
// TorrentStatus binds torrent status to a status code
|
||||
type TorrentStatus int64
|
||||
|
||||
const (
|
||||
// TorrentStatusStopped represents a stopped torrent
|
||||
TorrentStatusStopped TorrentStatus = 0
|
||||
// TorrentStatusCheckWait represents a torrent queued for files checking
|
||||
TorrentStatusCheckWait TorrentStatus = 1
|
||||
// TorrentStatusCheck represents a torrent which files are currently checked
|
||||
TorrentStatusCheck TorrentStatus = 2
|
||||
// TorrentStatusDownloadWait represents a torrent queue to download
|
||||
TorrentStatusDownloadWait TorrentStatus = 3
|
||||
// TorrentStatusDownload represents a torrent currently downloading
|
||||
TorrentStatusDownload TorrentStatus = 4
|
||||
// TorrentStatusSeedWait represents a torrent queued to seed
|
||||
TorrentStatusSeedWait TorrentStatus = 5
|
||||
// TorrentStatusSeed represents a torrent currently seeding
|
||||
TorrentStatusSeed TorrentStatus = 6
|
||||
// TorrentStatusIsolated represents a torrent which can't find peers
|
||||
TorrentStatusIsolated TorrentStatus = 7
|
||||
)
|
||||
|
||||
func (status TorrentStatus) String() string {
|
||||
switch status {
|
||||
case TorrentStatusStopped:
|
||||
return "stopped"
|
||||
case TorrentStatusCheckWait:
|
||||
return "waiting to check files"
|
||||
case TorrentStatusCheck:
|
||||
return "checking files"
|
||||
case TorrentStatusDownloadWait:
|
||||
return "waiting to download"
|
||||
case TorrentStatusDownload:
|
||||
return "downloading"
|
||||
case TorrentStatusSeedWait:
|
||||
return "waiting to seed"
|
||||
case TorrentStatusSeed:
|
||||
return "seeding"
|
||||
case TorrentStatusIsolated:
|
||||
return "can't find peers"
|
||||
default:
|
||||
return "<unknown>"
|
||||
}
|
||||
}
|
||||
|
||||
// GoString implements the GoStringer interface from the stdlib fmt package
|
||||
func (status TorrentStatus) GoString() string {
|
||||
switch status {
|
||||
case TorrentStatusStopped:
|
||||
return fmt.Sprintf("stopped (%d)", status)
|
||||
case TorrentStatusCheckWait:
|
||||
return fmt.Sprintf("waiting to check files (%d)", status)
|
||||
case TorrentStatusCheck:
|
||||
return fmt.Sprintf("checking files (%d)", status)
|
||||
case TorrentStatusDownloadWait:
|
||||
return fmt.Sprintf("waiting to download (%d)", status)
|
||||
case TorrentStatusDownload:
|
||||
return fmt.Sprintf("downloading (%d)", status)
|
||||
case TorrentStatusSeedWait:
|
||||
return fmt.Sprintf("waiting to seed (%d)", status)
|
||||
case TorrentStatusSeed:
|
||||
return fmt.Sprintf("seeding (%d)", status)
|
||||
case TorrentStatusIsolated:
|
||||
return fmt.Sprintf("can't find peers (%d)", status)
|
||||
default:
|
||||
return fmt.Sprintf("<unknown> (%d)", status)
|
||||
}
|
||||
}
|
||||
|
||||
// Tracker represent the base data of a torrent's tracker.
|
||||
// https://github.com/transmission/transmission/blob/2.9x/extras/rpc-spec.txt#L289
|
||||
type Tracker struct {
|
||||
Announce string `json:"announce"`
|
||||
ID int64 `json:"id"`
|
||||
Scrape string `json:"scrape"`
|
||||
Tier int64 `json:"tier"`
|
||||
}
|
||||
|
||||
// TrackerStats represent the extended data of a torrent's tracker.
|
||||
// https://github.com/transmission/transmission/blob/2.9x/extras/rpc-spec.txt#L296
|
||||
type TrackerStats struct {
|
||||
Announce string `json:"announce"`
|
||||
AnnounceState int64 `json:"announceState"`
|
||||
DownloadCount int64 `json:"downloadCount"`
|
||||
HasAnnounced bool `json:"hasAnnounced"`
|
||||
HasScraped bool `json:"hasScraped"`
|
||||
Host string `json:"host"`
|
||||
ID int64 `json:"id"`
|
||||
IsBackup bool `json:"isBackup"`
|
||||
LastAnnouncePeerCount int64 `json:"lastAnnouncePeerCount"`
|
||||
LastAnnounceResult string `json:"lastAnnounceResult"`
|
||||
LastAnnounceStartTime time.Time `json:"lastAnnounceStartTime"`
|
||||
LastAnnounceSucceeded bool `json:"lastAnnounceSucceeded"`
|
||||
LastAnnounceTime time.Time `json:"lastAnnounceTime"`
|
||||
LastAnnounceTimedOut bool `json:"lastAnnounceTimedOut"`
|
||||
LastScrapeResult string `json:"lastScrapeResult"`
|
||||
LastScrapeStartTime time.Time `json:"lastScrapeStartTime"`
|
||||
LastScrapeSucceeded bool `json:"lastScrapeSucceeded"`
|
||||
LastScrapeTime time.Time `json:"lastScrapeTime"`
|
||||
LastScrapeTimedOut bool `json:"lastScrapeTimedOut"` // should be boolean but number. Will be converter in UnmarshalJSON
|
||||
LeecherCount int64 `json:"leecherCount"`
|
||||
NextAnnounceTime time.Time `json:"nextAnnounceTime"`
|
||||
NextScrapeTime time.Time `json:"nextScrapeTime"`
|
||||
Scrape string `json:"scrape"`
|
||||
ScrapeState int64 `json:"scrapeState"`
|
||||
SeederCount int64 `json:"seederCount"`
|
||||
Tier int64 `json:"tier"`
|
||||
}
|
||||
|
||||
// UnmarshalJSON allows to convert timestamps to golang time.Time values.
|
||||
func (ts *TrackerStats) UnmarshalJSON(data []byte) (err error) {
|
||||
// Shadow real type for regular unmarshalling
|
||||
type RawTrackerStats TrackerStats
|
||||
tmp := struct {
|
||||
LastAnnounceStartTime int64 `json:"lastAnnounceStartTime"`
|
||||
LastAnnounceTime int64 `json:"lastAnnounceTime"`
|
||||
LastScrapeStartTime int64 `json:"lastScrapeStartTime"`
|
||||
LastScrapeTime int64 `json:"lastScrapeTime"`
|
||||
LastScrapeTimedOut int64 `json:"lastScrapeTimedOut"`
|
||||
NextAnnounceTime int64 `json:"nextAnnounceTime"`
|
||||
NextScrapeTime int64 `json:"nextScrapeTime"`
|
||||
*RawTrackerStats
|
||||
}{
|
||||
RawTrackerStats: (*RawTrackerStats)(ts),
|
||||
}
|
||||
// Unmarshall (with timestamps as number)
|
||||
if err = json.Unmarshal(data, &tmp); err != nil {
|
||||
return
|
||||
}
|
||||
// Convert to real boolean
|
||||
if tmp.LastScrapeTimedOut == 1 {
|
||||
ts.LastScrapeTimedOut = true
|
||||
} else if tmp.LastScrapeTimedOut != 0 {
|
||||
return fmt.Errorf("can't convert 'lastScrapeTimedOut' value '%v' into boolean", tmp.LastScrapeTimedOut)
|
||||
}
|
||||
// Create the real time value from the timestamps
|
||||
ts.LastAnnounceStartTime = time.Unix(tmp.LastAnnounceStartTime, 0)
|
||||
ts.LastAnnounceTime = time.Unix(tmp.LastAnnounceTime, 0)
|
||||
ts.LastScrapeStartTime = time.Unix(tmp.LastScrapeStartTime, 0)
|
||||
ts.LastScrapeTime = time.Unix(tmp.LastScrapeTime, 0)
|
||||
ts.NextAnnounceTime = time.Unix(tmp.NextAnnounceTime, 0)
|
||||
ts.NextScrapeTime = time.Unix(tmp.NextScrapeTime, 0)
|
||||
return
|
||||
}
|
||||
|
||||
// MarshalJSON allows to convert back golang values to original payload values.
|
||||
func (ts *TrackerStats) MarshalJSON() (data []byte, err error) {
|
||||
// Shadow real type for regular unmarshalling
|
||||
type RawTrackerStats TrackerStats
|
||||
tmp := struct {
|
||||
LastAnnounceStartTime int64 `json:"lastAnnounceStartTime"`
|
||||
LastAnnounceTime int64 `json:"lastAnnounceTime"`
|
||||
LastScrapeStartTime int64 `json:"lastScrapeStartTime"`
|
||||
LastScrapeTime int64 `json:"lastScrapeTime"`
|
||||
LastScrapeTimedOut int64 `json:"lastScrapeTimedOut"`
|
||||
NextAnnounceTime int64 `json:"nextAnnounceTime"`
|
||||
NextScrapeTime int64 `json:"nextScrapeTime"`
|
||||
*RawTrackerStats
|
||||
}{
|
||||
LastAnnounceStartTime: ts.LastAnnounceStartTime.Unix(),
|
||||
LastAnnounceTime: ts.LastAnnounceTime.Unix(),
|
||||
LastScrapeStartTime: ts.LastScrapeStartTime.Unix(),
|
||||
LastScrapeTime: ts.LastScrapeTime.Unix(),
|
||||
NextAnnounceTime: ts.NextAnnounceTime.Unix(),
|
||||
NextScrapeTime: ts.NextScrapeTime.Unix(),
|
||||
RawTrackerStats: (*RawTrackerStats)(ts),
|
||||
}
|
||||
// Convert real bool to its number form
|
||||
if ts.LastScrapeTimedOut {
|
||||
tmp.LastScrapeTimedOut = 1
|
||||
}
|
||||
// MarshalJSON allows to convert back golang values to original payload values
|
||||
return json.Marshal(&tmp)
|
||||
}
|
||||
152
vendor/github.com/hekmon/transmissionrpc/torrent_action_requests.go
generated
vendored
Normal file
152
vendor/github.com/hekmon/transmissionrpc/torrent_action_requests.go
generated
vendored
Normal file
@@ -0,0 +1,152 @@
|
||||
package transmissionrpc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
/*
|
||||
Torrent Action Requests
|
||||
https://github.com/transmission/transmission/blob/2.9x/extras/rpc-spec.txt#L86
|
||||
*/
|
||||
|
||||
type torrentActionIDsParam struct {
|
||||
IDs []int64 `json:"ids,omitempty"`
|
||||
}
|
||||
|
||||
type torrentActionHashesParam struct {
|
||||
IDs []string `json:"ids,omitempty"`
|
||||
}
|
||||
|
||||
type torrentActionRecentlyActiveParam struct {
|
||||
IDs string `json:"ids"`
|
||||
}
|
||||
|
||||
// TorrentStartIDs starts torrent(s) which id is in the provided slice.
|
||||
// Can be one, can be several, can be all (if slice is empty or nil).
|
||||
func (c *Client) TorrentStartIDs(ids []int64) (err error) {
|
||||
if err = c.rpcCall("torrent-start", &torrentActionIDsParam{IDs: ids}, nil); err != nil {
|
||||
err = fmt.Errorf("'torrent-start' rpc method failed: %v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// TorrentStartHashes starts torrent(s) which hash is in the provided slice.
|
||||
// Can be one, can be several, can be all (if slice is empty or nil).
|
||||
func (c *Client) TorrentStartHashes(hashes []string) (err error) {
|
||||
if err = c.rpcCall("torrent-start", &torrentActionHashesParam{IDs: hashes}, nil); err != nil {
|
||||
err = fmt.Errorf("'torrent-start' rpc method failed: %v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// TorrentStartRecentlyActive starts torrent(s) which have been recently active.
|
||||
func (c *Client) TorrentStartRecentlyActive() (err error) {
|
||||
if err = c.rpcCall("torrent-start", &torrentActionRecentlyActiveParam{IDs: "recently-active"}, nil); err != nil {
|
||||
err = fmt.Errorf("'torrent-start' rpc method failed: %v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// TorrentStartNowIDs starts (now) torrent(s) which id is in the provided slice.
|
||||
// Can be one, can be several, can be all (if slice is empty or nil).
|
||||
func (c *Client) TorrentStartNowIDs(ids []int64) (err error) {
|
||||
if err = c.rpcCall("torrent-start-now", &torrentActionIDsParam{IDs: ids}, nil); err != nil {
|
||||
err = fmt.Errorf("'torrent-start-now' rpc method failed: %v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// TorrentStartNowHashes starts (now) torrent(s) which hash is in the provided slice.
|
||||
// Can be one, can be several, can be all (if slice is empty or nil).
|
||||
func (c *Client) TorrentStartNowHashes(hashes []string) (err error) {
|
||||
if err = c.rpcCall("torrent-start-now", &torrentActionHashesParam{IDs: hashes}, nil); err != nil {
|
||||
err = fmt.Errorf("'torrent-start-now' rpc method failed: %v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// TorrentStartNowRecentlyActive starts (now) torrent(s) which have been recently active.
|
||||
func (c *Client) TorrentStartNowRecentlyActive() (err error) {
|
||||
if err = c.rpcCall("torrent-start-now", &torrentActionRecentlyActiveParam{IDs: "recently-active"}, nil); err != nil {
|
||||
err = fmt.Errorf("'torrent-start-now' rpc method failed: %v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// TorrentStopIDs stops torrent(s) which id is in the provided slice.
|
||||
// Can be one, can be several, can be all (if slice is empty or nil).
|
||||
func (c *Client) TorrentStopIDs(ids []int64) (err error) {
|
||||
if err = c.rpcCall("torrent-stop", &torrentActionIDsParam{IDs: ids}, nil); err != nil {
|
||||
err = fmt.Errorf("'torrent-stop' rpc method failed: %v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// TorrentStopHashes stops torrent(s) which hash is in the provided slice.
|
||||
// Can be one, can be several, can be all (if slice is empty or nil).
|
||||
func (c *Client) TorrentStopHashes(hashes []string) (err error) {
|
||||
if err = c.rpcCall("torrent-stop", &torrentActionHashesParam{IDs: hashes}, nil); err != nil {
|
||||
err = fmt.Errorf("'torrent-stop' rpc method failed: %v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// TorrentStopRecentlyActive stops torrent(s) which have been recently active.
|
||||
func (c *Client) TorrentStopRecentlyActive() (err error) {
|
||||
if err = c.rpcCall("torrent-stop", &torrentActionRecentlyActiveParam{IDs: "recently-active"}, nil); err != nil {
|
||||
err = fmt.Errorf("'torrent-stop' rpc method failed: %v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// TorrentVerifyIDs verifys torrent(s) which id is in the provided slice.
|
||||
// Can be one, can be several, can be all (if slice is empty or nil).
|
||||
func (c *Client) TorrentVerifyIDs(ids []int64) (err error) {
|
||||
if err = c.rpcCall("torrent-verify", &torrentActionIDsParam{IDs: ids}, nil); err != nil {
|
||||
err = fmt.Errorf("'torrent-verify' rpc method failed: %v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// TorrentVerifyHashes verifys torrent(s) which hash is in the provided slice.
|
||||
// Can be one, can be several, can be all (if slice is empty or nil).
|
||||
func (c *Client) TorrentVerifyHashes(hashes []string) (err error) {
|
||||
if err = c.rpcCall("torrent-verify", &torrentActionHashesParam{IDs: hashes}, nil); err != nil {
|
||||
err = fmt.Errorf("'torrent-verify' rpc method failed: %v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// TorrentVerifyRecentlyActive verifys torrent(s) which have been recently active.
|
||||
func (c *Client) TorrentVerifyRecentlyActive() (err error) {
|
||||
if err = c.rpcCall("torrent-verify", &torrentActionRecentlyActiveParam{IDs: "recently-active"}, nil); err != nil {
|
||||
err = fmt.Errorf("'torrent-verify' rpc method failed: %v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// TorrentReannounceIDs reannounces torrent(s) which id is in the provided slice.
|
||||
// Can be one, can be several, can be all (if slice is empty or nil).
|
||||
func (c *Client) TorrentReannounceIDs(ids []int64) (err error) {
|
||||
if err = c.rpcCall("torrent-reannounce", &torrentActionIDsParam{IDs: ids}, nil); err != nil {
|
||||
err = fmt.Errorf("'torrent-reannounce' rpc method failed: %v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// TorrentReannounceHashes reannounces torrent(s) which hash is in the provided slice.
|
||||
// Can be one, can be several, can be all (if slice is empty or nil).
|
||||
func (c *Client) TorrentReannounceHashes(hashes []string) (err error) {
|
||||
if err = c.rpcCall("torrent-reannounce", &torrentActionHashesParam{IDs: hashes}, nil); err != nil {
|
||||
err = fmt.Errorf("'torrent-reannounce' rpc method failed: %v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// TorrentReannounceRecentlyActive reannounces torrent(s) which have been recently active.
|
||||
func (c *Client) TorrentReannounceRecentlyActive() (err error) {
|
||||
if err = c.rpcCall("torrent-reannounce", &torrentActionRecentlyActiveParam{IDs: "recently-active"}, nil); err != nil {
|
||||
err = fmt.Errorf("'torrent-reannounce' rpc method failed: %v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
135
vendor/github.com/hekmon/transmissionrpc/torrent_add.go
generated
vendored
Normal file
135
vendor/github.com/hekmon/transmissionrpc/torrent_add.go
generated
vendored
Normal file
@@ -0,0 +1,135 @@
|
||||
package transmissionrpc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
/*
|
||||
Adding a Torrent
|
||||
https://github.com/transmission/transmission/blob/2.9x/extras/rpc-spec.txt#L371
|
||||
*/
|
||||
|
||||
// TorrentAddFile is wrapper to directly add a torrent file (it handles the base64 encoding
|
||||
// and payload generation). If successful (torrent added or duplicate) torrent return value
|
||||
// will only have HashString, ID and Name fields set up.
|
||||
func (c *Client) TorrentAddFile(filepath string) (torrent *Torrent, err error) {
|
||||
// Validate
|
||||
if filepath == "" {
|
||||
err = errors.New("filepath can't be empty")
|
||||
return
|
||||
}
|
||||
// Get base64 encoded file content
|
||||
b64, err := file2Base64(filepath)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("can't encode '%s' content as base64: %v", filepath, err)
|
||||
return
|
||||
}
|
||||
// Prepare and send payload
|
||||
return c.TorrentAdd(&TorrentAddPayload{MetaInfo: &b64})
|
||||
}
|
||||
|
||||
// TorrentAdd allows to send an Add payload. If successful (torrent added or duplicate) torrent
|
||||
// return value will only have HashString, ID and Name fields set up.
|
||||
// https://github.com/transmission/transmission/blob/2.9x/extras/rpc-spec.txt#L373
|
||||
func (c *Client) TorrentAdd(payload *TorrentAddPayload) (torrent *Torrent, err error) {
|
||||
// Validate
|
||||
if payload == nil {
|
||||
err = errors.New("payload can't be nil")
|
||||
return
|
||||
}
|
||||
if payload.Filename == nil && payload.MetaInfo == nil {
|
||||
err = errors.New("Filename and MetaInfo can't be both nil")
|
||||
return
|
||||
}
|
||||
// Send payload
|
||||
var result torrentAddAnswer
|
||||
if err = c.rpcCall("torrent-add", payload, &result); err != nil {
|
||||
err = fmt.Errorf("'torrent-add' rpc method failed: %v", err)
|
||||
return
|
||||
}
|
||||
// Extract results
|
||||
if result.TorrentAdded != nil {
|
||||
torrent = result.TorrentAdded
|
||||
} else if result.TorrentDuplicate != nil {
|
||||
torrent = result.TorrentDuplicate
|
||||
} else {
|
||||
err = errors.New("RPC call went fine but neither 'torrent-added' nor 'torrent-duplicate' result payload were found")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// TorrentAddPayload represents the data to send in order to add a torrent.
|
||||
// https://github.com/transmission/transmission/blob/2.9x/extras/rpc-spec.txt#L37762
|
||||
type TorrentAddPayload struct {
|
||||
Cookies *string `json:"cookies"` // pointer to a string of one or more cookies
|
||||
DownloadDir *string `json:"download-dir"` // path to download the torrent to
|
||||
Filename *string `json:"filename"` // filename or URL of the .torrent file
|
||||
MetaInfo *string `json:"metainfo"` // base64-encoded .torrent content
|
||||
Paused *bool `json:"paused"` // if true, don't start the torrent
|
||||
PeerLimit *int64 `json:"peer-limit"` // maximum number of peers
|
||||
BandwidthPriority *int64 `json:"bandwidthPriority"` // torrent's bandwidth tr_priority_t
|
||||
FilesWanted []int64 `json:"files-wanted"` // indices of file(s) to download
|
||||
FilesUnwanted []int64 `json:"files-unwanted"` // indices of file(s) to not download
|
||||
PriorityHigh []int64 `json:"priority-high"` // indices of high-priority file(s)
|
||||
PriorityLow []int64 `json:"priority-low"` // indices of low-priority file(s)
|
||||
PriorityNormal []int64 `json:"priority-normal"` // indices of normal-priority file(s)
|
||||
}
|
||||
|
||||
// MarshalJSON allows to marshall into JSON only the non nil fields.
|
||||
// It differs from 'omitempty' which also skip default values
|
||||
// (as 0 or false which can be valid here).
|
||||
func (tap *TorrentAddPayload) MarshalJSON() (data []byte, err error) {
|
||||
// Build a payload with only the non nil fields
|
||||
tspv := reflect.ValueOf(*tap)
|
||||
tspt := tspv.Type()
|
||||
cleanPayload := make(map[string]interface{}, tspt.NumField())
|
||||
var currentValue reflect.Value
|
||||
var currentStructField reflect.StructField
|
||||
for i := 0; i < tspv.NumField(); i++ {
|
||||
currentValue = tspv.Field(i)
|
||||
currentStructField = tspt.Field(i)
|
||||
if !currentValue.IsNil() {
|
||||
cleanPayload[currentStructField.Tag.Get("json")] = currentValue.Interface()
|
||||
}
|
||||
}
|
||||
// Marshall the clean payload
|
||||
return json.Marshal(cleanPayload)
|
||||
}
|
||||
|
||||
type torrentAddAnswer struct {
|
||||
TorrentAdded *Torrent `json:"torrent-added"`
|
||||
TorrentDuplicate *Torrent `json:"torrent-duplicate"`
|
||||
}
|
||||
|
||||
func file2Base64(filename string) (b64 string, err error) {
|
||||
// Try to open file
|
||||
file, err := os.Open(filename)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("can't open file: %v", err)
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
// Prepare encoder
|
||||
buffer := new(bytes.Buffer)
|
||||
encoder := base64.NewEncoder(base64.StdEncoding, buffer)
|
||||
// Stream file to the encoder
|
||||
if _, err = io.Copy(encoder, file); err != nil {
|
||||
err = fmt.Errorf("can't copy file content into the base64 encoder: %v", err)
|
||||
return
|
||||
}
|
||||
// Flush last bytes
|
||||
if err = encoder.Close(); err != nil {
|
||||
err = fmt.Errorf("can't flush last bytes of the base64 encoder: %v", err)
|
||||
return
|
||||
}
|
||||
// Get the string form
|
||||
b64 = buffer.String()
|
||||
return
|
||||
}
|
||||
105
vendor/github.com/hekmon/transmissionrpc/torrent_mutators.go
generated
vendored
Normal file
105
vendor/github.com/hekmon/transmissionrpc/torrent_mutators.go
generated
vendored
Normal file
@@ -0,0 +1,105 @@
|
||||
package transmissionrpc
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"time"
|
||||
)
|
||||
|
||||
/*
|
||||
Torrent Mutators
|
||||
https://github.com/transmission/transmission/blob/2.9x/extras/rpc-spec.txt#L105
|
||||
*/
|
||||
|
||||
// TorrentSet apply a list of mutator(s) to a list of torrent ids.
|
||||
// https://github.com/transmission/transmission/blob/2.9x/extras/rpc-spec.txt#L107
|
||||
func (c *Client) TorrentSet(payload *TorrentSetPayload) (err error) {
|
||||
// Validate
|
||||
if payload == nil {
|
||||
return errors.New("payload can't be nil")
|
||||
}
|
||||
if len(payload.IDs) == 0 {
|
||||
return errors.New("there must be at least one ID")
|
||||
}
|
||||
// Send payload
|
||||
if err = c.rpcCall("torrent-set", payload, nil); err != nil {
|
||||
err = fmt.Errorf("'torrent-set' rpc method failed: %v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// TorrentSetPayload contains all the mutators appliable on one torrent.
|
||||
// https://github.com/transmission/transmission/blob/2.9x/extras/rpc-spec.txt#L111
|
||||
type TorrentSetPayload struct {
|
||||
BandwidthPriority *int64 `json:"bandwidthPriority"` // this torrent's bandwidth tr_priority_t
|
||||
DownloadLimit *int64 `json:"downloadLimit"` // maximum download speed (KBps)
|
||||
DownloadLimited *bool `json:"downloadLimited"` // true if "downloadLimit" is honored
|
||||
FilesWanted []int64 `json:"files-wanted"` // indices of file(s) to download
|
||||
FilesUnwanted []int64 `json:"files-unwanted"` // indices of file(s) to not download
|
||||
HonorsSessionLimits *bool `json:"honorsSessionLimits"` // true if session upload limits are honored
|
||||
IDs []int64 `json:"ids"` // torrent list
|
||||
Location *string `json:"location"` // new location of the torrent's content
|
||||
Peerlimit *int64 `json:"peer-limit"` // maximum number of peers
|
||||
PriorityHigh []int64 `json:"priority-high"` // indices of high-priority file(s)
|
||||
PriorityLow []int64 `json:"priority-low"` // indices of low-priority file(s)
|
||||
PriorityNormal []int64 `json:"priority-normal"` // indices of normal-priority file(s)
|
||||
QueuePosition *int64 `json:"queuePosition"` // position of this torrent in its queue [0...n)
|
||||
SeedIdleLimit *time.Duration `json:"seedIdleLimit"` // torrent-level number of minutes of seeding inactivity
|
||||
SeedIdleMode *int64 `json:"seedIdleMode"` // which seeding inactivity to use
|
||||
SeedRatioLimit *float64 `json:"seedRatioLimit"` // torrent-level seeding ratio
|
||||
SeedRatioMode *SeedRatioMode `json:"seedRatioMode"` // which ratio mode to use
|
||||
TrackerAdd []string `json:"trackerAdd"` // strings of announce URLs to add
|
||||
TrackerRemove []int64 `json:"trackerRemove"` // ids of trackers to remove
|
||||
TrackerReplace []string `json:"trackerReplace"` // pairs of <trackerId/new announce URLs> (TODO: validate string value usable as is)
|
||||
UploadLimit *int64 `json:"uploadLimit"` // maximum upload speed (KBps)
|
||||
UploadLimited *bool `json:"uploadLimited"` // true if "uploadLimit" is honored
|
||||
}
|
||||
|
||||
// MarshalJSON allows to marshall into JSON only the non nil fields.
|
||||
// It differs from 'omitempty' which also skip default values
|
||||
// (as 0 or false which can be valid here).
|
||||
func (tsp *TorrentSetPayload) MarshalJSON() (data []byte, err error) {
|
||||
// Build an intermediary payload with base types
|
||||
type baseTorrentSetPayload TorrentSetPayload
|
||||
tmp := struct {
|
||||
SeedIdleLimit *int64 `json:"seedIdleLimit"`
|
||||
*baseTorrentSetPayload
|
||||
}{
|
||||
baseTorrentSetPayload: (*baseTorrentSetPayload)(tsp),
|
||||
}
|
||||
if tsp.SeedIdleLimit != nil {
|
||||
sil := int64(*tsp.SeedIdleLimit / time.Minute)
|
||||
tmp.SeedIdleLimit = &sil
|
||||
}
|
||||
// Build a payload with only the non nil fields
|
||||
tspv := reflect.ValueOf(tmp)
|
||||
tspt := tspv.Type()
|
||||
cleanPayload := make(map[string]interface{}, tspt.NumField())
|
||||
var currentValue, nestedStruct, currentNestedValue reflect.Value
|
||||
var currentStructField, currentNestedStructField reflect.StructField
|
||||
var j int
|
||||
for i := 0; i < tspv.NumField(); i++ {
|
||||
currentValue = tspv.Field(i)
|
||||
currentStructField = tspt.Field(i)
|
||||
if !currentValue.IsNil() {
|
||||
if currentStructField.Name == "baseTorrentSetPayload" {
|
||||
// inherited/nested struct
|
||||
nestedStruct = reflect.Indirect(currentValue)
|
||||
for j = 0; j < nestedStruct.NumField(); j++ {
|
||||
currentNestedValue = nestedStruct.Field(j)
|
||||
currentNestedStructField = nestedStruct.Type().Field(j)
|
||||
if !currentNestedValue.IsNil() {
|
||||
cleanPayload[currentNestedStructField.Tag.Get("json")] = currentNestedValue.Interface()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Overloaded field
|
||||
cleanPayload[currentStructField.Tag.Get("json")] = currentValue.Interface()
|
||||
}
|
||||
}
|
||||
}
|
||||
// Marshall the clean payload
|
||||
return json.Marshal(cleanPayload)
|
||||
}
|
||||
31
vendor/github.com/hekmon/transmissionrpc/torrent_remove.go
generated
vendored
Normal file
31
vendor/github.com/hekmon/transmissionrpc/torrent_remove.go
generated
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
package transmissionrpc
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
/*
|
||||
Removing a Torrent
|
||||
https://github.com/transmission/transmission/blob/2.9x/extras/rpc-spec.txt#L407
|
||||
*/
|
||||
|
||||
// TorrentRemove allows to delete one or more torrents only or with their data.
|
||||
func (c *Client) TorrentRemove(payload *TorrentRemovePayload) (err error) {
|
||||
// Validate
|
||||
if payload == nil {
|
||||
return errors.New("payload can't be nil")
|
||||
}
|
||||
// Send payload
|
||||
if err = c.rpcCall("torrent-remove", payload, nil); err != nil {
|
||||
return fmt.Errorf("'torrent-remove' rpc method failed: %v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// TorrentRemovePayload holds the torrent id(s) to delete with a data deletion flag.
|
||||
// https://github.com/transmission/transmission/blob/2.9x/extras/rpc-spec.txt#L413
|
||||
type TorrentRemovePayload struct {
|
||||
IDs []int64 `json:"ids"`
|
||||
DeleteLocalData bool `json:"delete-local-data"`
|
||||
}
|
||||
52
vendor/github.com/hekmon/transmissionrpc/torrent_rename_path.go
generated
vendored
Normal file
52
vendor/github.com/hekmon/transmissionrpc/torrent_rename_path.go
generated
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
package transmissionrpc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
/*
|
||||
Rename a torrent path
|
||||
https://github.com/transmission/transmission/blob/2.9x/extras/rpc-spec.txt#L438
|
||||
*/
|
||||
|
||||
// TorrentRenamePath allows to rename torrent name or path.
|
||||
// 'path' is the path to the file or folder that will be renamed.
|
||||
// 'name' the file or folder's new name
|
||||
// https://github.com/transmission/transmission/blob/2.9x/extras/rpc-spec.txt#L440
|
||||
func (c *Client) TorrentRenamePath(id int64, path, name string) (err error) {
|
||||
if err = c.rpcCall("torrent-rename-path", torrentRenamePathPayload{
|
||||
IDs: []int64{id},
|
||||
Path: path,
|
||||
Name: name,
|
||||
}, nil); err != nil {
|
||||
err = fmt.Errorf("'torrent-rename-path' rpc method failed: %v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// TorrentRenamePathHash allows to rename torrent name or path by its hash.
|
||||
// https://github.com/transmission/transmission/blob/2.9x/extras/rpc-spec.txt#L440
|
||||
func (c *Client) TorrentRenamePathHash(hash, path, name string) (err error) {
|
||||
if err = c.rpcCall("torrent-rename-path", torrentRenamePathHashPayload{
|
||||
Hashes: []string{hash},
|
||||
Path: path,
|
||||
Name: name,
|
||||
}, nil); err != nil {
|
||||
err = fmt.Errorf("'torrent-rename-path' rpc method failed: %v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// https://github.com/transmission/transmission/blob/2.9x/extras/rpc-spec.txt#L447
|
||||
type torrentRenamePathPayload struct {
|
||||
IDs []int64 `json:"ids"` // the torrent torrent list, as described in 3.1 (must only be 1 torrent)
|
||||
Path string `json:"path"` // the path to the file or folder that will be renamed
|
||||
Name string `json:"name"` // the file or folder's new name
|
||||
}
|
||||
|
||||
// https://github.com/transmission/transmission/blob/2.9x/extras/rpc-spec.txt#L447
|
||||
type torrentRenamePathHashPayload struct {
|
||||
Hashes []string `json:"ids"` // the torrent torrent list, as described in 3.1 (must only be 1 torrent)
|
||||
Path string `json:"path"` // the path to the file or folder that will be renamed
|
||||
Name string `json:"name"` // the file or folder's new name
|
||||
}
|
||||
54
vendor/github.com/hekmon/transmissionrpc/torrent_set_location.go
generated
vendored
Normal file
54
vendor/github.com/hekmon/transmissionrpc/torrent_set_location.go
generated
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
package transmissionrpc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
/*
|
||||
Moving a torrent
|
||||
https://github.com/transmission/transmission/blob/2.9x/extras/rpc-spec.txt#L421
|
||||
*/
|
||||
|
||||
// TorrentSetLocation allows to set a new location for one or more torrents.
|
||||
// 'location' is the new torrent location.
|
||||
// 'move' if true, move from previous location. Otherwise, search "location" for file.
|
||||
// https://github.com/transmission/transmission/blob/2.9x/extras/rpc-spec.txt#L423
|
||||
func (c *Client) TorrentSetLocation(id int64, location string, move bool) (err error) {
|
||||
if err = c.rpcCall("torrent-set-location", torrentSetLocationPayload{
|
||||
IDs: []int64{id},
|
||||
Location: location,
|
||||
Move: move,
|
||||
}, nil); err != nil {
|
||||
err = fmt.Errorf("'torrent-set-location' rpc method failed: %v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// TorrentSetLocationHash allows to set a new location for one or more torrents.
|
||||
// 'location' is the new torrent location.
|
||||
// 'move' if true, move from previous location. Otherwise, search "location" for file.
|
||||
// https://github.com/transmission/transmission/blob/2.9x/extras/rpc-spec.txt#L423
|
||||
func (c *Client) TorrentSetLocationHash(hash, location string, move bool) (err error) {
|
||||
if err = c.rpcCall("torrent-set-location", torrentSetLocationHashPayload{
|
||||
Hashes: []string{hash},
|
||||
Location: location,
|
||||
Move: move,
|
||||
}, nil); err != nil {
|
||||
err = fmt.Errorf("'torrent-set-location' rpc method failed: %v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// https://github.com/transmission/transmission/blob/2.9x/extras/rpc-spec.txt#L427
|
||||
type torrentSetLocationPayload struct {
|
||||
IDs []int64 `json:"ids"` // torrent list
|
||||
Location string `json:"location"` // the new torrent location
|
||||
Move bool `json:"move"` // if true, move from previous location. Otherwise, search "location" for files
|
||||
}
|
||||
|
||||
// https://github.com/transmission/transmission/blob/2.9x/extras/rpc-spec.txt#L427
|
||||
type torrentSetLocationHashPayload struct {
|
||||
Hashes []string `json:"ids"` // torrent list
|
||||
Location string `json:"location"` // the new torrent location
|
||||
Move bool `json:"move"` // if true, move from previous location. Otherwise, search "location" for files
|
||||
}
|
||||
Reference in New Issue
Block a user