Add pause/resume, enchance tora-cli
This commit is contained in:
@@ -1,6 +1,4 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
export GIT_CONFIG_GLOBAL=/dev/null
|
|
||||||
|
|
||||||
eval "$(devenv direnvrc)"
|
eval "$(devenv direnvrc)"
|
||||||
|
|
||||||
# You can pass flags to the devenv command
|
# You can pass flags to the devenv command
|
||||||
|
|||||||
@@ -8,3 +8,6 @@ devenv.local.yaml
|
|||||||
|
|
||||||
# pre-commit
|
# pre-commit
|
||||||
.pre-commit-config.yaml
|
.pre-commit-config.yaml
|
||||||
|
|
||||||
|
|
||||||
|
target/
|
||||||
|
|||||||
Generated
+25
@@ -472,6 +472,15 @@ version = "1.0.5"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570"
|
checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "colored"
|
||||||
|
version = "3.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "faf9468729b8cbcea668e36183cb69d317348c2e08e994829fb56ebfdfbaac34"
|
||||||
|
dependencies = [
|
||||||
|
"windows-sys 0.61.2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "compression-codecs"
|
name = "compression-codecs"
|
||||||
version = "0.4.38"
|
version = "0.4.38"
|
||||||
@@ -1827,6 +1836,18 @@ dependencies = [
|
|||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nix"
|
||||||
|
version = "0.31.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cf20d2fde8ff38632c426f1165ed7436270b44f199fc55284c38276f9db47c3d"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"cfg-if",
|
||||||
|
"cfg_aliases",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nonzero_ext"
|
name = "nonzero_ext"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
@@ -3169,7 +3190,10 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"clap",
|
"clap",
|
||||||
|
"colored",
|
||||||
"hyper-util",
|
"hyper-util",
|
||||||
|
"nix",
|
||||||
|
"serde_json",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tonic",
|
"tonic",
|
||||||
"tonic-health",
|
"tonic-health",
|
||||||
@@ -3195,6 +3219,7 @@ dependencies = [
|
|||||||
"anyhow",
|
"anyhow",
|
||||||
"clap",
|
"clap",
|
||||||
"librqbit",
|
"librqbit",
|
||||||
|
"librqbit-core",
|
||||||
"sqlx",
|
"sqlx",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-stream",
|
"tokio-stream",
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
version: v2
|
version: v2
|
||||||
plugins:
|
plugins:
|
||||||
- local: protoc-gen-prost
|
- local: protoc-gen-prost
|
||||||
out: ../crates/proto/src/generated
|
out: ./crates/proto/src/generated
|
||||||
opt:
|
opt:
|
||||||
- file_descriptor_set
|
- file_descriptor_set
|
||||||
- enable_type_names
|
- enable_type_names
|
||||||
- local: protoc-gen-prost-serde
|
- local: protoc-gen-prost-serde
|
||||||
out: ../crates/proto/src/generated
|
out: ./crates/proto/src/generated
|
||||||
- local: protoc-gen-tonic
|
- local: protoc-gen-tonic
|
||||||
out: ../crates/proto/src/generated
|
out: ./crates/proto/src/generated
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
version: v2
|
version: v2
|
||||||
modules:
|
modules:
|
||||||
- path: .
|
- path: proto
|
||||||
lint:
|
lint:
|
||||||
use:
|
use:
|
||||||
- STANDARD
|
- STANDARD
|
||||||
@@ -76,6 +76,89 @@ impl ::prost::Name for ListResponse {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
|
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
|
||||||
|
pub struct RemoveRequest {
|
||||||
|
#[prost(string, tag = "1")]
|
||||||
|
pub id: ::prost::alloc::string::String,
|
||||||
|
#[prost(bool, tag = "2")]
|
||||||
|
pub delete_files: bool,
|
||||||
|
}
|
||||||
|
impl ::prost::Name for RemoveRequest {
|
||||||
|
const NAME: &'static str = "RemoveRequest";
|
||||||
|
const PACKAGE: &'static str = "torrent";
|
||||||
|
fn full_name() -> ::prost::alloc::string::String {
|
||||||
|
"torrent.RemoveRequest".into()
|
||||||
|
}
|
||||||
|
fn type_url() -> ::prost::alloc::string::String {
|
||||||
|
"/torrent.RemoveRequest".into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)]
|
||||||
|
pub struct RemoveResponse {}
|
||||||
|
impl ::prost::Name for RemoveResponse {
|
||||||
|
const NAME: &'static str = "RemoveResponse";
|
||||||
|
const PACKAGE: &'static str = "torrent";
|
||||||
|
fn full_name() -> ::prost::alloc::string::String {
|
||||||
|
"torrent.RemoveResponse".into()
|
||||||
|
}
|
||||||
|
fn type_url() -> ::prost::alloc::string::String {
|
||||||
|
"/torrent.RemoveResponse".into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
|
||||||
|
pub struct PauseRequest {
|
||||||
|
#[prost(string, tag = "1")]
|
||||||
|
pub id: ::prost::alloc::string::String,
|
||||||
|
}
|
||||||
|
impl ::prost::Name for PauseRequest {
|
||||||
|
const NAME: &'static str = "PauseRequest";
|
||||||
|
const PACKAGE: &'static str = "torrent";
|
||||||
|
fn full_name() -> ::prost::alloc::string::String {
|
||||||
|
"torrent.PauseRequest".into()
|
||||||
|
}
|
||||||
|
fn type_url() -> ::prost::alloc::string::String {
|
||||||
|
"/torrent.PauseRequest".into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)]
|
||||||
|
pub struct PauseResponse {}
|
||||||
|
impl ::prost::Name for PauseResponse {
|
||||||
|
const NAME: &'static str = "PauseResponse";
|
||||||
|
const PACKAGE: &'static str = "torrent";
|
||||||
|
fn full_name() -> ::prost::alloc::string::String {
|
||||||
|
"torrent.PauseResponse".into()
|
||||||
|
}
|
||||||
|
fn type_url() -> ::prost::alloc::string::String {
|
||||||
|
"/torrent.PauseResponse".into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
|
||||||
|
pub struct ResumeRequest {
|
||||||
|
#[prost(string, tag = "1")]
|
||||||
|
pub id: ::prost::alloc::string::String,
|
||||||
|
}
|
||||||
|
impl ::prost::Name for ResumeRequest {
|
||||||
|
const NAME: &'static str = "ResumeRequest";
|
||||||
|
const PACKAGE: &'static str = "torrent";
|
||||||
|
fn full_name() -> ::prost::alloc::string::String {
|
||||||
|
"torrent.ResumeRequest".into()
|
||||||
|
}
|
||||||
|
fn type_url() -> ::prost::alloc::string::String {
|
||||||
|
"/torrent.ResumeRequest".into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)]
|
||||||
|
pub struct ResumeResponse {}
|
||||||
|
impl ::prost::Name for ResumeResponse {
|
||||||
|
const NAME: &'static str = "ResumeResponse";
|
||||||
|
const PACKAGE: &'static str = "torrent";
|
||||||
|
fn full_name() -> ::prost::alloc::string::String {
|
||||||
|
"torrent.ResumeResponse".into()
|
||||||
|
}
|
||||||
|
fn type_url() -> ::prost::alloc::string::String {
|
||||||
|
"/torrent.ResumeResponse".into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
|
||||||
pub struct TorrentStatus {
|
pub struct TorrentStatus {
|
||||||
#[prost(string, tag = "1")]
|
#[prost(string, tag = "1")]
|
||||||
pub id: ::prost::alloc::string::String,
|
pub id: ::prost::alloc::string::String,
|
||||||
@@ -95,6 +178,18 @@ pub struct TorrentStatus {
|
|||||||
pub state: i32,
|
pub state: i32,
|
||||||
#[prost(string, tag = "9")]
|
#[prost(string, tag = "9")]
|
||||||
pub error_message: ::prost::alloc::string::String,
|
pub error_message: ::prost::alloc::string::String,
|
||||||
|
#[prost(uint64, tag = "10")]
|
||||||
|
pub download_speed_bps: u64,
|
||||||
|
#[prost(uint64, tag = "11")]
|
||||||
|
pub eta_seconds: u64,
|
||||||
|
#[prost(uint64, tag = "12")]
|
||||||
|
pub uploaded_bytes: u64,
|
||||||
|
#[prost(uint64, tag = "13")]
|
||||||
|
pub upload_speed_bps: u64,
|
||||||
|
#[prost(uint32, tag = "14")]
|
||||||
|
pub peers: u32,
|
||||||
|
#[prost(uint32, tag = "15")]
|
||||||
|
pub seeds: u32,
|
||||||
}
|
}
|
||||||
impl ::prost::Name for TorrentStatus {
|
impl ::prost::Name for TorrentStatus {
|
||||||
const NAME: &'static str = "TorrentStatus";
|
const NAME: &'static str = "TorrentStatus";
|
||||||
@@ -146,7 +241,7 @@ impl State {
|
|||||||
}
|
}
|
||||||
/// Encoded file descriptor set for the `torrent` package
|
/// Encoded file descriptor set for the `torrent` package
|
||||||
pub const FILE_DESCRIPTOR_SET: &[u8] = &[
|
pub const FILE_DESCRIPTOR_SET: &[u8] = &[
|
||||||
0x0a, 0x94, 0x13, 0x0a, 0x0d, 0x74, 0x6f, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x2e, 0x70, 0x72, 0x6f,
|
0x0a, 0x90, 0x1f, 0x0a, 0x0d, 0x74, 0x6f, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x2e, 0x70, 0x72, 0x6f,
|
||||||
0x74, 0x6f, 0x12, 0x07, 0x74, 0x6f, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x22, 0x43, 0x0a, 0x0a, 0x41,
|
0x74, 0x6f, 0x12, 0x07, 0x74, 0x6f, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x22, 0x43, 0x0a, 0x0a, 0x41,
|
||||||
0x64, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x61, 0x67,
|
0x64, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x61, 0x67,
|
||||||
0x6e, 0x65, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6d, 0x61, 0x67, 0x6e, 0x65,
|
0x6e, 0x65, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6d, 0x61, 0x67, 0x6e, 0x65,
|
||||||
@@ -161,145 +256,241 @@ pub const FILE_DESCRIPTOR_SET: &[u8] = &[
|
|||||||
0x32, 0x0a, 0x08, 0x74, 0x6f, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28,
|
0x32, 0x0a, 0x08, 0x74, 0x6f, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28,
|
||||||
0x0b, 0x32, 0x16, 0x2e, 0x74, 0x6f, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x2e, 0x54, 0x6f, 0x72, 0x72,
|
0x0b, 0x32, 0x16, 0x2e, 0x74, 0x6f, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x2e, 0x54, 0x6f, 0x72, 0x72,
|
||||||
0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x08, 0x74, 0x6f, 0x72, 0x72, 0x65,
|
0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x08, 0x74, 0x6f, 0x72, 0x72, 0x65,
|
||||||
0x6e, 0x74, 0x73, 0x22, 0xa0, 0x02, 0x0a, 0x0d, 0x54, 0x6f, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x53,
|
0x6e, 0x74, 0x73, 0x22, 0x42, 0x0a, 0x0d, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x65, 0x71,
|
||||||
0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,
|
0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
|
||||||
0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x69, 0x6e, 0x66, 0x6f, 0x5f, 0x68, 0x61,
|
0x52, 0x02, 0x69, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x5f, 0x66,
|
||||||
0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x69, 0x6e, 0x66, 0x6f, 0x48, 0x61,
|
0x69, 0x6c, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x64, 0x65, 0x6c, 0x65,
|
||||||
0x73, 0x68, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09,
|
0x74, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x22, 0x10, 0x0a, 0x0e, 0x52, 0x65, 0x6d, 0x6f, 0x76,
|
||||||
0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
|
0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1e, 0x0a, 0x0c, 0x50, 0x61, 0x75,
|
||||||
0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x1f,
|
0x73, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18,
|
||||||
0x0a, 0x0b, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x05, 0x20,
|
0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x0f, 0x0a, 0x0d, 0x50, 0x61, 0x75,
|
||||||
0x01, 0x28, 0x09, 0x52, 0x0a, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x50, 0x61, 0x74, 0x68, 0x12,
|
0x73, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x0a, 0x0d, 0x52, 0x65,
|
||||||
0x1f, 0x0a, 0x0b, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x06,
|
0x73, 0x75, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69,
|
||||||
0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x42, 0x79, 0x74, 0x65, 0x73,
|
0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x10, 0x0a, 0x0e, 0x52,
|
||||||
0x12, 0x29, 0x0a, 0x10, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x65, 0x64, 0x5f, 0x62,
|
0x65, 0x73, 0x75, 0x6d, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xec, 0x03,
|
||||||
0x79, 0x74, 0x65, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x64, 0x6f, 0x77, 0x6e,
|
0x0a, 0x0d, 0x54, 0x6f, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12,
|
||||||
0x6c, 0x6f, 0x61, 0x64, 0x65, 0x64, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x24, 0x0a, 0x05, 0x73,
|
0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12,
|
||||||
0x74, 0x61, 0x74, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0e, 0x2e, 0x74, 0x6f, 0x72,
|
0x1b, 0x0a, 0x09, 0x69, 0x6e, 0x66, 0x6f, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01,
|
||||||
0x72, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74,
|
0x28, 0x09, 0x52, 0x08, 0x69, 0x6e, 0x66, 0x6f, 0x48, 0x61, 0x73, 0x68, 0x12, 0x12, 0x0a, 0x04,
|
||||||
0x65, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61,
|
0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65,
|
||||||
0x67, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x4d,
|
0x12, 0x16, 0x0a, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09,
|
||||||
0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2a, 0x61, 0x0a, 0x05, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12,
|
0x52, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x6f, 0x75, 0x74, 0x70,
|
||||||
0x15, 0x0a, 0x11, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49,
|
0x75, 0x74, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6f,
|
||||||
0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x50, 0x45, 0x4e, 0x44, 0x49, 0x4e,
|
0x75, 0x74, 0x70, 0x75, 0x74, 0x50, 0x61, 0x74, 0x68, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x6f, 0x74,
|
||||||
0x47, 0x10, 0x01, 0x12, 0x0f, 0x0a, 0x0b, 0x44, 0x4f, 0x57, 0x4e, 0x4c, 0x4f, 0x41, 0x44, 0x49,
|
0x61, 0x6c, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a,
|
||||||
0x4e, 0x47, 0x10, 0x02, 0x12, 0x0a, 0x0a, 0x06, 0x50, 0x41, 0x55, 0x53, 0x45, 0x44, 0x10, 0x03,
|
0x74, 0x6f, 0x74, 0x61, 0x6c, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x29, 0x0a, 0x10, 0x64, 0x6f,
|
||||||
0x12, 0x0c, 0x0a, 0x08, 0x46, 0x49, 0x4e, 0x49, 0x53, 0x48, 0x45, 0x44, 0x10, 0x04, 0x12, 0x09,
|
0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x65, 0x64, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x07,
|
||||||
0x0a, 0x05, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x05, 0x32, 0xab, 0x01, 0x0a, 0x08, 0x54, 0x6f,
|
0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x65, 0x64,
|
||||||
0x72, 0x72, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x30, 0x0a, 0x03, 0x41, 0x64, 0x64, 0x12, 0x13, 0x2e,
|
0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x24, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x08,
|
||||||
0x74, 0x6f, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x2e, 0x41, 0x64, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65,
|
0x20, 0x01, 0x28, 0x0e, 0x32, 0x0e, 0x2e, 0x74, 0x6f, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x2e, 0x53,
|
||||||
0x73, 0x74, 0x1a, 0x14, 0x2e, 0x74, 0x6f, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x2e, 0x41, 0x64, 0x64,
|
0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x65,
|
||||||
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x38, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74,
|
0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x09, 0x20, 0x01,
|
||||||
0x75, 0x73, 0x12, 0x16, 0x2e, 0x74, 0x6f, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x74, 0x61,
|
0x28, 0x09, 0x52, 0x0c, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65,
|
||||||
0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x74, 0x6f, 0x72,
|
0x12, 0x2c, 0x0a, 0x12, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x73, 0x70, 0x65,
|
||||||
0x72, 0x65, 0x6e, 0x74, 0x2e, 0x54, 0x6f, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74,
|
0x65, 0x64, 0x5f, 0x62, 0x70, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x04, 0x52, 0x10, 0x64, 0x6f,
|
||||||
0x75, 0x73, 0x12, 0x33, 0x0a, 0x04, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x14, 0x2e, 0x74, 0x6f, 0x72,
|
0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x70, 0x65, 0x65, 0x64, 0x42, 0x70, 0x73, 0x12, 0x1f,
|
||||||
0x72, 0x65, 0x6e, 0x74, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
|
0x0a, 0x0b, 0x65, 0x74, 0x61, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x0b, 0x20,
|
||||||
0x1a, 0x15, 0x2e, 0x74, 0x6f, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52,
|
0x01, 0x28, 0x04, 0x52, 0x0a, 0x65, 0x74, 0x61, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12,
|
||||||
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4a, 0xe5, 0x0c, 0x0a, 0x06, 0x12, 0x04, 0x00, 0x00,
|
0x25, 0x0a, 0x0e, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x65, 0x64, 0x5f, 0x62, 0x79, 0x74, 0x65,
|
||||||
0x34, 0x01, 0x0a, 0x08, 0x0a, 0x01, 0x0c, 0x12, 0x03, 0x00, 0x00, 0x12, 0x0a, 0x08, 0x0a, 0x01,
|
0x73, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x65,
|
||||||
0x02, 0x12, 0x03, 0x02, 0x00, 0x10, 0x0a, 0x0a, 0x0a, 0x02, 0x06, 0x00, 0x12, 0x04, 0x04, 0x00,
|
0x64, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x28, 0x0a, 0x10, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64,
|
||||||
0x0b, 0x01, 0x0a, 0x0a, 0x0a, 0x03, 0x06, 0x00, 0x01, 0x12, 0x03, 0x04, 0x08, 0x10, 0x0a, 0x49,
|
0x5f, 0x73, 0x70, 0x65, 0x65, 0x64, 0x5f, 0x62, 0x70, 0x73, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x04,
|
||||||
0x0a, 0x04, 0x06, 0x00, 0x02, 0x00, 0x12, 0x03, 0x06, 0x02, 0x2c, 0x1a, 0x3c, 0x20, 0x41, 0x64,
|
0x52, 0x0e, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x70, 0x65, 0x65, 0x64, 0x42, 0x70, 0x73,
|
||||||
0x64, 0x20, 0x61, 0x20, 0x74, 0x6f, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x20, 0x66, 0x72, 0x6f, 0x6d,
|
0x12, 0x14, 0x0a, 0x05, 0x70, 0x65, 0x65, 0x72, 0x73, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0d, 0x52,
|
||||||
0x20, 0x61, 0x20, 0x6d, 0x61, 0x67, 0x6e, 0x65, 0x74, 0x20, 0x6c, 0x69, 0x6e, 0x6b, 0x20, 0x61,
|
0x05, 0x70, 0x65, 0x65, 0x72, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x65, 0x65, 0x64, 0x73, 0x18,
|
||||||
0x6e, 0x64, 0x20, 0x73, 0x74, 0x61, 0x72, 0x74, 0x20, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61,
|
0x0f, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x73, 0x65, 0x65, 0x64, 0x73, 0x2a, 0x61, 0x0a, 0x05,
|
||||||
0x64, 0x69, 0x6e, 0x67, 0x20, 0x69, 0x74, 0x2e, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02,
|
0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x15, 0x0a, 0x11, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x55,
|
||||||
0x00, 0x01, 0x12, 0x03, 0x06, 0x06, 0x09, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x00, 0x02,
|
0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07,
|
||||||
0x12, 0x03, 0x06, 0x0a, 0x14, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x00, 0x03, 0x12, 0x03,
|
0x50, 0x45, 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x0f, 0x0a, 0x0b, 0x44, 0x4f, 0x57,
|
||||||
0x06, 0x1f, 0x2a, 0x0a, 0x3a, 0x0a, 0x04, 0x06, 0x00, 0x02, 0x01, 0x12, 0x03, 0x08, 0x02, 0x34,
|
0x4e, 0x4c, 0x4f, 0x41, 0x44, 0x49, 0x4e, 0x47, 0x10, 0x02, 0x12, 0x0a, 0x0a, 0x06, 0x50, 0x41,
|
||||||
0x1a, 0x2d, 0x20, 0x47, 0x65, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x75, 0x72, 0x72, 0x65,
|
0x55, 0x53, 0x45, 0x44, 0x10, 0x03, 0x12, 0x0c, 0x0a, 0x08, 0x46, 0x49, 0x4e, 0x49, 0x53, 0x48,
|
||||||
0x6e, 0x74, 0x20, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x20, 0x73,
|
0x45, 0x44, 0x10, 0x04, 0x12, 0x09, 0x0a, 0x05, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x05, 0x32,
|
||||||
0x69, 0x6e, 0x67, 0x6c, 0x65, 0x20, 0x74, 0x6f, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x2e, 0x0a, 0x0a,
|
0xd9, 0x02, 0x0a, 0x08, 0x54, 0x6f, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x30, 0x0a, 0x03,
|
||||||
0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x01, 0x01, 0x12, 0x03, 0x08, 0x06, 0x0c, 0x0a, 0x0c, 0x0a,
|
0x41, 0x64, 0x64, 0x12, 0x13, 0x2e, 0x74, 0x6f, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x2e, 0x41, 0x64,
|
||||||
0x05, 0x06, 0x00, 0x02, 0x01, 0x02, 0x12, 0x03, 0x08, 0x0d, 0x1a, 0x0a, 0x0c, 0x0a, 0x05, 0x06,
|
0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x74, 0x6f, 0x72, 0x72, 0x65,
|
||||||
0x00, 0x02, 0x01, 0x03, 0x12, 0x03, 0x08, 0x25, 0x32, 0x0a, 0x35, 0x0a, 0x04, 0x06, 0x00, 0x02,
|
0x6e, 0x74, 0x2e, 0x41, 0x64, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x38,
|
||||||
0x02, 0x12, 0x03, 0x0a, 0x02, 0x2f, 0x1a, 0x28, 0x20, 0x4c, 0x69, 0x73, 0x74, 0x20, 0x61, 0x6c,
|
0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x16, 0x2e, 0x74, 0x6f, 0x72, 0x72, 0x65,
|
||||||
0x6c, 0x20, 0x74, 0x6f, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x73, 0x20, 0x6b, 0x6e, 0x6f, 0x77, 0x6e,
|
0x6e, 0x74, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
|
||||||
0x20, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x0a,
|
0x1a, 0x16, 0x2e, 0x74, 0x6f, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x2e, 0x54, 0x6f, 0x72, 0x72, 0x65,
|
||||||
0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x02, 0x01, 0x12, 0x03, 0x0a, 0x06, 0x0a, 0x0a, 0x0c,
|
0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x33, 0x0a, 0x04, 0x4c, 0x69, 0x73, 0x74,
|
||||||
0x0a, 0x05, 0x06, 0x00, 0x02, 0x02, 0x02, 0x12, 0x03, 0x0a, 0x0b, 0x16, 0x0a, 0x0c, 0x0a, 0x05,
|
0x12, 0x14, 0x2e, 0x74, 0x6f, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52,
|
||||||
0x06, 0x00, 0x02, 0x02, 0x03, 0x12, 0x03, 0x0a, 0x21, 0x2d, 0x0a, 0x0a, 0x0a, 0x02, 0x04, 0x00,
|
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x74, 0x6f, 0x72, 0x72, 0x65, 0x6e, 0x74,
|
||||||
0x12, 0x04, 0x0d, 0x00, 0x11, 0x01, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x00, 0x01, 0x12, 0x03, 0x0d,
|
0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x39, 0x0a,
|
||||||
0x08, 0x12, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, 0x05, 0x12, 0x03, 0x0e, 0x02, 0x08,
|
0x06, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x12, 0x16, 0x2e, 0x74, 0x6f, 0x72, 0x72, 0x65, 0x6e,
|
||||||
0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x00, 0x02, 0x00, 0x12, 0x03, 0x0e, 0x02, 0x14, 0x0a, 0x0c, 0x0a,
|
0x74, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
|
||||||
0x05, 0x04, 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, 0x0e, 0x09, 0x0f, 0x0a, 0x0c, 0x0a, 0x05, 0x04,
|
0x17, 0x2e, 0x74, 0x6f, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65,
|
||||||
0x00, 0x02, 0x00, 0x03, 0x12, 0x03, 0x0e, 0x12, 0x13, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02,
|
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x36, 0x0a, 0x05, 0x50, 0x61, 0x75, 0x73,
|
||||||
0x01, 0x05, 0x12, 0x03, 0x10, 0x02, 0x08, 0x0a, 0x53, 0x0a, 0x04, 0x04, 0x00, 0x02, 0x01, 0x12,
|
0x65, 0x12, 0x15, 0x2e, 0x74, 0x6f, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x61, 0x75, 0x73,
|
||||||
0x03, 0x10, 0x02, 0x18, 0x1a, 0x46, 0x20, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x20,
|
0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x74, 0x6f, 0x72, 0x72, 0x65,
|
||||||
0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x20, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79,
|
0x6e, 0x74, 0x2e, 0x50, 0x61, 0x75, 0x73, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
|
||||||
0x20, 0x6f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x2e, 0x20, 0x45, 0x6d, 0x70, 0x74, 0x79,
|
0x12, 0x39, 0x0a, 0x06, 0x52, 0x65, 0x73, 0x75, 0x6d, 0x65, 0x12, 0x16, 0x2e, 0x74, 0x6f, 0x72,
|
||||||
0x20, 0x6d, 0x65, 0x61, 0x6e, 0x73, 0x20, 0x75, 0x73, 0x65, 0x20, 0x74, 0x6f, 0x72, 0x61, 0x64,
|
0x72, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x65, 0x73, 0x75, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65,
|
||||||
0x27, 0x73, 0x20, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x2e, 0x0a, 0x0a, 0x0c, 0x0a, 0x05,
|
0x73, 0x74, 0x1a, 0x17, 0x2e, 0x74, 0x6f, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x65, 0x73,
|
||||||
0x04, 0x00, 0x02, 0x01, 0x01, 0x12, 0x03, 0x10, 0x09, 0x13, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00,
|
0x75, 0x6d, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4a, 0xad, 0x14, 0x0a, 0x06,
|
||||||
0x02, 0x01, 0x03, 0x12, 0x03, 0x10, 0x16, 0x17, 0x0a, 0x0a, 0x0a, 0x02, 0x04, 0x01, 0x12, 0x04,
|
0x12, 0x04, 0x00, 0x00, 0x53, 0x01, 0x0a, 0x08, 0x0a, 0x01, 0x0c, 0x12, 0x03, 0x00, 0x00, 0x12,
|
||||||
0x13, 0x00, 0x15, 0x01, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x01, 0x01, 0x12, 0x03, 0x13, 0x08, 0x13,
|
0x0a, 0x08, 0x0a, 0x01, 0x02, 0x12, 0x03, 0x02, 0x00, 0x10, 0x0a, 0x0a, 0x0a, 0x02, 0x06, 0x00,
|
||||||
0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x00, 0x05, 0x12, 0x03, 0x14, 0x02, 0x08, 0x0a, 0x0b,
|
0x12, 0x04, 0x04, 0x00, 0x11, 0x01, 0x0a, 0x0a, 0x0a, 0x03, 0x06, 0x00, 0x01, 0x12, 0x03, 0x04,
|
||||||
0x0a, 0x04, 0x04, 0x01, 0x02, 0x00, 0x12, 0x03, 0x14, 0x02, 0x10, 0x0a, 0x0c, 0x0a, 0x05, 0x04,
|
0x08, 0x10, 0x0a, 0x5a, 0x0a, 0x04, 0x06, 0x00, 0x02, 0x00, 0x12, 0x03, 0x06, 0x02, 0x2c, 0x1a,
|
||||||
0x01, 0x02, 0x00, 0x01, 0x12, 0x03, 0x14, 0x09, 0x0b, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02,
|
0x4d, 0x20, 0x41, 0x64, 0x64, 0x20, 0x61, 0x20, 0x74, 0x6f, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x20,
|
||||||
0x00, 0x03, 0x12, 0x03, 0x14, 0x0e, 0x0f, 0x0a, 0x0a, 0x0a, 0x02, 0x04, 0x02, 0x12, 0x04, 0x17,
|
0x66, 0x72, 0x6f, 0x6d, 0x20, 0x61, 0x20, 0x6d, 0x61, 0x67, 0x6e, 0x65, 0x74, 0x20, 0x6c, 0x69,
|
||||||
0x00, 0x19, 0x01, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x02, 0x01, 0x12, 0x03, 0x17, 0x08, 0x15, 0x0a,
|
0x6e, 0x6b, 0x20, 0x6f, 0x72, 0x20, 0x2e, 0x74, 0x6f, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x20, 0x66,
|
||||||
0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x00, 0x05, 0x12, 0x03, 0x18, 0x02, 0x08, 0x0a, 0x0b, 0x0a,
|
0x69, 0x6c, 0x65, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x73, 0x74, 0x61, 0x72, 0x74, 0x20, 0x64, 0x6f,
|
||||||
0x04, 0x04, 0x02, 0x02, 0x00, 0x12, 0x03, 0x18, 0x02, 0x10, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02,
|
0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x69, 0x74, 0x2e, 0x0a, 0x0a, 0x0c,
|
||||||
0x02, 0x00, 0x01, 0x12, 0x03, 0x18, 0x09, 0x0b, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x00,
|
0x0a, 0x05, 0x06, 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, 0x06, 0x06, 0x09, 0x0a, 0x0c, 0x0a, 0x05,
|
||||||
0x03, 0x12, 0x03, 0x18, 0x0e, 0x0f, 0x0a, 0x09, 0x0a, 0x02, 0x04, 0x03, 0x12, 0x03, 0x1b, 0x00,
|
0x06, 0x00, 0x02, 0x00, 0x02, 0x12, 0x03, 0x06, 0x0a, 0x14, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00,
|
||||||
0x16, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x03, 0x01, 0x12, 0x03, 0x1b, 0x08, 0x13, 0x0a, 0x0a, 0x0a,
|
0x02, 0x00, 0x03, 0x12, 0x03, 0x06, 0x1f, 0x2a, 0x0a, 0x3a, 0x0a, 0x04, 0x06, 0x00, 0x02, 0x01,
|
||||||
0x02, 0x04, 0x04, 0x12, 0x04, 0x1d, 0x00, 0x1f, 0x01, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x04, 0x01,
|
0x12, 0x03, 0x08, 0x02, 0x34, 0x1a, 0x2d, 0x20, 0x47, 0x65, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20,
|
||||||
0x12, 0x03, 0x1d, 0x08, 0x14, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x00, 0x04, 0x12, 0x03,
|
0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x20, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x20, 0x6f,
|
||||||
0x1e, 0x02, 0x0a, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x04, 0x02, 0x00, 0x12, 0x03, 0x1e, 0x02, 0x26,
|
0x66, 0x20, 0x61, 0x20, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x20, 0x74, 0x6f, 0x72, 0x72, 0x65,
|
||||||
0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x00, 0x06, 0x12, 0x03, 0x1e, 0x0b, 0x18, 0x0a, 0x0c,
|
0x6e, 0x74, 0x2e, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x01, 0x01, 0x12, 0x03, 0x08,
|
||||||
0x0a, 0x05, 0x04, 0x04, 0x02, 0x00, 0x01, 0x12, 0x03, 0x1e, 0x19, 0x21, 0x0a, 0x0c, 0x0a, 0x05,
|
0x06, 0x0c, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x01, 0x02, 0x12, 0x03, 0x08, 0x0d, 0x1a,
|
||||||
0x04, 0x04, 0x02, 0x00, 0x03, 0x12, 0x03, 0x1e, 0x24, 0x25, 0x0a, 0x0a, 0x0a, 0x02, 0x05, 0x00,
|
0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x01, 0x03, 0x12, 0x03, 0x08, 0x25, 0x32, 0x0a, 0x35,
|
||||||
0x12, 0x04, 0x21, 0x00, 0x28, 0x01, 0x0a, 0x0a, 0x0a, 0x03, 0x05, 0x00, 0x01, 0x12, 0x03, 0x21,
|
0x0a, 0x04, 0x06, 0x00, 0x02, 0x02, 0x12, 0x03, 0x0a, 0x02, 0x2f, 0x1a, 0x28, 0x20, 0x4c, 0x69,
|
||||||
0x05, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, 0x22, 0x02, 0x13,
|
0x73, 0x74, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x74, 0x6f, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x73, 0x20,
|
||||||
0x0a, 0x0b, 0x0a, 0x04, 0x05, 0x00, 0x02, 0x00, 0x12, 0x03, 0x22, 0x02, 0x18, 0x0a, 0x0c, 0x0a,
|
0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x64, 0x61, 0x65,
|
||||||
0x05, 0x05, 0x00, 0x02, 0x00, 0x02, 0x12, 0x03, 0x22, 0x16, 0x17, 0x0a, 0x0c, 0x0a, 0x05, 0x05,
|
0x6d, 0x6f, 0x6e, 0x2e, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x02, 0x01, 0x12, 0x03,
|
||||||
0x00, 0x02, 0x01, 0x01, 0x12, 0x03, 0x23, 0x02, 0x09, 0x0a, 0x0b, 0x0a, 0x04, 0x05, 0x00, 0x02,
|
0x0a, 0x06, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x02, 0x02, 0x12, 0x03, 0x0a, 0x0b,
|
||||||
0x01, 0x12, 0x03, 0x23, 0x02, 0x0e, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x00, 0x02, 0x01, 0x02, 0x12,
|
0x16, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x02, 0x03, 0x12, 0x03, 0x0a, 0x21, 0x2d, 0x0a,
|
||||||
0x03, 0x23, 0x0c, 0x0d, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x00, 0x02, 0x02, 0x01, 0x12, 0x03, 0x24,
|
0x2e, 0x0a, 0x04, 0x06, 0x00, 0x02, 0x03, 0x12, 0x03, 0x0c, 0x02, 0x35, 0x1a, 0x21, 0x20, 0x52,
|
||||||
0x02, 0x0d, 0x0a, 0x0b, 0x0a, 0x04, 0x05, 0x00, 0x02, 0x02, 0x12, 0x03, 0x24, 0x02, 0x12, 0x0a,
|
0x65, 0x6d, 0x6f, 0x76, 0x65, 0x20, 0x61, 0x20, 0x74, 0x6f, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x20,
|
||||||
0x0c, 0x0a, 0x05, 0x05, 0x00, 0x02, 0x02, 0x02, 0x12, 0x03, 0x24, 0x10, 0x11, 0x0a, 0x0c, 0x0a,
|
0x66, 0x72, 0x6f, 0x6d, 0x20, 0x74, 0x72, 0x61, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x2e, 0x0a, 0x0a,
|
||||||
0x05, 0x05, 0x00, 0x02, 0x03, 0x01, 0x12, 0x03, 0x25, 0x02, 0x08, 0x0a, 0x0b, 0x0a, 0x04, 0x05,
|
0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x03, 0x01, 0x12, 0x03, 0x0c, 0x06, 0x0c, 0x0a, 0x0c, 0x0a,
|
||||||
0x00, 0x02, 0x03, 0x12, 0x03, 0x25, 0x02, 0x0d, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x00, 0x02, 0x03,
|
0x05, 0x06, 0x00, 0x02, 0x03, 0x02, 0x12, 0x03, 0x0c, 0x0d, 0x1a, 0x0a, 0x0c, 0x0a, 0x05, 0x06,
|
||||||
0x02, 0x12, 0x03, 0x25, 0x0b, 0x0c, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x00, 0x02, 0x04, 0x01, 0x12,
|
0x00, 0x02, 0x03, 0x03, 0x12, 0x03, 0x0c, 0x25, 0x33, 0x0a, 0x2b, 0x0a, 0x04, 0x06, 0x00, 0x02,
|
||||||
0x03, 0x26, 0x02, 0x0a, 0x0a, 0x0b, 0x0a, 0x04, 0x05, 0x00, 0x02, 0x04, 0x12, 0x03, 0x26, 0x02,
|
0x04, 0x12, 0x03, 0x0e, 0x02, 0x32, 0x1a, 0x1e, 0x20, 0x50, 0x61, 0x75, 0x73, 0x65, 0x20, 0x61,
|
||||||
0x0f, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x00, 0x02, 0x04, 0x02, 0x12, 0x03, 0x26, 0x0d, 0x0e, 0x0a,
|
0x20, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x74, 0x6f, 0x72,
|
||||||
0x0c, 0x0a, 0x05, 0x05, 0x00, 0x02, 0x05, 0x01, 0x12, 0x03, 0x27, 0x02, 0x07, 0x0a, 0x0b, 0x0a,
|
0x72, 0x65, 0x6e, 0x74, 0x2e, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x04, 0x01, 0x12,
|
||||||
0x04, 0x05, 0x00, 0x02, 0x05, 0x12, 0x03, 0x27, 0x02, 0x0c, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x00,
|
0x03, 0x0e, 0x06, 0x0b, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x04, 0x02, 0x12, 0x03, 0x0e,
|
||||||
0x02, 0x05, 0x02, 0x12, 0x03, 0x27, 0x0a, 0x0b, 0x0a, 0x0a, 0x0a, 0x02, 0x04, 0x05, 0x12, 0x04,
|
0x0c, 0x18, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x04, 0x03, 0x12, 0x03, 0x0e, 0x23, 0x30,
|
||||||
0x2a, 0x00, 0x34, 0x01, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x05, 0x01, 0x12, 0x03, 0x2a, 0x08, 0x15,
|
0x0a, 0x27, 0x0a, 0x04, 0x06, 0x00, 0x02, 0x05, 0x12, 0x03, 0x10, 0x02, 0x35, 0x1a, 0x1a, 0x20,
|
||||||
0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x00, 0x05, 0x12, 0x03, 0x2b, 0x02, 0x08, 0x0a, 0x0b,
|
0x52, 0x65, 0x73, 0x75, 0x6d, 0x65, 0x20, 0x61, 0x20, 0x70, 0x61, 0x75, 0x73, 0x65, 0x64, 0x20,
|
||||||
0x0a, 0x04, 0x04, 0x05, 0x02, 0x00, 0x12, 0x03, 0x2b, 0x02, 0x10, 0x0a, 0x0c, 0x0a, 0x05, 0x04,
|
0x74, 0x6f, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x2e, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02,
|
||||||
0x05, 0x02, 0x00, 0x01, 0x12, 0x03, 0x2b, 0x09, 0x0b, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02,
|
0x05, 0x01, 0x12, 0x03, 0x10, 0x06, 0x0c, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x05, 0x02,
|
||||||
0x00, 0x03, 0x12, 0x03, 0x2b, 0x0e, 0x0f, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x01, 0x05,
|
0x12, 0x03, 0x10, 0x0d, 0x1a, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x05, 0x03, 0x12, 0x03,
|
||||||
0x12, 0x03, 0x2c, 0x02, 0x08, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x05, 0x02, 0x01, 0x12, 0x03, 0x2c,
|
0x10, 0x25, 0x33, 0x0a, 0x0a, 0x0a, 0x02, 0x04, 0x00, 0x12, 0x04, 0x13, 0x00, 0x17, 0x01, 0x0a,
|
||||||
0x02, 0x17, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x01, 0x01, 0x12, 0x03, 0x2c, 0x09, 0x12,
|
0x0a, 0x0a, 0x03, 0x04, 0x00, 0x01, 0x12, 0x03, 0x13, 0x08, 0x12, 0x0a, 0x0c, 0x0a, 0x05, 0x04,
|
||||||
0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x01, 0x03, 0x12, 0x03, 0x2c, 0x15, 0x16, 0x0a, 0x0c,
|
0x00, 0x02, 0x00, 0x05, 0x12, 0x03, 0x14, 0x02, 0x08, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x00, 0x02,
|
||||||
0x0a, 0x05, 0x04, 0x05, 0x02, 0x02, 0x05, 0x12, 0x03, 0x2d, 0x02, 0x08, 0x0a, 0x0b, 0x0a, 0x04,
|
0x00, 0x12, 0x03, 0x14, 0x02, 0x14, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, 0x01, 0x12,
|
||||||
0x04, 0x05, 0x02, 0x02, 0x12, 0x03, 0x2d, 0x02, 0x12, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02,
|
0x03, 0x14, 0x09, 0x0f, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, 0x03, 0x12, 0x03, 0x14,
|
||||||
0x02, 0x01, 0x12, 0x03, 0x2d, 0x09, 0x0d, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x02, 0x03,
|
0x12, 0x13, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x01, 0x05, 0x12, 0x03, 0x16, 0x02, 0x08,
|
||||||
0x12, 0x03, 0x2d, 0x10, 0x11, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x03, 0x05, 0x12, 0x03,
|
0x0a, 0x53, 0x0a, 0x04, 0x04, 0x00, 0x02, 0x01, 0x12, 0x03, 0x16, 0x02, 0x18, 0x1a, 0x46, 0x20,
|
||||||
0x2e, 0x02, 0x08, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x05, 0x02, 0x03, 0x12, 0x03, 0x2e, 0x02, 0x14,
|
0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x20, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x20,
|
||||||
0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x03, 0x01, 0x12, 0x03, 0x2e, 0x09, 0x0f, 0x0a, 0x0c,
|
0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x20, 0x6f, 0x76, 0x65, 0x72, 0x72, 0x69,
|
||||||
0x0a, 0x05, 0x04, 0x05, 0x02, 0x03, 0x03, 0x12, 0x03, 0x2e, 0x12, 0x13, 0x0a, 0x0c, 0x0a, 0x05,
|
0x64, 0x65, 0x2e, 0x20, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x20, 0x6d, 0x65, 0x61, 0x6e, 0x73, 0x20,
|
||||||
0x04, 0x05, 0x02, 0x04, 0x05, 0x12, 0x03, 0x2f, 0x02, 0x08, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x05,
|
0x75, 0x73, 0x65, 0x20, 0x74, 0x6f, 0x72, 0x61, 0x64, 0x27, 0x73, 0x20, 0x64, 0x65, 0x66, 0x61,
|
||||||
0x02, 0x04, 0x12, 0x03, 0x2f, 0x02, 0x19, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x04, 0x01,
|
0x75, 0x6c, 0x74, 0x2e, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x01, 0x01, 0x12, 0x03,
|
||||||
0x12, 0x03, 0x2f, 0x09, 0x14, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x04, 0x03, 0x12, 0x03,
|
0x16, 0x09, 0x13, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x01, 0x03, 0x12, 0x03, 0x16, 0x16,
|
||||||
0x2f, 0x17, 0x18, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x05, 0x05, 0x12, 0x03, 0x30, 0x02,
|
0x17, 0x0a, 0x0a, 0x0a, 0x02, 0x04, 0x01, 0x12, 0x04, 0x19, 0x00, 0x1b, 0x01, 0x0a, 0x0a, 0x0a,
|
||||||
0x08, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x05, 0x02, 0x05, 0x12, 0x03, 0x30, 0x02, 0x19, 0x0a, 0x0c,
|
0x03, 0x04, 0x01, 0x01, 0x12, 0x03, 0x19, 0x08, 0x13, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02,
|
||||||
0x0a, 0x05, 0x04, 0x05, 0x02, 0x05, 0x01, 0x12, 0x03, 0x30, 0x09, 0x14, 0x0a, 0x0c, 0x0a, 0x05,
|
0x00, 0x05, 0x12, 0x03, 0x1a, 0x02, 0x08, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x01, 0x02, 0x00, 0x12,
|
||||||
0x04, 0x05, 0x02, 0x05, 0x03, 0x12, 0x03, 0x30, 0x17, 0x18, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05,
|
0x03, 0x1a, 0x02, 0x10, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x00, 0x01, 0x12, 0x03, 0x1a,
|
||||||
0x02, 0x06, 0x05, 0x12, 0x03, 0x31, 0x02, 0x08, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x05, 0x02, 0x06,
|
0x09, 0x0b, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x00, 0x03, 0x12, 0x03, 0x1a, 0x0e, 0x0f,
|
||||||
0x12, 0x03, 0x31, 0x02, 0x1e, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x06, 0x01, 0x12, 0x03,
|
0x0a, 0x0a, 0x0a, 0x02, 0x04, 0x02, 0x12, 0x04, 0x1d, 0x00, 0x1f, 0x01, 0x0a, 0x0a, 0x0a, 0x03,
|
||||||
0x31, 0x09, 0x19, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x06, 0x03, 0x12, 0x03, 0x31, 0x1c,
|
0x04, 0x02, 0x01, 0x12, 0x03, 0x1d, 0x08, 0x15, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x00,
|
||||||
0x1d, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x07, 0x06, 0x12, 0x03, 0x32, 0x02, 0x07, 0x0a,
|
0x05, 0x12, 0x03, 0x1e, 0x02, 0x08, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x02, 0x02, 0x00, 0x12, 0x03,
|
||||||
0x0b, 0x0a, 0x04, 0x04, 0x05, 0x02, 0x07, 0x12, 0x03, 0x32, 0x02, 0x12, 0x0a, 0x0c, 0x0a, 0x05,
|
0x1e, 0x02, 0x10, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x00, 0x01, 0x12, 0x03, 0x1e, 0x09,
|
||||||
0x04, 0x05, 0x02, 0x07, 0x01, 0x12, 0x03, 0x32, 0x08, 0x0d, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05,
|
0x0b, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x00, 0x03, 0x12, 0x03, 0x1e, 0x0e, 0x0f, 0x0a,
|
||||||
0x02, 0x07, 0x03, 0x12, 0x03, 0x32, 0x10, 0x11, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x08,
|
0x09, 0x0a, 0x02, 0x04, 0x03, 0x12, 0x03, 0x21, 0x00, 0x16, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x03,
|
||||||
0x05, 0x12, 0x03, 0x33, 0x02, 0x08, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x05, 0x02, 0x08, 0x12, 0x03,
|
0x01, 0x12, 0x03, 0x21, 0x08, 0x13, 0x0a, 0x0a, 0x0a, 0x02, 0x04, 0x04, 0x12, 0x04, 0x23, 0x00,
|
||||||
0x33, 0x02, 0x1b, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x08, 0x01, 0x12, 0x03, 0x33, 0x09,
|
0x25, 0x01, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x04, 0x01, 0x12, 0x03, 0x23, 0x08, 0x14, 0x0a, 0x0c,
|
||||||
0x16, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x08, 0x03, 0x12, 0x03, 0x33, 0x19, 0x1a, 0x62,
|
0x0a, 0x05, 0x04, 0x04, 0x02, 0x00, 0x04, 0x12, 0x03, 0x24, 0x02, 0x0a, 0x0a, 0x0b, 0x0a, 0x04,
|
||||||
0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
0x04, 0x04, 0x02, 0x00, 0x12, 0x03, 0x24, 0x02, 0x26, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02,
|
||||||
|
0x00, 0x06, 0x12, 0x03, 0x24, 0x0b, 0x18, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x00, 0x01,
|
||||||
|
0x12, 0x03, 0x24, 0x19, 0x21, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x00, 0x03, 0x12, 0x03,
|
||||||
|
0x24, 0x24, 0x25, 0x0a, 0x0a, 0x0a, 0x02, 0x04, 0x05, 0x12, 0x04, 0x27, 0x00, 0x2a, 0x01, 0x0a,
|
||||||
|
0x0a, 0x0a, 0x03, 0x04, 0x05, 0x01, 0x12, 0x03, 0x27, 0x08, 0x15, 0x0a, 0x0c, 0x0a, 0x05, 0x04,
|
||||||
|
0x05, 0x02, 0x00, 0x05, 0x12, 0x03, 0x28, 0x02, 0x08, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x05, 0x02,
|
||||||
|
0x00, 0x12, 0x03, 0x28, 0x02, 0x10, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x00, 0x01, 0x12,
|
||||||
|
0x03, 0x28, 0x09, 0x0b, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x00, 0x03, 0x12, 0x03, 0x28,
|
||||||
|
0x0e, 0x0f, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x01, 0x05, 0x12, 0x03, 0x29, 0x02, 0x06,
|
||||||
|
0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x05, 0x02, 0x01, 0x12, 0x03, 0x29, 0x02, 0x18, 0x0a, 0x0c, 0x0a,
|
||||||
|
0x05, 0x04, 0x05, 0x02, 0x01, 0x01, 0x12, 0x03, 0x29, 0x07, 0x13, 0x0a, 0x0c, 0x0a, 0x05, 0x04,
|
||||||
|
0x05, 0x02, 0x01, 0x03, 0x12, 0x03, 0x29, 0x16, 0x17, 0x0a, 0x09, 0x0a, 0x02, 0x04, 0x06, 0x12,
|
||||||
|
0x03, 0x2c, 0x00, 0x19, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x06, 0x01, 0x12, 0x03, 0x2c, 0x08, 0x16,
|
||||||
|
0x0a, 0x0a, 0x0a, 0x02, 0x04, 0x07, 0x12, 0x04, 0x2e, 0x00, 0x30, 0x01, 0x0a, 0x0a, 0x0a, 0x03,
|
||||||
|
0x04, 0x07, 0x01, 0x12, 0x03, 0x2e, 0x08, 0x14, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x07, 0x02, 0x00,
|
||||||
|
0x05, 0x12, 0x03, 0x2f, 0x02, 0x08, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x07, 0x02, 0x00, 0x12, 0x03,
|
||||||
|
0x2f, 0x02, 0x10, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x07, 0x02, 0x00, 0x01, 0x12, 0x03, 0x2f, 0x09,
|
||||||
|
0x0b, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x07, 0x02, 0x00, 0x03, 0x12, 0x03, 0x2f, 0x0e, 0x0f, 0x0a,
|
||||||
|
0x09, 0x0a, 0x02, 0x04, 0x08, 0x12, 0x03, 0x32, 0x00, 0x18, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x08,
|
||||||
|
0x01, 0x12, 0x03, 0x32, 0x08, 0x15, 0x0a, 0x0a, 0x0a, 0x02, 0x04, 0x09, 0x12, 0x04, 0x34, 0x00,
|
||||||
|
0x36, 0x01, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x09, 0x01, 0x12, 0x03, 0x34, 0x08, 0x15, 0x0a, 0x0c,
|
||||||
|
0x0a, 0x05, 0x04, 0x09, 0x02, 0x00, 0x05, 0x12, 0x03, 0x35, 0x02, 0x08, 0x0a, 0x0b, 0x0a, 0x04,
|
||||||
|
0x04, 0x09, 0x02, 0x00, 0x12, 0x03, 0x35, 0x02, 0x10, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x09, 0x02,
|
||||||
|
0x00, 0x01, 0x12, 0x03, 0x35, 0x09, 0x0b, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x09, 0x02, 0x00, 0x03,
|
||||||
|
0x12, 0x03, 0x35, 0x0e, 0x0f, 0x0a, 0x09, 0x0a, 0x02, 0x04, 0x0a, 0x12, 0x03, 0x38, 0x00, 0x19,
|
||||||
|
0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x0a, 0x01, 0x12, 0x03, 0x38, 0x08, 0x16, 0x0a, 0x0a, 0x0a, 0x02,
|
||||||
|
0x05, 0x00, 0x12, 0x04, 0x3a, 0x00, 0x41, 0x01, 0x0a, 0x0a, 0x0a, 0x03, 0x05, 0x00, 0x01, 0x12,
|
||||||
|
0x03, 0x3a, 0x05, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, 0x3b,
|
||||||
|
0x02, 0x13, 0x0a, 0x0b, 0x0a, 0x04, 0x05, 0x00, 0x02, 0x00, 0x12, 0x03, 0x3b, 0x02, 0x18, 0x0a,
|
||||||
|
0x0c, 0x0a, 0x05, 0x05, 0x00, 0x02, 0x00, 0x02, 0x12, 0x03, 0x3b, 0x16, 0x17, 0x0a, 0x0c, 0x0a,
|
||||||
|
0x05, 0x05, 0x00, 0x02, 0x01, 0x01, 0x12, 0x03, 0x3c, 0x02, 0x09, 0x0a, 0x0b, 0x0a, 0x04, 0x05,
|
||||||
|
0x00, 0x02, 0x01, 0x12, 0x03, 0x3c, 0x02, 0x0e, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x00, 0x02, 0x01,
|
||||||
|
0x02, 0x12, 0x03, 0x3c, 0x0c, 0x0d, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x00, 0x02, 0x02, 0x01, 0x12,
|
||||||
|
0x03, 0x3d, 0x02, 0x0d, 0x0a, 0x0b, 0x0a, 0x04, 0x05, 0x00, 0x02, 0x02, 0x12, 0x03, 0x3d, 0x02,
|
||||||
|
0x12, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x00, 0x02, 0x02, 0x02, 0x12, 0x03, 0x3d, 0x10, 0x11, 0x0a,
|
||||||
|
0x0c, 0x0a, 0x05, 0x05, 0x00, 0x02, 0x03, 0x01, 0x12, 0x03, 0x3e, 0x02, 0x08, 0x0a, 0x0b, 0x0a,
|
||||||
|
0x04, 0x05, 0x00, 0x02, 0x03, 0x12, 0x03, 0x3e, 0x02, 0x0d, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x00,
|
||||||
|
0x02, 0x03, 0x02, 0x12, 0x03, 0x3e, 0x0b, 0x0c, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x00, 0x02, 0x04,
|
||||||
|
0x01, 0x12, 0x03, 0x3f, 0x02, 0x0a, 0x0a, 0x0b, 0x0a, 0x04, 0x05, 0x00, 0x02, 0x04, 0x12, 0x03,
|
||||||
|
0x3f, 0x02, 0x0f, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x00, 0x02, 0x04, 0x02, 0x12, 0x03, 0x3f, 0x0d,
|
||||||
|
0x0e, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x00, 0x02, 0x05, 0x01, 0x12, 0x03, 0x40, 0x02, 0x07, 0x0a,
|
||||||
|
0x0b, 0x0a, 0x04, 0x05, 0x00, 0x02, 0x05, 0x12, 0x03, 0x40, 0x02, 0x0c, 0x0a, 0x0c, 0x0a, 0x05,
|
||||||
|
0x05, 0x00, 0x02, 0x05, 0x02, 0x12, 0x03, 0x40, 0x0a, 0x0b, 0x0a, 0x0a, 0x0a, 0x02, 0x04, 0x0b,
|
||||||
|
0x12, 0x04, 0x43, 0x00, 0x53, 0x01, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x0b, 0x01, 0x12, 0x03, 0x43,
|
||||||
|
0x08, 0x15, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x00, 0x05, 0x12, 0x03, 0x44, 0x02, 0x08,
|
||||||
|
0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x0b, 0x02, 0x00, 0x12, 0x03, 0x44, 0x02, 0x10, 0x0a, 0x0c, 0x0a,
|
||||||
|
0x05, 0x04, 0x0b, 0x02, 0x00, 0x01, 0x12, 0x03, 0x44, 0x09, 0x0b, 0x0a, 0x0c, 0x0a, 0x05, 0x04,
|
||||||
|
0x0b, 0x02, 0x00, 0x03, 0x12, 0x03, 0x44, 0x0e, 0x0f, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0b, 0x02,
|
||||||
|
0x01, 0x05, 0x12, 0x03, 0x45, 0x02, 0x08, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x0b, 0x02, 0x01, 0x12,
|
||||||
|
0x03, 0x45, 0x02, 0x17, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x01, 0x01, 0x12, 0x03, 0x45,
|
||||||
|
0x09, 0x12, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x01, 0x03, 0x12, 0x03, 0x45, 0x15, 0x16,
|
||||||
|
0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x02, 0x05, 0x12, 0x03, 0x46, 0x02, 0x08, 0x0a, 0x0b,
|
||||||
|
0x0a, 0x04, 0x04, 0x0b, 0x02, 0x02, 0x12, 0x03, 0x46, 0x02, 0x12, 0x0a, 0x0c, 0x0a, 0x05, 0x04,
|
||||||
|
0x0b, 0x02, 0x02, 0x01, 0x12, 0x03, 0x46, 0x09, 0x0d, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0b, 0x02,
|
||||||
|
0x02, 0x03, 0x12, 0x03, 0x46, 0x10, 0x11, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x03, 0x05,
|
||||||
|
0x12, 0x03, 0x47, 0x02, 0x08, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x0b, 0x02, 0x03, 0x12, 0x03, 0x47,
|
||||||
|
0x02, 0x14, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x03, 0x01, 0x12, 0x03, 0x47, 0x09, 0x0f,
|
||||||
|
0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x03, 0x03, 0x12, 0x03, 0x47, 0x12, 0x13, 0x0a, 0x0c,
|
||||||
|
0x0a, 0x05, 0x04, 0x0b, 0x02, 0x04, 0x05, 0x12, 0x03, 0x48, 0x02, 0x08, 0x0a, 0x0b, 0x0a, 0x04,
|
||||||
|
0x04, 0x0b, 0x02, 0x04, 0x12, 0x03, 0x48, 0x02, 0x19, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0b, 0x02,
|
||||||
|
0x04, 0x01, 0x12, 0x03, 0x48, 0x09, 0x14, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x04, 0x03,
|
||||||
|
0x12, 0x03, 0x48, 0x17, 0x18, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x05, 0x05, 0x12, 0x03,
|
||||||
|
0x49, 0x02, 0x08, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x0b, 0x02, 0x05, 0x12, 0x03, 0x49, 0x02, 0x19,
|
||||||
|
0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x05, 0x01, 0x12, 0x03, 0x49, 0x09, 0x14, 0x0a, 0x0c,
|
||||||
|
0x0a, 0x05, 0x04, 0x0b, 0x02, 0x05, 0x03, 0x12, 0x03, 0x49, 0x17, 0x18, 0x0a, 0x0c, 0x0a, 0x05,
|
||||||
|
0x04, 0x0b, 0x02, 0x06, 0x05, 0x12, 0x03, 0x4a, 0x02, 0x08, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x0b,
|
||||||
|
0x02, 0x06, 0x12, 0x03, 0x4a, 0x02, 0x1e, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x06, 0x01,
|
||||||
|
0x12, 0x03, 0x4a, 0x09, 0x19, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x06, 0x03, 0x12, 0x03,
|
||||||
|
0x4a, 0x1c, 0x1d, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x07, 0x06, 0x12, 0x03, 0x4b, 0x02,
|
||||||
|
0x07, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x0b, 0x02, 0x07, 0x12, 0x03, 0x4b, 0x02, 0x12, 0x0a, 0x0c,
|
||||||
|
0x0a, 0x05, 0x04, 0x0b, 0x02, 0x07, 0x01, 0x12, 0x03, 0x4b, 0x08, 0x0d, 0x0a, 0x0c, 0x0a, 0x05,
|
||||||
|
0x04, 0x0b, 0x02, 0x07, 0x03, 0x12, 0x03, 0x4b, 0x10, 0x11, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0b,
|
||||||
|
0x02, 0x08, 0x05, 0x12, 0x03, 0x4c, 0x02, 0x08, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x0b, 0x02, 0x08,
|
||||||
|
0x12, 0x03, 0x4c, 0x02, 0x1b, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x08, 0x01, 0x12, 0x03,
|
||||||
|
0x4c, 0x09, 0x16, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x08, 0x03, 0x12, 0x03, 0x4c, 0x19,
|
||||||
|
0x1a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x09, 0x05, 0x12, 0x03, 0x4d, 0x02, 0x08, 0x0a,
|
||||||
|
0x0b, 0x0a, 0x04, 0x04, 0x0b, 0x02, 0x09, 0x12, 0x03, 0x4d, 0x02, 0x21, 0x0a, 0x0c, 0x0a, 0x05,
|
||||||
|
0x04, 0x0b, 0x02, 0x09, 0x01, 0x12, 0x03, 0x4d, 0x09, 0x1b, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0b,
|
||||||
|
0x02, 0x09, 0x03, 0x12, 0x03, 0x4d, 0x1e, 0x20, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x0a,
|
||||||
|
0x05, 0x12, 0x03, 0x4e, 0x02, 0x08, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x0b, 0x02, 0x0a, 0x12, 0x03,
|
||||||
|
0x4e, 0x02, 0x1a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x0a, 0x01, 0x12, 0x03, 0x4e, 0x09,
|
||||||
|
0x14, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x0a, 0x03, 0x12, 0x03, 0x4e, 0x17, 0x19, 0x0a,
|
||||||
|
0x0c, 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x0b, 0x05, 0x12, 0x03, 0x4f, 0x02, 0x08, 0x0a, 0x0b, 0x0a,
|
||||||
|
0x04, 0x04, 0x0b, 0x02, 0x0b, 0x12, 0x03, 0x4f, 0x02, 0x1d, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0b,
|
||||||
|
0x02, 0x0b, 0x01, 0x12, 0x03, 0x4f, 0x09, 0x17, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x0b,
|
||||||
|
0x03, 0x12, 0x03, 0x4f, 0x1a, 0x1c, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x0c, 0x05, 0x12,
|
||||||
|
0x03, 0x50, 0x02, 0x08, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x0b, 0x02, 0x0c, 0x12, 0x03, 0x50, 0x02,
|
||||||
|
0x1f, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x0c, 0x01, 0x12, 0x03, 0x50, 0x09, 0x19, 0x0a,
|
||||||
|
0x0c, 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x0c, 0x03, 0x12, 0x03, 0x50, 0x1c, 0x1e, 0x0a, 0x0c, 0x0a,
|
||||||
|
0x05, 0x04, 0x0b, 0x02, 0x0d, 0x05, 0x12, 0x03, 0x51, 0x02, 0x08, 0x0a, 0x0b, 0x0a, 0x04, 0x04,
|
||||||
|
0x0b, 0x02, 0x0d, 0x12, 0x03, 0x51, 0x02, 0x14, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x0d,
|
||||||
|
0x01, 0x12, 0x03, 0x51, 0x09, 0x0e, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x0d, 0x03, 0x12,
|
||||||
|
0x03, 0x51, 0x11, 0x13, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x0e, 0x05, 0x12, 0x03, 0x52,
|
||||||
|
0x02, 0x08, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x0b, 0x02, 0x0e, 0x12, 0x03, 0x52, 0x02, 0x14, 0x0a,
|
||||||
|
0x0c, 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x0e, 0x01, 0x12, 0x03, 0x52, 0x09, 0x0e, 0x0a, 0x0c, 0x0a,
|
||||||
|
0x05, 0x04, 0x0b, 0x02, 0x0e, 0x03, 0x12, 0x03, 0x52, 0x11, 0x13, 0x62, 0x06, 0x70, 0x72, 0x6f,
|
||||||
|
0x74, 0x6f, 0x33,
|
||||||
];
|
];
|
||||||
include!("torrent.serde.rs");
|
include!("torrent.serde.rs");
|
||||||
include!("torrent.tonic.rs");
|
include!("torrent.tonic.rs");
|
||||||
|
|||||||
@@ -362,6 +362,511 @@ impl<'de> serde::Deserialize<'de> for ListResponse {
|
|||||||
deserializer.deserialize_struct("torrent.ListResponse", FIELDS, GeneratedVisitor)
|
deserializer.deserialize_struct("torrent.ListResponse", FIELDS, GeneratedVisitor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl serde::Serialize for PauseRequest {
|
||||||
|
#[allow(deprecated)]
|
||||||
|
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: serde::Serializer,
|
||||||
|
{
|
||||||
|
use serde::ser::SerializeStruct;
|
||||||
|
let mut len = 0;
|
||||||
|
if !self.id.is_empty() {
|
||||||
|
len += 1;
|
||||||
|
}
|
||||||
|
let mut struct_ser = serializer.serialize_struct("torrent.PauseRequest", len)?;
|
||||||
|
if !self.id.is_empty() {
|
||||||
|
struct_ser.serialize_field("id", &self.id)?;
|
||||||
|
}
|
||||||
|
struct_ser.end()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<'de> serde::Deserialize<'de> for PauseRequest {
|
||||||
|
#[allow(deprecated)]
|
||||||
|
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
const FIELDS: &[&str] = &["id"];
|
||||||
|
|
||||||
|
#[allow(clippy::enum_variant_names)]
|
||||||
|
enum GeneratedField {
|
||||||
|
Id,
|
||||||
|
}
|
||||||
|
impl<'de> serde::Deserialize<'de> for GeneratedField {
|
||||||
|
fn deserialize<D>(deserializer: D) -> std::result::Result<GeneratedField, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
struct GeneratedVisitor;
|
||||||
|
|
||||||
|
impl<'de> serde::de::Visitor<'de> for GeneratedVisitor {
|
||||||
|
type Value = GeneratedField;
|
||||||
|
|
||||||
|
fn expecting(
|
||||||
|
&self,
|
||||||
|
formatter: &mut std::fmt::Formatter<'_>,
|
||||||
|
) -> std::fmt::Result {
|
||||||
|
write!(formatter, "expected one of: {:?}", &FIELDS)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused_variables)]
|
||||||
|
fn visit_str<E>(self, value: &str) -> std::result::Result<GeneratedField, E>
|
||||||
|
where
|
||||||
|
E: serde::de::Error,
|
||||||
|
{
|
||||||
|
match value {
|
||||||
|
"id" => Ok(GeneratedField::Id),
|
||||||
|
_ => Err(serde::de::Error::unknown_field(value, FIELDS)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
deserializer.deserialize_identifier(GeneratedVisitor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
struct GeneratedVisitor;
|
||||||
|
impl<'de> serde::de::Visitor<'de> for GeneratedVisitor {
|
||||||
|
type Value = PauseRequest;
|
||||||
|
|
||||||
|
fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
formatter.write_str("struct torrent.PauseRequest")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_map<V>(self, mut map_: V) -> std::result::Result<PauseRequest, V::Error>
|
||||||
|
where
|
||||||
|
V: serde::de::MapAccess<'de>,
|
||||||
|
{
|
||||||
|
let mut id__ = None;
|
||||||
|
while let Some(k) = map_.next_key()? {
|
||||||
|
match k {
|
||||||
|
GeneratedField::Id => {
|
||||||
|
if id__.is_some() {
|
||||||
|
return Err(serde::de::Error::duplicate_field("id"));
|
||||||
|
}
|
||||||
|
id__ = Some(map_.next_value()?);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(PauseRequest {
|
||||||
|
id: id__.unwrap_or_default(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
deserializer.deserialize_struct("torrent.PauseRequest", FIELDS, GeneratedVisitor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl serde::Serialize for PauseResponse {
|
||||||
|
#[allow(deprecated)]
|
||||||
|
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: serde::Serializer,
|
||||||
|
{
|
||||||
|
use serde::ser::SerializeStruct;
|
||||||
|
let len = 0;
|
||||||
|
let struct_ser = serializer.serialize_struct("torrent.PauseResponse", len)?;
|
||||||
|
struct_ser.end()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<'de> serde::Deserialize<'de> for PauseResponse {
|
||||||
|
#[allow(deprecated)]
|
||||||
|
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
const FIELDS: &[&str] = &[];
|
||||||
|
|
||||||
|
#[allow(clippy::enum_variant_names)]
|
||||||
|
enum GeneratedField {}
|
||||||
|
impl<'de> serde::Deserialize<'de> for GeneratedField {
|
||||||
|
fn deserialize<D>(deserializer: D) -> std::result::Result<GeneratedField, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
struct GeneratedVisitor;
|
||||||
|
|
||||||
|
impl<'de> serde::de::Visitor<'de> for GeneratedVisitor {
|
||||||
|
type Value = GeneratedField;
|
||||||
|
|
||||||
|
fn expecting(
|
||||||
|
&self,
|
||||||
|
formatter: &mut std::fmt::Formatter<'_>,
|
||||||
|
) -> std::fmt::Result {
|
||||||
|
write!(formatter, "expected one of: {:?}", &FIELDS)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused_variables)]
|
||||||
|
fn visit_str<E>(self, value: &str) -> std::result::Result<GeneratedField, E>
|
||||||
|
where
|
||||||
|
E: serde::de::Error,
|
||||||
|
{
|
||||||
|
Err(serde::de::Error::unknown_field(value, FIELDS))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
deserializer.deserialize_identifier(GeneratedVisitor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
struct GeneratedVisitor;
|
||||||
|
impl<'de> serde::de::Visitor<'de> for GeneratedVisitor {
|
||||||
|
type Value = PauseResponse;
|
||||||
|
|
||||||
|
fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
formatter.write_str("struct torrent.PauseResponse")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_map<V>(self, mut map_: V) -> std::result::Result<PauseResponse, V::Error>
|
||||||
|
where
|
||||||
|
V: serde::de::MapAccess<'de>,
|
||||||
|
{
|
||||||
|
while map_.next_key::<GeneratedField>()?.is_some() {
|
||||||
|
let _ = map_.next_value::<serde::de::IgnoredAny>()?;
|
||||||
|
}
|
||||||
|
Ok(PauseResponse {})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
deserializer.deserialize_struct("torrent.PauseResponse", FIELDS, GeneratedVisitor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl serde::Serialize for RemoveRequest {
|
||||||
|
#[allow(deprecated)]
|
||||||
|
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: serde::Serializer,
|
||||||
|
{
|
||||||
|
use serde::ser::SerializeStruct;
|
||||||
|
let mut len = 0;
|
||||||
|
if !self.id.is_empty() {
|
||||||
|
len += 1;
|
||||||
|
}
|
||||||
|
if self.delete_files {
|
||||||
|
len += 1;
|
||||||
|
}
|
||||||
|
let mut struct_ser = serializer.serialize_struct("torrent.RemoveRequest", len)?;
|
||||||
|
if !self.id.is_empty() {
|
||||||
|
struct_ser.serialize_field("id", &self.id)?;
|
||||||
|
}
|
||||||
|
if self.delete_files {
|
||||||
|
struct_ser.serialize_field("deleteFiles", &self.delete_files)?;
|
||||||
|
}
|
||||||
|
struct_ser.end()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<'de> serde::Deserialize<'de> for RemoveRequest {
|
||||||
|
#[allow(deprecated)]
|
||||||
|
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
const FIELDS: &[&str] = &["id", "delete_files", "deleteFiles"];
|
||||||
|
|
||||||
|
#[allow(clippy::enum_variant_names)]
|
||||||
|
enum GeneratedField {
|
||||||
|
Id,
|
||||||
|
DeleteFiles,
|
||||||
|
}
|
||||||
|
impl<'de> serde::Deserialize<'de> for GeneratedField {
|
||||||
|
fn deserialize<D>(deserializer: D) -> std::result::Result<GeneratedField, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
struct GeneratedVisitor;
|
||||||
|
|
||||||
|
impl<'de> serde::de::Visitor<'de> for GeneratedVisitor {
|
||||||
|
type Value = GeneratedField;
|
||||||
|
|
||||||
|
fn expecting(
|
||||||
|
&self,
|
||||||
|
formatter: &mut std::fmt::Formatter<'_>,
|
||||||
|
) -> std::fmt::Result {
|
||||||
|
write!(formatter, "expected one of: {:?}", &FIELDS)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused_variables)]
|
||||||
|
fn visit_str<E>(self, value: &str) -> std::result::Result<GeneratedField, E>
|
||||||
|
where
|
||||||
|
E: serde::de::Error,
|
||||||
|
{
|
||||||
|
match value {
|
||||||
|
"id" => Ok(GeneratedField::Id),
|
||||||
|
"deleteFiles" | "delete_files" => Ok(GeneratedField::DeleteFiles),
|
||||||
|
_ => Err(serde::de::Error::unknown_field(value, FIELDS)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
deserializer.deserialize_identifier(GeneratedVisitor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
struct GeneratedVisitor;
|
||||||
|
impl<'de> serde::de::Visitor<'de> for GeneratedVisitor {
|
||||||
|
type Value = RemoveRequest;
|
||||||
|
|
||||||
|
fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
formatter.write_str("struct torrent.RemoveRequest")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_map<V>(self, mut map_: V) -> std::result::Result<RemoveRequest, V::Error>
|
||||||
|
where
|
||||||
|
V: serde::de::MapAccess<'de>,
|
||||||
|
{
|
||||||
|
let mut id__ = None;
|
||||||
|
let mut delete_files__ = None;
|
||||||
|
while let Some(k) = map_.next_key()? {
|
||||||
|
match k {
|
||||||
|
GeneratedField::Id => {
|
||||||
|
if id__.is_some() {
|
||||||
|
return Err(serde::de::Error::duplicate_field("id"));
|
||||||
|
}
|
||||||
|
id__ = Some(map_.next_value()?);
|
||||||
|
}
|
||||||
|
GeneratedField::DeleteFiles => {
|
||||||
|
if delete_files__.is_some() {
|
||||||
|
return Err(serde::de::Error::duplicate_field("deleteFiles"));
|
||||||
|
}
|
||||||
|
delete_files__ = Some(map_.next_value()?);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(RemoveRequest {
|
||||||
|
id: id__.unwrap_or_default(),
|
||||||
|
delete_files: delete_files__.unwrap_or_default(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
deserializer.deserialize_struct("torrent.RemoveRequest", FIELDS, GeneratedVisitor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl serde::Serialize for RemoveResponse {
|
||||||
|
#[allow(deprecated)]
|
||||||
|
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: serde::Serializer,
|
||||||
|
{
|
||||||
|
use serde::ser::SerializeStruct;
|
||||||
|
let len = 0;
|
||||||
|
let struct_ser = serializer.serialize_struct("torrent.RemoveResponse", len)?;
|
||||||
|
struct_ser.end()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<'de> serde::Deserialize<'de> for RemoveResponse {
|
||||||
|
#[allow(deprecated)]
|
||||||
|
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
const FIELDS: &[&str] = &[];
|
||||||
|
|
||||||
|
#[allow(clippy::enum_variant_names)]
|
||||||
|
enum GeneratedField {}
|
||||||
|
impl<'de> serde::Deserialize<'de> for GeneratedField {
|
||||||
|
fn deserialize<D>(deserializer: D) -> std::result::Result<GeneratedField, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
struct GeneratedVisitor;
|
||||||
|
|
||||||
|
impl<'de> serde::de::Visitor<'de> for GeneratedVisitor {
|
||||||
|
type Value = GeneratedField;
|
||||||
|
|
||||||
|
fn expecting(
|
||||||
|
&self,
|
||||||
|
formatter: &mut std::fmt::Formatter<'_>,
|
||||||
|
) -> std::fmt::Result {
|
||||||
|
write!(formatter, "expected one of: {:?}", &FIELDS)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused_variables)]
|
||||||
|
fn visit_str<E>(self, value: &str) -> std::result::Result<GeneratedField, E>
|
||||||
|
where
|
||||||
|
E: serde::de::Error,
|
||||||
|
{
|
||||||
|
Err(serde::de::Error::unknown_field(value, FIELDS))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
deserializer.deserialize_identifier(GeneratedVisitor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
struct GeneratedVisitor;
|
||||||
|
impl<'de> serde::de::Visitor<'de> for GeneratedVisitor {
|
||||||
|
type Value = RemoveResponse;
|
||||||
|
|
||||||
|
fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
formatter.write_str("struct torrent.RemoveResponse")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_map<V>(self, mut map_: V) -> std::result::Result<RemoveResponse, V::Error>
|
||||||
|
where
|
||||||
|
V: serde::de::MapAccess<'de>,
|
||||||
|
{
|
||||||
|
while map_.next_key::<GeneratedField>()?.is_some() {
|
||||||
|
let _ = map_.next_value::<serde::de::IgnoredAny>()?;
|
||||||
|
}
|
||||||
|
Ok(RemoveResponse {})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
deserializer.deserialize_struct("torrent.RemoveResponse", FIELDS, GeneratedVisitor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl serde::Serialize for ResumeRequest {
|
||||||
|
#[allow(deprecated)]
|
||||||
|
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: serde::Serializer,
|
||||||
|
{
|
||||||
|
use serde::ser::SerializeStruct;
|
||||||
|
let mut len = 0;
|
||||||
|
if !self.id.is_empty() {
|
||||||
|
len += 1;
|
||||||
|
}
|
||||||
|
let mut struct_ser = serializer.serialize_struct("torrent.ResumeRequest", len)?;
|
||||||
|
if !self.id.is_empty() {
|
||||||
|
struct_ser.serialize_field("id", &self.id)?;
|
||||||
|
}
|
||||||
|
struct_ser.end()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<'de> serde::Deserialize<'de> for ResumeRequest {
|
||||||
|
#[allow(deprecated)]
|
||||||
|
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
const FIELDS: &[&str] = &["id"];
|
||||||
|
|
||||||
|
#[allow(clippy::enum_variant_names)]
|
||||||
|
enum GeneratedField {
|
||||||
|
Id,
|
||||||
|
}
|
||||||
|
impl<'de> serde::Deserialize<'de> for GeneratedField {
|
||||||
|
fn deserialize<D>(deserializer: D) -> std::result::Result<GeneratedField, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
struct GeneratedVisitor;
|
||||||
|
|
||||||
|
impl<'de> serde::de::Visitor<'de> for GeneratedVisitor {
|
||||||
|
type Value = GeneratedField;
|
||||||
|
|
||||||
|
fn expecting(
|
||||||
|
&self,
|
||||||
|
formatter: &mut std::fmt::Formatter<'_>,
|
||||||
|
) -> std::fmt::Result {
|
||||||
|
write!(formatter, "expected one of: {:?}", &FIELDS)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused_variables)]
|
||||||
|
fn visit_str<E>(self, value: &str) -> std::result::Result<GeneratedField, E>
|
||||||
|
where
|
||||||
|
E: serde::de::Error,
|
||||||
|
{
|
||||||
|
match value {
|
||||||
|
"id" => Ok(GeneratedField::Id),
|
||||||
|
_ => Err(serde::de::Error::unknown_field(value, FIELDS)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
deserializer.deserialize_identifier(GeneratedVisitor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
struct GeneratedVisitor;
|
||||||
|
impl<'de> serde::de::Visitor<'de> for GeneratedVisitor {
|
||||||
|
type Value = ResumeRequest;
|
||||||
|
|
||||||
|
fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
formatter.write_str("struct torrent.ResumeRequest")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_map<V>(self, mut map_: V) -> std::result::Result<ResumeRequest, V::Error>
|
||||||
|
where
|
||||||
|
V: serde::de::MapAccess<'de>,
|
||||||
|
{
|
||||||
|
let mut id__ = None;
|
||||||
|
while let Some(k) = map_.next_key()? {
|
||||||
|
match k {
|
||||||
|
GeneratedField::Id => {
|
||||||
|
if id__.is_some() {
|
||||||
|
return Err(serde::de::Error::duplicate_field("id"));
|
||||||
|
}
|
||||||
|
id__ = Some(map_.next_value()?);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(ResumeRequest {
|
||||||
|
id: id__.unwrap_or_default(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
deserializer.deserialize_struct("torrent.ResumeRequest", FIELDS, GeneratedVisitor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl serde::Serialize for ResumeResponse {
|
||||||
|
#[allow(deprecated)]
|
||||||
|
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: serde::Serializer,
|
||||||
|
{
|
||||||
|
use serde::ser::SerializeStruct;
|
||||||
|
let len = 0;
|
||||||
|
let struct_ser = serializer.serialize_struct("torrent.ResumeResponse", len)?;
|
||||||
|
struct_ser.end()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<'de> serde::Deserialize<'de> for ResumeResponse {
|
||||||
|
#[allow(deprecated)]
|
||||||
|
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
const FIELDS: &[&str] = &[];
|
||||||
|
|
||||||
|
#[allow(clippy::enum_variant_names)]
|
||||||
|
enum GeneratedField {}
|
||||||
|
impl<'de> serde::Deserialize<'de> for GeneratedField {
|
||||||
|
fn deserialize<D>(deserializer: D) -> std::result::Result<GeneratedField, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
struct GeneratedVisitor;
|
||||||
|
|
||||||
|
impl<'de> serde::de::Visitor<'de> for GeneratedVisitor {
|
||||||
|
type Value = GeneratedField;
|
||||||
|
|
||||||
|
fn expecting(
|
||||||
|
&self,
|
||||||
|
formatter: &mut std::fmt::Formatter<'_>,
|
||||||
|
) -> std::fmt::Result {
|
||||||
|
write!(formatter, "expected one of: {:?}", &FIELDS)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused_variables)]
|
||||||
|
fn visit_str<E>(self, value: &str) -> std::result::Result<GeneratedField, E>
|
||||||
|
where
|
||||||
|
E: serde::de::Error,
|
||||||
|
{
|
||||||
|
Err(serde::de::Error::unknown_field(value, FIELDS))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
deserializer.deserialize_identifier(GeneratedVisitor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
struct GeneratedVisitor;
|
||||||
|
impl<'de> serde::de::Visitor<'de> for GeneratedVisitor {
|
||||||
|
type Value = ResumeResponse;
|
||||||
|
|
||||||
|
fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
formatter.write_str("struct torrent.ResumeResponse")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_map<V>(self, mut map_: V) -> std::result::Result<ResumeResponse, V::Error>
|
||||||
|
where
|
||||||
|
V: serde::de::MapAccess<'de>,
|
||||||
|
{
|
||||||
|
while map_.next_key::<GeneratedField>()?.is_some() {
|
||||||
|
let _ = map_.next_value::<serde::de::IgnoredAny>()?;
|
||||||
|
}
|
||||||
|
Ok(ResumeResponse {})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
deserializer.deserialize_struct("torrent.ResumeResponse", FIELDS, GeneratedVisitor)
|
||||||
|
}
|
||||||
|
}
|
||||||
impl serde::Serialize for State {
|
impl serde::Serialize for State {
|
||||||
#[allow(deprecated)]
|
#[allow(deprecated)]
|
||||||
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
|
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
|
||||||
@@ -572,6 +1077,24 @@ impl serde::Serialize for TorrentStatus {
|
|||||||
if !self.error_message.is_empty() {
|
if !self.error_message.is_empty() {
|
||||||
len += 1;
|
len += 1;
|
||||||
}
|
}
|
||||||
|
if self.download_speed_bps != 0 {
|
||||||
|
len += 1;
|
||||||
|
}
|
||||||
|
if self.eta_seconds != 0 {
|
||||||
|
len += 1;
|
||||||
|
}
|
||||||
|
if self.uploaded_bytes != 0 {
|
||||||
|
len += 1;
|
||||||
|
}
|
||||||
|
if self.upload_speed_bps != 0 {
|
||||||
|
len += 1;
|
||||||
|
}
|
||||||
|
if self.peers != 0 {
|
||||||
|
len += 1;
|
||||||
|
}
|
||||||
|
if self.seeds != 0 {
|
||||||
|
len += 1;
|
||||||
|
}
|
||||||
let mut struct_ser = serializer.serialize_struct("torrent.TorrentStatus", len)?;
|
let mut struct_ser = serializer.serialize_struct("torrent.TorrentStatus", len)?;
|
||||||
if !self.id.is_empty() {
|
if !self.id.is_empty() {
|
||||||
struct_ser.serialize_field("id", &self.id)?;
|
struct_ser.serialize_field("id", &self.id)?;
|
||||||
@@ -613,6 +1136,44 @@ impl serde::Serialize for TorrentStatus {
|
|||||||
if !self.error_message.is_empty() {
|
if !self.error_message.is_empty() {
|
||||||
struct_ser.serialize_field("errorMessage", &self.error_message)?;
|
struct_ser.serialize_field("errorMessage", &self.error_message)?;
|
||||||
}
|
}
|
||||||
|
if self.download_speed_bps != 0 {
|
||||||
|
#[allow(clippy::needless_borrow)]
|
||||||
|
#[allow(clippy::needless_borrows_for_generic_args)]
|
||||||
|
struct_ser.serialize_field(
|
||||||
|
"downloadSpeedBps",
|
||||||
|
ToString::to_string(&self.download_speed_bps).as_str(),
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
if self.eta_seconds != 0 {
|
||||||
|
#[allow(clippy::needless_borrow)]
|
||||||
|
#[allow(clippy::needless_borrows_for_generic_args)]
|
||||||
|
struct_ser.serialize_field(
|
||||||
|
"etaSeconds",
|
||||||
|
ToString::to_string(&self.eta_seconds).as_str(),
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
if self.uploaded_bytes != 0 {
|
||||||
|
#[allow(clippy::needless_borrow)]
|
||||||
|
#[allow(clippy::needless_borrows_for_generic_args)]
|
||||||
|
struct_ser.serialize_field(
|
||||||
|
"uploadedBytes",
|
||||||
|
ToString::to_string(&self.uploaded_bytes).as_str(),
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
if self.upload_speed_bps != 0 {
|
||||||
|
#[allow(clippy::needless_borrow)]
|
||||||
|
#[allow(clippy::needless_borrows_for_generic_args)]
|
||||||
|
struct_ser.serialize_field(
|
||||||
|
"uploadSpeedBps",
|
||||||
|
ToString::to_string(&self.upload_speed_bps).as_str(),
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
if self.peers != 0 {
|
||||||
|
struct_ser.serialize_field("peers", &self.peers)?;
|
||||||
|
}
|
||||||
|
if self.seeds != 0 {
|
||||||
|
struct_ser.serialize_field("seeds", &self.seeds)?;
|
||||||
|
}
|
||||||
struct_ser.end()
|
struct_ser.end()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -637,6 +1198,16 @@ impl<'de> serde::Deserialize<'de> for TorrentStatus {
|
|||||||
"state",
|
"state",
|
||||||
"error_message",
|
"error_message",
|
||||||
"errorMessage",
|
"errorMessage",
|
||||||
|
"download_speed_bps",
|
||||||
|
"downloadSpeedBps",
|
||||||
|
"eta_seconds",
|
||||||
|
"etaSeconds",
|
||||||
|
"uploaded_bytes",
|
||||||
|
"uploadedBytes",
|
||||||
|
"upload_speed_bps",
|
||||||
|
"uploadSpeedBps",
|
||||||
|
"peers",
|
||||||
|
"seeds",
|
||||||
];
|
];
|
||||||
|
|
||||||
#[allow(clippy::enum_variant_names)]
|
#[allow(clippy::enum_variant_names)]
|
||||||
@@ -650,6 +1221,12 @@ impl<'de> serde::Deserialize<'de> for TorrentStatus {
|
|||||||
DownloadedBytes,
|
DownloadedBytes,
|
||||||
State,
|
State,
|
||||||
ErrorMessage,
|
ErrorMessage,
|
||||||
|
DownloadSpeedBps,
|
||||||
|
EtaSeconds,
|
||||||
|
UploadedBytes,
|
||||||
|
UploadSpeedBps,
|
||||||
|
Peers,
|
||||||
|
Seeds,
|
||||||
}
|
}
|
||||||
impl<'de> serde::Deserialize<'de> for GeneratedField {
|
impl<'de> serde::Deserialize<'de> for GeneratedField {
|
||||||
fn deserialize<D>(deserializer: D) -> std::result::Result<GeneratedField, D::Error>
|
fn deserialize<D>(deserializer: D) -> std::result::Result<GeneratedField, D::Error>
|
||||||
@@ -685,6 +1262,16 @@ impl<'de> serde::Deserialize<'de> for TorrentStatus {
|
|||||||
}
|
}
|
||||||
"state" => Ok(GeneratedField::State),
|
"state" => Ok(GeneratedField::State),
|
||||||
"errorMessage" | "error_message" => Ok(GeneratedField::ErrorMessage),
|
"errorMessage" | "error_message" => Ok(GeneratedField::ErrorMessage),
|
||||||
|
"downloadSpeedBps" | "download_speed_bps" => {
|
||||||
|
Ok(GeneratedField::DownloadSpeedBps)
|
||||||
|
}
|
||||||
|
"etaSeconds" | "eta_seconds" => Ok(GeneratedField::EtaSeconds),
|
||||||
|
"uploadedBytes" | "uploaded_bytes" => Ok(GeneratedField::UploadedBytes),
|
||||||
|
"uploadSpeedBps" | "upload_speed_bps" => {
|
||||||
|
Ok(GeneratedField::UploadSpeedBps)
|
||||||
|
}
|
||||||
|
"peers" => Ok(GeneratedField::Peers),
|
||||||
|
"seeds" => Ok(GeneratedField::Seeds),
|
||||||
_ => Err(serde::de::Error::unknown_field(value, FIELDS)),
|
_ => Err(serde::de::Error::unknown_field(value, FIELDS)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -713,6 +1300,12 @@ impl<'de> serde::Deserialize<'de> for TorrentStatus {
|
|||||||
let mut downloaded_bytes__ = None;
|
let mut downloaded_bytes__ = None;
|
||||||
let mut state__ = None;
|
let mut state__ = None;
|
||||||
let mut error_message__ = None;
|
let mut error_message__ = None;
|
||||||
|
let mut download_speed_bps__ = None;
|
||||||
|
let mut eta_seconds__ = None;
|
||||||
|
let mut uploaded_bytes__ = None;
|
||||||
|
let mut upload_speed_bps__ = None;
|
||||||
|
let mut peers__ = None;
|
||||||
|
let mut seeds__ = None;
|
||||||
while let Some(k) = map_.next_key()? {
|
while let Some(k) = map_.next_key()? {
|
||||||
match k {
|
match k {
|
||||||
GeneratedField::Id => {
|
GeneratedField::Id => {
|
||||||
@@ -775,6 +1368,60 @@ impl<'de> serde::Deserialize<'de> for TorrentStatus {
|
|||||||
}
|
}
|
||||||
error_message__ = Some(map_.next_value()?);
|
error_message__ = Some(map_.next_value()?);
|
||||||
}
|
}
|
||||||
|
GeneratedField::DownloadSpeedBps => {
|
||||||
|
if download_speed_bps__.is_some() {
|
||||||
|
return Err(serde::de::Error::duplicate_field("downloadSpeedBps"));
|
||||||
|
}
|
||||||
|
download_speed_bps__ = Some(
|
||||||
|
map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?
|
||||||
|
.0,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
GeneratedField::EtaSeconds => {
|
||||||
|
if eta_seconds__.is_some() {
|
||||||
|
return Err(serde::de::Error::duplicate_field("etaSeconds"));
|
||||||
|
}
|
||||||
|
eta_seconds__ = Some(
|
||||||
|
map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?
|
||||||
|
.0,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
GeneratedField::UploadedBytes => {
|
||||||
|
if uploaded_bytes__.is_some() {
|
||||||
|
return Err(serde::de::Error::duplicate_field("uploadedBytes"));
|
||||||
|
}
|
||||||
|
uploaded_bytes__ = Some(
|
||||||
|
map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?
|
||||||
|
.0,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
GeneratedField::UploadSpeedBps => {
|
||||||
|
if upload_speed_bps__.is_some() {
|
||||||
|
return Err(serde::de::Error::duplicate_field("uploadSpeedBps"));
|
||||||
|
}
|
||||||
|
upload_speed_bps__ = Some(
|
||||||
|
map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?
|
||||||
|
.0,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
GeneratedField::Peers => {
|
||||||
|
if peers__.is_some() {
|
||||||
|
return Err(serde::de::Error::duplicate_field("peers"));
|
||||||
|
}
|
||||||
|
peers__ = Some(
|
||||||
|
map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?
|
||||||
|
.0,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
GeneratedField::Seeds => {
|
||||||
|
if seeds__.is_some() {
|
||||||
|
return Err(serde::de::Error::duplicate_field("seeds"));
|
||||||
|
}
|
||||||
|
seeds__ = Some(
|
||||||
|
map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?
|
||||||
|
.0,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(TorrentStatus {
|
Ok(TorrentStatus {
|
||||||
@@ -787,6 +1434,12 @@ impl<'de> serde::Deserialize<'de> for TorrentStatus {
|
|||||||
downloaded_bytes: downloaded_bytes__.unwrap_or_default(),
|
downloaded_bytes: downloaded_bytes__.unwrap_or_default(),
|
||||||
state: state__.unwrap_or_default(),
|
state: state__.unwrap_or_default(),
|
||||||
error_message: error_message__.unwrap_or_default(),
|
error_message: error_message__.unwrap_or_default(),
|
||||||
|
download_speed_bps: download_speed_bps__.unwrap_or_default(),
|
||||||
|
eta_seconds: eta_seconds__.unwrap_or_default(),
|
||||||
|
uploaded_bytes: uploaded_bytes__.unwrap_or_default(),
|
||||||
|
upload_speed_bps: upload_speed_bps__.unwrap_or_default(),
|
||||||
|
peers: peers__.unwrap_or_default(),
|
||||||
|
seeds: seeds__.unwrap_or_default(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -131,6 +131,48 @@ pub mod torrents_client {
|
|||||||
.insert(GrpcMethod::new("torrent.Torrents", "List"));
|
.insert(GrpcMethod::new("torrent.Torrents", "List"));
|
||||||
self.inner.unary(req, path, codec).await
|
self.inner.unary(req, path, codec).await
|
||||||
}
|
}
|
||||||
|
pub async fn remove(
|
||||||
|
&mut self,
|
||||||
|
request: impl tonic::IntoRequest<super::RemoveRequest>,
|
||||||
|
) -> std::result::Result<tonic::Response<super::RemoveResponse>, tonic::Status> {
|
||||||
|
self.inner.ready().await.map_err(|e| {
|
||||||
|
tonic::Status::unknown(format!("Service was not ready: {}", e.into()))
|
||||||
|
})?;
|
||||||
|
let codec = tonic_prost::ProstCodec::default();
|
||||||
|
let path = http::uri::PathAndQuery::from_static("/torrent.Torrents/Remove");
|
||||||
|
let mut req = request.into_request();
|
||||||
|
req.extensions_mut()
|
||||||
|
.insert(GrpcMethod::new("torrent.Torrents", "Remove"));
|
||||||
|
self.inner.unary(req, path, codec).await
|
||||||
|
}
|
||||||
|
pub async fn pause(
|
||||||
|
&mut self,
|
||||||
|
request: impl tonic::IntoRequest<super::PauseRequest>,
|
||||||
|
) -> std::result::Result<tonic::Response<super::PauseResponse>, tonic::Status> {
|
||||||
|
self.inner.ready().await.map_err(|e| {
|
||||||
|
tonic::Status::unknown(format!("Service was not ready: {}", e.into()))
|
||||||
|
})?;
|
||||||
|
let codec = tonic_prost::ProstCodec::default();
|
||||||
|
let path = http::uri::PathAndQuery::from_static("/torrent.Torrents/Pause");
|
||||||
|
let mut req = request.into_request();
|
||||||
|
req.extensions_mut()
|
||||||
|
.insert(GrpcMethod::new("torrent.Torrents", "Pause"));
|
||||||
|
self.inner.unary(req, path, codec).await
|
||||||
|
}
|
||||||
|
pub async fn resume(
|
||||||
|
&mut self,
|
||||||
|
request: impl tonic::IntoRequest<super::ResumeRequest>,
|
||||||
|
) -> std::result::Result<tonic::Response<super::ResumeResponse>, tonic::Status> {
|
||||||
|
self.inner.ready().await.map_err(|e| {
|
||||||
|
tonic::Status::unknown(format!("Service was not ready: {}", e.into()))
|
||||||
|
})?;
|
||||||
|
let codec = tonic_prost::ProstCodec::default();
|
||||||
|
let path = http::uri::PathAndQuery::from_static("/torrent.Torrents/Resume");
|
||||||
|
let mut req = request.into_request();
|
||||||
|
req.extensions_mut()
|
||||||
|
.insert(GrpcMethod::new("torrent.Torrents", "Resume"));
|
||||||
|
self.inner.unary(req, path, codec).await
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Generated server implementations.
|
/// Generated server implementations.
|
||||||
@@ -158,6 +200,18 @@ pub mod torrents_server {
|
|||||||
&self,
|
&self,
|
||||||
request: tonic::Request<super::ListRequest>,
|
request: tonic::Request<super::ListRequest>,
|
||||||
) -> std::result::Result<tonic::Response<super::ListResponse>, tonic::Status>;
|
) -> std::result::Result<tonic::Response<super::ListResponse>, tonic::Status>;
|
||||||
|
async fn remove(
|
||||||
|
&self,
|
||||||
|
request: tonic::Request<super::RemoveRequest>,
|
||||||
|
) -> std::result::Result<tonic::Response<super::RemoveResponse>, tonic::Status>;
|
||||||
|
async fn pause(
|
||||||
|
&self,
|
||||||
|
request: tonic::Request<super::PauseRequest>,
|
||||||
|
) -> std::result::Result<tonic::Response<super::PauseResponse>, tonic::Status>;
|
||||||
|
async fn resume(
|
||||||
|
&self,
|
||||||
|
request: tonic::Request<super::ResumeRequest>,
|
||||||
|
) -> std::result::Result<tonic::Response<super::ResumeResponse>, tonic::Status>;
|
||||||
}
|
}
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct TorrentsServer<T> {
|
pub struct TorrentsServer<T> {
|
||||||
@@ -343,6 +397,117 @@ pub mod torrents_server {
|
|||||||
};
|
};
|
||||||
Box::pin(fut)
|
Box::pin(fut)
|
||||||
}
|
}
|
||||||
|
"/torrent.Torrents/Remove" => {
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
struct RemoveSvc<T: Torrents>(pub Arc<T>);
|
||||||
|
impl<T: Torrents> tonic::server::UnaryService<super::RemoveRequest> for RemoveSvc<T> {
|
||||||
|
type Response = super::RemoveResponse;
|
||||||
|
type Future = BoxFuture<tonic::Response<Self::Response>, tonic::Status>;
|
||||||
|
fn call(
|
||||||
|
&mut self,
|
||||||
|
request: tonic::Request<super::RemoveRequest>,
|
||||||
|
) -> Self::Future {
|
||||||
|
let inner = Arc::clone(&self.0);
|
||||||
|
let fut = async move { <T as Torrents>::remove(&inner, request).await };
|
||||||
|
Box::pin(fut)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let accept_compression_encodings = self.accept_compression_encodings;
|
||||||
|
let send_compression_encodings = self.send_compression_encodings;
|
||||||
|
let max_decoding_message_size = self.max_decoding_message_size;
|
||||||
|
let max_encoding_message_size = self.max_encoding_message_size;
|
||||||
|
let inner = self.inner.clone();
|
||||||
|
let fut = async move {
|
||||||
|
let method = RemoveSvc(inner);
|
||||||
|
let codec = tonic_prost::ProstCodec::default();
|
||||||
|
let mut grpc = tonic::server::Grpc::new(codec)
|
||||||
|
.apply_compression_config(
|
||||||
|
accept_compression_encodings,
|
||||||
|
send_compression_encodings,
|
||||||
|
)
|
||||||
|
.apply_max_message_size_config(
|
||||||
|
max_decoding_message_size,
|
||||||
|
max_encoding_message_size,
|
||||||
|
);
|
||||||
|
let res = grpc.unary(method, req).await;
|
||||||
|
Ok(res)
|
||||||
|
};
|
||||||
|
Box::pin(fut)
|
||||||
|
}
|
||||||
|
"/torrent.Torrents/Pause" => {
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
struct PauseSvc<T: Torrents>(pub Arc<T>);
|
||||||
|
impl<T: Torrents> tonic::server::UnaryService<super::PauseRequest> for PauseSvc<T> {
|
||||||
|
type Response = super::PauseResponse;
|
||||||
|
type Future = BoxFuture<tonic::Response<Self::Response>, tonic::Status>;
|
||||||
|
fn call(
|
||||||
|
&mut self,
|
||||||
|
request: tonic::Request<super::PauseRequest>,
|
||||||
|
) -> Self::Future {
|
||||||
|
let inner = Arc::clone(&self.0);
|
||||||
|
let fut = async move { <T as Torrents>::pause(&inner, request).await };
|
||||||
|
Box::pin(fut)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let accept_compression_encodings = self.accept_compression_encodings;
|
||||||
|
let send_compression_encodings = self.send_compression_encodings;
|
||||||
|
let max_decoding_message_size = self.max_decoding_message_size;
|
||||||
|
let max_encoding_message_size = self.max_encoding_message_size;
|
||||||
|
let inner = self.inner.clone();
|
||||||
|
let fut = async move {
|
||||||
|
let method = PauseSvc(inner);
|
||||||
|
let codec = tonic_prost::ProstCodec::default();
|
||||||
|
let mut grpc = tonic::server::Grpc::new(codec)
|
||||||
|
.apply_compression_config(
|
||||||
|
accept_compression_encodings,
|
||||||
|
send_compression_encodings,
|
||||||
|
)
|
||||||
|
.apply_max_message_size_config(
|
||||||
|
max_decoding_message_size,
|
||||||
|
max_encoding_message_size,
|
||||||
|
);
|
||||||
|
let res = grpc.unary(method, req).await;
|
||||||
|
Ok(res)
|
||||||
|
};
|
||||||
|
Box::pin(fut)
|
||||||
|
}
|
||||||
|
"/torrent.Torrents/Resume" => {
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
struct ResumeSvc<T: Torrents>(pub Arc<T>);
|
||||||
|
impl<T: Torrents> tonic::server::UnaryService<super::ResumeRequest> for ResumeSvc<T> {
|
||||||
|
type Response = super::ResumeResponse;
|
||||||
|
type Future = BoxFuture<tonic::Response<Self::Response>, tonic::Status>;
|
||||||
|
fn call(
|
||||||
|
&mut self,
|
||||||
|
request: tonic::Request<super::ResumeRequest>,
|
||||||
|
) -> Self::Future {
|
||||||
|
let inner = Arc::clone(&self.0);
|
||||||
|
let fut = async move { <T as Torrents>::resume(&inner, request).await };
|
||||||
|
Box::pin(fut)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let accept_compression_encodings = self.accept_compression_encodings;
|
||||||
|
let send_compression_encodings = self.send_compression_encodings;
|
||||||
|
let max_decoding_message_size = self.max_decoding_message_size;
|
||||||
|
let max_encoding_message_size = self.max_encoding_message_size;
|
||||||
|
let inner = self.inner.clone();
|
||||||
|
let fut = async move {
|
||||||
|
let method = ResumeSvc(inner);
|
||||||
|
let codec = tonic_prost::ProstCodec::default();
|
||||||
|
let mut grpc = tonic::server::Grpc::new(codec)
|
||||||
|
.apply_compression_config(
|
||||||
|
accept_compression_encodings,
|
||||||
|
send_compression_encodings,
|
||||||
|
)
|
||||||
|
.apply_max_message_size_config(
|
||||||
|
max_decoding_message_size,
|
||||||
|
max_encoding_message_size,
|
||||||
|
);
|
||||||
|
let res = grpc.unary(method, req).await;
|
||||||
|
Ok(res)
|
||||||
|
};
|
||||||
|
Box::pin(fut)
|
||||||
|
}
|
||||||
_ => Box::pin(async move {
|
_ => Box::pin(async move {
|
||||||
let mut response = http::Response::new(tonic::body::Body::default());
|
let mut response = http::Response::new(tonic::body::Body::default());
|
||||||
let headers = response.headers_mut();
|
let headers = response.headers_mut();
|
||||||
|
|||||||
@@ -3,7 +3,8 @@ pub mod torrent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub use torrent::{
|
pub use torrent::{
|
||||||
AddRequest, AddResponse, ListRequest, ListResponse, State, StatusRequest, TorrentStatus,
|
AddRequest, AddResponse, ListRequest, ListResponse, PauseRequest, PauseResponse, RemoveRequest,
|
||||||
|
RemoveResponse, ResumeRequest, ResumeResponse, State, StatusRequest, TorrentStatus,
|
||||||
torrents_client::TorrentsClient,
|
torrents_client::TorrentsClient,
|
||||||
torrents_server::{Torrents, TorrentsServer},
|
torrents_server::{Torrents, TorrentsServer},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,7 +6,10 @@ edition = "2024"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0.103"
|
anyhow = "1.0.103"
|
||||||
clap = { version = "4.6.1", features = ["derive", "env"] }
|
clap = { version = "4.6.1", features = ["derive", "env"] }
|
||||||
|
colored = "3"
|
||||||
hyper-util = { version = "0.1.20", features = ["tokio"] }
|
hyper-util = { version = "0.1.20", features = ["tokio"] }
|
||||||
|
nix = { version = "0.31", features = ["signal"] }
|
||||||
|
serde_json = "1.0.140"
|
||||||
tokio = { version = "1.52.3", features = ["rt-multi-thread", "macros", "net"] }
|
tokio = { version = "1.52.3", features = ["rt-multi-thread", "macros", "net"] }
|
||||||
tonic = "0.14.6"
|
tonic = "0.14.6"
|
||||||
tonic-health = "0.14.6"
|
tonic-health = "0.14.6"
|
||||||
|
|||||||
+69
-6
@@ -1,26 +1,56 @@
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use clap::{Parser, Subcommand};
|
use clap::{Parser, Subcommand, ValueEnum};
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
#[command(name = "tora", about = "CLI client for the tora torrent daemon")]
|
#[command(
|
||||||
|
name = "tora",
|
||||||
|
version,
|
||||||
|
about = "CLI client for the tora torrent daemon"
|
||||||
|
)]
|
||||||
pub struct Cli {
|
pub struct Cli {
|
||||||
/// Path to torad's Unix domain socket.
|
/// Path to torad's Unix domain socket.
|
||||||
#[arg(long, global = true, env = "TORAD_SOCKET")]
|
#[arg(long, global = true, env = "TORAD_SOCKET")]
|
||||||
pub socket: Option<PathBuf>,
|
pub socket: Option<PathBuf>,
|
||||||
|
|
||||||
|
/// Output format for results.
|
||||||
|
#[arg(long, short = 'o', global = true, value_enum, default_value_t = OutputFormat::Long)]
|
||||||
|
pub format: OutputFormat,
|
||||||
|
|
||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
pub command: Command,
|
pub command: Command,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, ValueEnum)]
|
||||||
|
pub enum OutputFormat {
|
||||||
|
/// Human-readable key-value format (default).
|
||||||
|
Long,
|
||||||
|
/// Machine-readable JSON.
|
||||||
|
Json,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, ValueEnum)]
|
||||||
|
pub enum SortKey {
|
||||||
|
/// Sort by torrent name (alphabetical).
|
||||||
|
Name,
|
||||||
|
/// Sort by download progress (highest first).
|
||||||
|
Progress,
|
||||||
|
/// Sort by total size (largest first).
|
||||||
|
Size,
|
||||||
|
/// Sort by download speed (fastest first).
|
||||||
|
Speed,
|
||||||
|
/// Sort by time added (newest first, default).
|
||||||
|
Added,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Subcommand)]
|
#[derive(Subcommand)]
|
||||||
pub enum Command {
|
pub enum Command {
|
||||||
/// Check whether torad is reachable and healthy.
|
/// Check whether torad is reachable and healthy.
|
||||||
Health,
|
Health,
|
||||||
/// Start downloading a torrent from a magnet link.
|
/// Start downloading a torrent from a magnet link or .torrent file.
|
||||||
Download {
|
Download {
|
||||||
/// The magnet link to download.
|
/// Magnet link, HTTP(S) URL, or path to a .torrent file.
|
||||||
magnet: String,
|
source: String,
|
||||||
/// Directory to save the torrent into. Defaults to torad's configured download directory.
|
/// Directory to save the torrent into. Defaults to torad's configured download directory.
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
output_dir: Option<String>,
|
output_dir: Option<String>,
|
||||||
@@ -31,5 +61,38 @@ pub enum Command {
|
|||||||
id: String,
|
id: String,
|
||||||
},
|
},
|
||||||
/// List all torrents known to torad.
|
/// List all torrents known to torad.
|
||||||
List,
|
List {
|
||||||
|
/// Sort torrents by the given key.
|
||||||
|
#[arg(long, value_enum)]
|
||||||
|
sort: Option<SortKey>,
|
||||||
|
},
|
||||||
|
/// Remove a torrent from torad.
|
||||||
|
Remove {
|
||||||
|
/// The torrent id.
|
||||||
|
id: String,
|
||||||
|
/// Also delete downloaded files from disk.
|
||||||
|
#[arg(long)]
|
||||||
|
delete_files: bool,
|
||||||
|
},
|
||||||
|
/// Pause a downloading torrent.
|
||||||
|
Pause {
|
||||||
|
/// The torrent id.
|
||||||
|
id: String,
|
||||||
|
},
|
||||||
|
/// Resume a paused torrent.
|
||||||
|
Resume {
|
||||||
|
/// The torrent id.
|
||||||
|
id: String,
|
||||||
|
},
|
||||||
|
/// Start the torad daemon in the background.
|
||||||
|
Start {
|
||||||
|
/// Postgres connection string.
|
||||||
|
#[arg(long, env = "DATABASE_URL")]
|
||||||
|
database_url: String,
|
||||||
|
/// Directory torrents are downloaded into by default.
|
||||||
|
#[arg(long)]
|
||||||
|
download_dir: Option<PathBuf>,
|
||||||
|
},
|
||||||
|
/// Stop the torad daemon.
|
||||||
|
Stop,
|
||||||
}
|
}
|
||||||
|
|||||||
+186
-58
@@ -1,33 +1,56 @@
|
|||||||
mod cli;
|
mod cli;
|
||||||
|
mod output;
|
||||||
|
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use cli::{Cli, Command};
|
use cli::{Cli, Command, SortKey};
|
||||||
use hyper_util::rt::TokioIo;
|
use hyper_util::rt::TokioIo;
|
||||||
|
use output::Formatter;
|
||||||
use tokio::net::UnixStream;
|
use tokio::net::UnixStream;
|
||||||
use tonic::transport::{Channel, Endpoint, Uri};
|
use tonic::transport::{Channel, Endpoint, Uri};
|
||||||
use tonic_health::pb::HealthCheckRequest;
|
use tonic_health::pb::HealthCheckRequest;
|
||||||
use tonic_health::pb::health_check_response::ServingStatus;
|
use tonic_health::pb::health_check_response::ServingStatus;
|
||||||
use tonic_health::pb::health_client::HealthClient;
|
use tonic_health::pb::health_client::HealthClient;
|
||||||
use tora_proto::{AddRequest, ListRequest, State, StatusRequest, TorrentStatus, TorrentsClient};
|
use tora_proto::{
|
||||||
|
AddRequest, ListRequest, PauseRequest, RemoveRequest, ResumeRequest, StatusRequest,
|
||||||
|
TorrentsClient,
|
||||||
|
};
|
||||||
use tower::service_fn;
|
use tower::service_fn;
|
||||||
|
|
||||||
|
const RPC_TIMEOUT: Duration = Duration::from_secs(10);
|
||||||
|
|
||||||
fn default_socket_path() -> PathBuf {
|
fn default_socket_path() -> PathBuf {
|
||||||
std::env::temp_dir().join("torad.sock")
|
std::env::temp_dir().join("torad.sock")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn default_pid_file_path() -> PathBuf {
|
||||||
|
std::env::temp_dir().join("torad.pid")
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> Result<()> {
|
async fn main() -> Result<()> {
|
||||||
let cli = Cli::parse();
|
let cli = Cli::parse();
|
||||||
let socket = cli.socket.unwrap_or_else(default_socket_path);
|
let socket = cli.socket.unwrap_or_else(default_socket_path);
|
||||||
|
let pid_file = default_pid_file_path();
|
||||||
|
let formatter = output::from_format(cli.format);
|
||||||
|
let f = formatter.as_ref();
|
||||||
|
|
||||||
match cli.command {
|
match cli.command {
|
||||||
Command::Health => health(&socket).await,
|
Command::Health => health(&socket, f).await,
|
||||||
Command::Download { magnet, output_dir } => download(&socket, magnet, output_dir).await,
|
Command::Download { source, output_dir } => download(&socket, source, output_dir, f).await,
|
||||||
Command::Status { id } => status(&socket, id).await,
|
Command::Status { id } => status(&socket, id, f).await,
|
||||||
Command::List => list(&socket).await,
|
Command::List { sort } => list(&socket, sort, f).await,
|
||||||
|
Command::Remove { id, delete_files } => remove(&socket, id, delete_files, f).await,
|
||||||
|
Command::Pause { id } => pause(&socket, id, f).await,
|
||||||
|
Command::Resume { id } => resume(&socket, id, f).await,
|
||||||
|
Command::Start {
|
||||||
|
database_url,
|
||||||
|
download_dir,
|
||||||
|
} => start_daemon(&socket, &pid_file, &database_url, download_dir, f).await,
|
||||||
|
Command::Stop => stop_daemon(&socket, &pid_file, f).await,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,103 +64,208 @@ async fn connect(socket: &Path) -> Result<Channel> {
|
|||||||
.await
|
.await
|
||||||
.with_context(|| {
|
.with_context(|| {
|
||||||
format!(
|
format!(
|
||||||
"could not reach torad at {} - is it running? start it with `torad`",
|
"could not reach torad at {} - is it running? start it with `tora start`",
|
||||||
socket.display()
|
socket.display()
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
Ok(channel)
|
Ok(channel)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn health(socket: &Path) -> Result<()> {
|
async fn health(socket: &Path, f: &dyn Formatter) -> Result<()> {
|
||||||
let channel = connect(socket).await?;
|
let channel = connect(socket).await?;
|
||||||
|
|
||||||
let mut client = HealthClient::new(channel);
|
let mut client = HealthClient::new(channel);
|
||||||
let response = client
|
let response = tokio::time::timeout(
|
||||||
.check(HealthCheckRequest {
|
RPC_TIMEOUT,
|
||||||
|
client.check(HealthCheckRequest {
|
||||||
service: String::new(),
|
service: String::new(),
|
||||||
})
|
}),
|
||||||
.await
|
)
|
||||||
.context("health check RPC failed")?;
|
.await
|
||||||
|
.context("health check timed out - is torad responsive?")?
|
||||||
|
.context("health check RPC failed")?;
|
||||||
|
|
||||||
let status =
|
let status =
|
||||||
ServingStatus::try_from(response.into_inner().status).unwrap_or(ServingStatus::Unknown);
|
ServingStatus::try_from(response.into_inner().status).unwrap_or(ServingStatus::Unknown);
|
||||||
|
let healthy = status == ServingStatus::Serving;
|
||||||
|
|
||||||
println!("{}", status.as_str_name());
|
println!("{}", f.health(healthy, status.as_str_name())?);
|
||||||
if status != ServingStatus::Serving {
|
|
||||||
|
if !healthy {
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn download(socket: &Path, magnet: String, output_dir: Option<String>) -> Result<()> {
|
async fn download(
|
||||||
|
socket: &Path,
|
||||||
|
source: String,
|
||||||
|
output_dir: Option<String>,
|
||||||
|
f: &dyn Formatter,
|
||||||
|
) -> Result<()> {
|
||||||
let channel = connect(socket).await?;
|
let channel = connect(socket).await?;
|
||||||
let mut client = TorrentsClient::new(channel);
|
let mut client = TorrentsClient::new(channel);
|
||||||
|
|
||||||
let response = client
|
let response = tokio::time::timeout(
|
||||||
.add(AddRequest {
|
RPC_TIMEOUT,
|
||||||
magnet,
|
client.add(AddRequest {
|
||||||
|
magnet: source,
|
||||||
output_dir: output_dir.unwrap_or_default(),
|
output_dir: output_dir.unwrap_or_default(),
|
||||||
})
|
}),
|
||||||
.await
|
)
|
||||||
.context("Add RPC failed")?;
|
.await
|
||||||
|
.context("add request timed out")?
|
||||||
|
.context("Add RPC failed")?;
|
||||||
|
|
||||||
println!("{}", response.into_inner().id);
|
println!("{}", f.download(&response.into_inner().id)?);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn status(socket: &Path, id: String) -> Result<()> {
|
async fn status(socket: &Path, id: String, f: &dyn Formatter) -> Result<()> {
|
||||||
let channel = connect(socket).await?;
|
let channel = connect(socket).await?;
|
||||||
let mut client = TorrentsClient::new(channel);
|
let mut client = TorrentsClient::new(channel);
|
||||||
|
|
||||||
let response = client
|
let response = tokio::time::timeout(RPC_TIMEOUT, client.status(StatusRequest { id }))
|
||||||
.status(StatusRequest { id })
|
|
||||||
.await
|
.await
|
||||||
|
.context("status request timed out")?
|
||||||
.context("Status RPC failed")?;
|
.context("Status RPC failed")?;
|
||||||
|
|
||||||
print_status(&response.into_inner());
|
println!("{}", f.status(&response.into_inner())?);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn list(socket: &Path) -> Result<()> {
|
async fn list(socket: &Path, sort: Option<SortKey>, f: &dyn Formatter) -> Result<()> {
|
||||||
let channel = connect(socket).await?;
|
let channel = connect(socket).await?;
|
||||||
let mut client = TorrentsClient::new(channel);
|
let mut client = TorrentsClient::new(channel);
|
||||||
|
|
||||||
let response = client
|
let response = tokio::time::timeout(RPC_TIMEOUT, client.list(ListRequest {}))
|
||||||
.list(ListRequest {})
|
|
||||||
.await
|
.await
|
||||||
|
.context("list request timed out")?
|
||||||
.context("List RPC failed")?
|
.context("List RPC failed")?
|
||||||
.into_inner();
|
.into_inner();
|
||||||
|
|
||||||
if response.torrents.is_empty() {
|
let mut torrents = response.torrents;
|
||||||
println!("no torrents");
|
if let Some(key) = sort {
|
||||||
return Ok(());
|
sort_torrents(&mut torrents, key);
|
||||||
}
|
|
||||||
for torrent in &response.torrents {
|
|
||||||
print_status(torrent);
|
|
||||||
println!();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
println!("{}", f.list(&torrents)?);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_status(status: &TorrentStatus) {
|
fn sort_torrents(torrents: &mut [tora_proto::TorrentStatus], key: SortKey) {
|
||||||
let state = State::try_from(status.state).unwrap_or(State::Unspecified);
|
match key {
|
||||||
println!("id: {}", status.id);
|
SortKey::Name => torrents.sort_by(|a, b| a.name.cmp(&b.name)),
|
||||||
println!("info_hash: {}", status.info_hash);
|
SortKey::Progress => torrents.sort_by_key(|t| std::cmp::Reverse(pct(t))),
|
||||||
println!(
|
SortKey::Size => torrents.sort_by_key(|t| std::cmp::Reverse(t.total_bytes)),
|
||||||
"name: {}",
|
SortKey::Speed => torrents.sort_by_key(|t| std::cmp::Reverse(t.download_speed_bps)),
|
||||||
if status.name.is_empty() {
|
SortKey::Added => {}
|
||||||
"(unknown)"
|
}
|
||||||
} else {
|
}
|
||||||
&status.name
|
|
||||||
}
|
fn pct(t: &tora_proto::TorrentStatus) -> u64 {
|
||||||
);
|
t.downloaded_bytes
|
||||||
println!("state: {}", state.as_str_name());
|
.checked_mul(100)
|
||||||
println!(
|
.and_then(|v| v.checked_div(t.total_bytes))
|
||||||
"progress: {} / {} bytes",
|
.unwrap_or(0)
|
||||||
status.downloaded_bytes, status.total_bytes
|
}
|
||||||
);
|
|
||||||
println!("output_path: {}", status.output_path);
|
async fn remove(socket: &Path, id: String, delete_files: bool, f: &dyn Formatter) -> Result<()> {
|
||||||
if !status.error_message.is_empty() {
|
let channel = connect(socket).await?;
|
||||||
println!("error: {}", status.error_message);
|
let mut client = TorrentsClient::new(channel);
|
||||||
|
|
||||||
|
tokio::time::timeout(
|
||||||
|
RPC_TIMEOUT,
|
||||||
|
client.remove(RemoveRequest { id, delete_files }),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.context("remove request timed out")?
|
||||||
|
.context("Remove RPC failed")?;
|
||||||
|
|
||||||
|
println!("{}", f.remove(delete_files)?);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn pause(socket: &Path, id: String, f: &dyn Formatter) -> Result<()> {
|
||||||
|
let channel = connect(socket).await?;
|
||||||
|
let mut client = TorrentsClient::new(channel);
|
||||||
|
|
||||||
|
tokio::time::timeout(RPC_TIMEOUT, client.pause(PauseRequest { id }))
|
||||||
|
.await
|
||||||
|
.context("pause request timed out")?
|
||||||
|
.context("Pause RPC failed")?;
|
||||||
|
|
||||||
|
println!("{}", f.pause()?);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn resume(socket: &Path, id: String, f: &dyn Formatter) -> Result<()> {
|
||||||
|
let channel = connect(socket).await?;
|
||||||
|
let mut client = TorrentsClient::new(channel);
|
||||||
|
|
||||||
|
tokio::time::timeout(RPC_TIMEOUT, client.resume(ResumeRequest { id }))
|
||||||
|
.await
|
||||||
|
.context("resume request timed out")?
|
||||||
|
.context("Resume RPC failed")?;
|
||||||
|
|
||||||
|
println!("{}", f.resume()?);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn start_daemon(
|
||||||
|
socket: &Path,
|
||||||
|
pid_file: &Path,
|
||||||
|
database_url: &str,
|
||||||
|
download_dir: Option<PathBuf>,
|
||||||
|
f: &dyn Formatter,
|
||||||
|
) -> Result<()> {
|
||||||
|
let mut cmd = std::process::Command::new("torad");
|
||||||
|
cmd.arg("--database-url").arg(database_url);
|
||||||
|
cmd.arg("--socket").arg(socket);
|
||||||
|
cmd.arg("--pid-file").arg(pid_file);
|
||||||
|
if let Some(dir) = download_dir {
|
||||||
|
cmd.arg("--download-dir").arg(dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.stdin(std::process::Stdio::null());
|
||||||
|
cmd.stdout(std::process::Stdio::null());
|
||||||
|
cmd.stderr(std::process::Stdio::null());
|
||||||
|
|
||||||
|
cmd.spawn()
|
||||||
|
.context("failed to spawn torad - is the binary installed and on PATH?")?;
|
||||||
|
|
||||||
|
println!("{}", f.start()?);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn stop_daemon(socket: &Path, pid_file: &Path, f: &dyn Formatter) -> Result<()> {
|
||||||
|
let pid_str = std::fs::read_to_string(pid_file).with_context(|| {
|
||||||
|
format!(
|
||||||
|
"failed to read PID file at {} - is torad running?",
|
||||||
|
pid_file.display()
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
let pid: i32 = pid_str
|
||||||
|
.trim()
|
||||||
|
.parse()
|
||||||
|
.with_context(|| format!("invalid PID in file: {pid_str:?}"))?;
|
||||||
|
|
||||||
|
let result = nix::sys::signal::kill(
|
||||||
|
nix::unistd::Pid::from_raw(pid),
|
||||||
|
nix::sys::signal::Signal::SIGTERM,
|
||||||
|
);
|
||||||
|
|
||||||
|
match result {
|
||||||
|
Ok(()) => {
|
||||||
|
let _ = std::fs::remove_file(socket);
|
||||||
|
let _ = std::fs::remove_file(pid_file);
|
||||||
|
println!("{}", f.stop()?);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Err(nix::errno::Errno::ESRCH) => {
|
||||||
|
let _ = std::fs::remove_file(socket);
|
||||||
|
let _ = std::fs::remove_file(pid_file);
|
||||||
|
anyhow::bail!("torad (PID {pid}) is not running - cleaned up stale PID file")
|
||||||
|
}
|
||||||
|
Err(err) => Err(err).context("failed to send SIGTERM to torad"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -7,6 +7,7 @@ edition = "2024"
|
|||||||
anyhow = "1.0.103"
|
anyhow = "1.0.103"
|
||||||
clap = { version = "4.6.1", features = ["derive", "env"] }
|
clap = { version = "4.6.1", features = ["derive", "env"] }
|
||||||
librqbit = { version = "8.1.1", default-features = false, features = ["rust-tls", "http-api-client"] }
|
librqbit = { version = "8.1.1", default-features = false, features = ["rust-tls", "http-api-client"] }
|
||||||
|
librqbit-core = { version = "5.0.0", default-features = false }
|
||||||
sqlx = { version = "0.9.0", features = ["postgres", "runtime-tokio", "tls-rustls", "uuid"] }
|
sqlx = { version = "0.9.0", features = ["postgres", "runtime-tokio", "tls-rustls", "uuid"] }
|
||||||
tokio = { version = "1.52.3", features = ["rt-multi-thread", "macros", "net", "signal"] }
|
tokio = { version = "1.52.3", features = ["rt-multi-thread", "macros", "net", "signal"] }
|
||||||
tokio-stream = { version = "0.1.18", features = ["net"] }
|
tokio-stream = { version = "0.1.18", features = ["net"] }
|
||||||
|
|||||||
+29
-1
@@ -38,7 +38,6 @@ pub struct TorrentRow {
|
|||||||
pub error_message: Option<String>,
|
pub error_message: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Insert a new pending torrent, or return the existing row if the info_hash is already tracked.
|
|
||||||
pub async fn insert_pending(
|
pub async fn insert_pending(
|
||||||
pool: &PgPool,
|
pool: &PgPool,
|
||||||
info_hash: &str,
|
info_hash: &str,
|
||||||
@@ -80,6 +79,17 @@ pub async fn list(pool: &PgPool) -> Result<Vec<TorrentRow>> {
|
|||||||
Ok(rows)
|
Ok(rows)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn delete(pool: &PgPool, id: Uuid) -> Result<Option<TorrentRow>> {
|
||||||
|
let row = sqlx::query_as::<_, TorrentRow>(
|
||||||
|
"DELETE FROM torrents WHERE id = $1
|
||||||
|
RETURNING id, info_hash, name, source, output_path, total_bytes, downloaded_bytes, state, error_message",
|
||||||
|
)
|
||||||
|
.bind(id)
|
||||||
|
.fetch_optional(pool)
|
||||||
|
.await?;
|
||||||
|
Ok(row)
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn list_pending(pool: &PgPool) -> Result<Vec<TorrentRow>> {
|
pub async fn list_pending(pool: &PgPool) -> Result<Vec<TorrentRow>> {
|
||||||
let rows = sqlx::query_as::<_, TorrentRow>(
|
let rows = sqlx::query_as::<_, TorrentRow>(
|
||||||
"SELECT id, info_hash, name, source, output_path, total_bytes, downloaded_bytes, state, error_message
|
"SELECT id, info_hash, name, source, output_path, total_bytes, downloaded_bytes, state, error_message
|
||||||
@@ -90,6 +100,24 @@ pub async fn list_pending(pool: &PgPool) -> Result<Vec<TorrentRow>> {
|
|||||||
Ok(rows)
|
Ok(rows)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn find_ids_by_prefix(pool: &PgPool, prefix: &str) -> Result<Vec<Uuid>> {
|
||||||
|
let rows =
|
||||||
|
sqlx::query_scalar::<_, Uuid>("SELECT id FROM torrents WHERE id::text LIKE $1 || '%'")
|
||||||
|
.bind(prefix)
|
||||||
|
.fetch_all(pool)
|
||||||
|
.await?;
|
||||||
|
Ok(rows)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn set_state(pool: &PgPool, id: Uuid, state: TorrentState) -> Result<()> {
|
||||||
|
sqlx::query("UPDATE torrents SET state = $2, updated_at = now() WHERE id = $1")
|
||||||
|
.bind(id)
|
||||||
|
.bind(state)
|
||||||
|
.execute(pool)
|
||||||
|
.await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Progress<'a> {
|
pub struct Progress<'a> {
|
||||||
pub name: Option<&'a str>,
|
pub name: Option<&'a str>,
|
||||||
pub total_bytes: i64,
|
pub total_bytes: i64,
|
||||||
|
|||||||
@@ -1,11 +1,17 @@
|
|||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
use librqbit::Magnet;
|
use librqbit::Magnet;
|
||||||
|
|
||||||
/// Extract the BTIH info hash from a magnet link, normalized to lowercase hex.
|
pub fn info_hash(source: &str) -> Result<String> {
|
||||||
pub fn info_hash(magnet: &str) -> Result<String> {
|
if source.starts_with("magnet:") {
|
||||||
let parsed = Magnet::parse(magnet).context("failed to parse magnet link")?;
|
let parsed = Magnet::parse(source).context("failed to parse magnet link")?;
|
||||||
let id20 = parsed
|
let id20 = parsed
|
||||||
.as_id20()
|
.as_id20()
|
||||||
.context("magnet link has no v1 (BTIH) info hash")?;
|
.context("magnet link has no v1 (BTIH) info hash")?;
|
||||||
Ok(id20.as_string())
|
return Ok(id20.as_string());
|
||||||
|
}
|
||||||
|
let bytes =
|
||||||
|
std::fs::read(source).with_context(|| format!("failed to read torrent file: {source}"))?;
|
||||||
|
let parsed = librqbit::torrent_from_bytes_ext::<Vec<u8>>(&bytes)
|
||||||
|
.context("failed to parse .torrent file")?;
|
||||||
|
Ok(parsed.meta.info_hash.as_string())
|
||||||
}
|
}
|
||||||
|
|||||||
+250
-21
@@ -4,12 +4,15 @@ use std::sync::Arc;
|
|||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
|
use librqbit::api::TorrentIdOrHash;
|
||||||
use librqbit::{AddTorrent, AddTorrentOptions, ManagedTorrent, Session, TorrentStatsState};
|
use librqbit::{AddTorrent, AddTorrentOptions, ManagedTorrent, Session, TorrentStatsState};
|
||||||
|
use librqbit_core::Id20;
|
||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
use tonic::{Request, Response, Status};
|
use tonic::{Request, Response, Status};
|
||||||
use tora_proto::{
|
use tora_proto::{
|
||||||
AddRequest, AddResponse, ListRequest, ListResponse, State as ProtoState, StatusRequest,
|
AddRequest, AddResponse, ListRequest, ListResponse, PauseRequest, PauseResponse, RemoveRequest,
|
||||||
|
RemoveResponse, ResumeRequest, ResumeResponse, State as ProtoState, StatusRequest,
|
||||||
TorrentStatus, Torrents,
|
TorrentStatus, Torrents,
|
||||||
};
|
};
|
||||||
use tracing::{error, info, warn};
|
use tracing::{error, info, warn};
|
||||||
@@ -19,6 +22,13 @@ use crate::db::{self, TorrentRow, TorrentState};
|
|||||||
use crate::magnet;
|
use crate::magnet;
|
||||||
|
|
||||||
const POLL_INTERVAL: Duration = Duration::from_secs(2);
|
const POLL_INTERVAL: Duration = Duration::from_secs(2);
|
||||||
|
const BYTES_PER_MIB: f64 = 1_048_576.0;
|
||||||
|
|
||||||
|
pub enum ResolveError {
|
||||||
|
NotFound,
|
||||||
|
Ambiguous(usize),
|
||||||
|
Internal(anyhow::Error),
|
||||||
|
}
|
||||||
|
|
||||||
pub struct TorrentManager {
|
pub struct TorrentManager {
|
||||||
pool: PgPool,
|
pool: PgPool,
|
||||||
@@ -55,12 +65,100 @@ impl TorrentManager {
|
|||||||
db::insert_pending(&self.pool, &info_hash, magnet, &output_path).await
|
db::insert_pending(&self.pool, &info_hash, magnet, &output_path).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get(&self, id: Uuid) -> Result<Option<TorrentRow>> {
|
pub async fn get_status(&self, id: Uuid) -> Result<Option<TorrentStatus>> {
|
||||||
db::get(&self.pool, id).await
|
let Some(row) = db::get(&self.pool, id).await? else {
|
||||||
|
return Ok(None);
|
||||||
|
};
|
||||||
|
let tracked = self.tracked.lock().await;
|
||||||
|
let live = extract_live_stats(&tracked, &row.id);
|
||||||
|
Ok(Some(row_to_status(row, live)))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn list(&self) -> Result<Vec<TorrentRow>> {
|
pub async fn list_statuses(&self) -> Result<Vec<TorrentStatus>> {
|
||||||
db::list(&self.pool).await
|
let rows = db::list(&self.pool).await?;
|
||||||
|
let tracked = self.tracked.lock().await;
|
||||||
|
Ok(rows
|
||||||
|
.into_iter()
|
||||||
|
.map(|row| {
|
||||||
|
let live = extract_live_stats(&tracked, &row.id);
|
||||||
|
row_to_status(row, live)
|
||||||
|
})
|
||||||
|
.collect())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn resolve_id(&self, id_or_prefix: &str) -> Result<Uuid, ResolveError> {
|
||||||
|
if let Ok(uuid) = Uuid::parse_str(id_or_prefix) {
|
||||||
|
return Ok(uuid);
|
||||||
|
}
|
||||||
|
let matches = db::find_ids_by_prefix(&self.pool, id_or_prefix)
|
||||||
|
.await
|
||||||
|
.map_err(ResolveError::Internal)?;
|
||||||
|
match matches.len() {
|
||||||
|
0 => Err(ResolveError::NotFound),
|
||||||
|
1 => Ok(matches[0]),
|
||||||
|
_ => Err(ResolveError::Ambiguous(matches.len())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn remove(&self, id: Uuid, delete_files: bool) -> Result<Option<TorrentRow>> {
|
||||||
|
let row = db::get(&self.pool, id).await?;
|
||||||
|
if let Some(ref row) = row {
|
||||||
|
let mut tracked = self.tracked.lock().await;
|
||||||
|
tracked.remove(&id);
|
||||||
|
drop(tracked);
|
||||||
|
|
||||||
|
if let Ok(info_hash) = row.info_hash.parse::<Id20>()
|
||||||
|
&& let Err(err) = self
|
||||||
|
.session
|
||||||
|
.delete(TorrentIdOrHash::Hash(info_hash), delete_files)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
warn!(id = %id, error = %err, "failed to delete torrent from session");
|
||||||
|
}
|
||||||
|
|
||||||
|
if delete_files {
|
||||||
|
let _ = std::fs::remove_dir_all(&row.output_path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
db::delete(&self.pool, id).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn pause(&self, id: Uuid) -> Result<Option<TorrentRow>> {
|
||||||
|
let row = db::get(&self.pool, id).await?;
|
||||||
|
if row.is_none() {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
let tracked = self.tracked.lock().await;
|
||||||
|
if let Some(handle) = tracked.get(&id) {
|
||||||
|
self.session
|
||||||
|
.pause(handle)
|
||||||
|
.await
|
||||||
|
.context("failed to pause torrent in librqbit session")?;
|
||||||
|
} else {
|
||||||
|
// Torrent not yet tracked by librqbit (still pending in DB).
|
||||||
|
// Set state to Paused so the poller won't pick it up.
|
||||||
|
db::set_state(&self.pool, id, TorrentState::Paused).await?;
|
||||||
|
}
|
||||||
|
Ok(row)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn resume(&self, id: Uuid) -> Result<Option<TorrentRow>> {
|
||||||
|
let row = db::get(&self.pool, id).await?;
|
||||||
|
if row.is_none() {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
let tracked = self.tracked.lock().await;
|
||||||
|
if let Some(handle) = tracked.get(&id) {
|
||||||
|
self.session
|
||||||
|
.unpause(handle)
|
||||||
|
.await
|
||||||
|
.context("failed to resume torrent in librqbit session")?;
|
||||||
|
} else {
|
||||||
|
// Torrent not tracked by librqbit. Set state to Pending so
|
||||||
|
// the poller picks it up again.
|
||||||
|
db::set_state(&self.pool, id, TorrentState::Pending).await?;
|
||||||
|
}
|
||||||
|
Ok(row)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn spawn_poller(self: Arc<Self>) {
|
pub fn spawn_poller(self: Arc<Self>) {
|
||||||
@@ -96,11 +194,14 @@ impl TorrentManager {
|
|||||||
overwrite: true,
|
overwrite: true,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
match self
|
let add = match AddTorrent::from_cli_argument(&row.source) {
|
||||||
.session
|
Ok(add) => add,
|
||||||
.add_torrent(AddTorrent::from_url(row.source.clone()), Some(options))
|
Err(err) => {
|
||||||
.await
|
error!(id = %row.id, source = %row.source, error = %err, "failed to parse torrent source");
|
||||||
{
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
match self.session.add_torrent(add, Some(options)).await {
|
||||||
Ok(response) => match response.into_handle() {
|
Ok(response) => match response.into_handle() {
|
||||||
Some(handle) => {
|
Some(handle) => {
|
||||||
info!(id = %row.id, info_hash = %row.info_hash, "torrent added to session");
|
info!(id = %row.id, info_hash = %row.info_hash, "torrent added to session");
|
||||||
@@ -155,6 +256,59 @@ impl TorrentManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct LiveTorrentStats {
|
||||||
|
download_speed_bps: u64,
|
||||||
|
eta_seconds: u64,
|
||||||
|
uploaded_bytes: u64,
|
||||||
|
upload_speed_bps: u64,
|
||||||
|
peers: u32,
|
||||||
|
seeds: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extract_live_stats(tracked: &HashMap<Uuid, Arc<ManagedTorrent>>, id: &Uuid) -> LiveTorrentStats {
|
||||||
|
let Some(handle) = tracked.get(id) else {
|
||||||
|
return LiveTorrentStats::default();
|
||||||
|
};
|
||||||
|
let stats = handle.stats();
|
||||||
|
let uploaded_bytes = stats.uploaded_bytes;
|
||||||
|
|
||||||
|
let Some(live) = stats.live else {
|
||||||
|
return LiveTorrentStats {
|
||||||
|
uploaded_bytes,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
let download_bps = if live.download_speed.mbps > 0.0 {
|
||||||
|
(live.download_speed.mbps * BYTES_PER_MIB) as u64
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
let upload_bps = if live.upload_speed.mbps > 0.0 {
|
||||||
|
(live.upload_speed.mbps * BYTES_PER_MIB) as u64
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
let total = stats.total_bytes;
|
||||||
|
let downloaded = stats.progress_bytes;
|
||||||
|
let remaining = total.saturating_sub(downloaded);
|
||||||
|
let eta = if download_bps > 0 {
|
||||||
|
(remaining as f64 / download_bps as f64) as u64
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
LiveTorrentStats {
|
||||||
|
download_speed_bps: download_bps,
|
||||||
|
eta_seconds: eta,
|
||||||
|
uploaded_bytes,
|
||||||
|
upload_speed_bps: upload_bps,
|
||||||
|
peers: live.snapshot.peer_stats.live as u32,
|
||||||
|
seeds: live.snapshot.peer_stats.not_needed as u32,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn proto_state(state: TorrentState) -> ProtoState {
|
fn proto_state(state: TorrentState) -> ProtoState {
|
||||||
match state {
|
match state {
|
||||||
TorrentState::Pending => ProtoState::Pending,
|
TorrentState::Pending => ProtoState::Pending,
|
||||||
@@ -165,7 +319,7 @@ fn proto_state(state: TorrentState) -> ProtoState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn row_to_status(row: TorrentRow) -> TorrentStatus {
|
fn row_to_status(row: TorrentRow, live: LiveTorrentStats) -> TorrentStatus {
|
||||||
TorrentStatus {
|
TorrentStatus {
|
||||||
id: row.id.to_string(),
|
id: row.id.to_string(),
|
||||||
info_hash: row.info_hash,
|
info_hash: row.info_hash,
|
||||||
@@ -176,6 +330,12 @@ fn row_to_status(row: TorrentRow) -> TorrentStatus {
|
|||||||
downloaded_bytes: row.downloaded_bytes as u64,
|
downloaded_bytes: row.downloaded_bytes as u64,
|
||||||
state: proto_state(row.state) as i32,
|
state: proto_state(row.state) as i32,
|
||||||
error_message: row.error_message.unwrap_or_default(),
|
error_message: row.error_message.unwrap_or_default(),
|
||||||
|
download_speed_bps: live.download_speed_bps,
|
||||||
|
eta_seconds: live.eta_seconds,
|
||||||
|
uploaded_bytes: live.uploaded_bytes,
|
||||||
|
upload_speed_bps: live.upload_speed_bps,
|
||||||
|
peers: live.peers,
|
||||||
|
seeds: live.seeds,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -191,6 +351,16 @@ impl GrpcTorrents {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn resolve_err(e: ResolveError) -> Status {
|
||||||
|
match e {
|
||||||
|
ResolveError::NotFound => Status::not_found("torrent not found"),
|
||||||
|
ResolveError::Ambiguous(n) => {
|
||||||
|
Status::invalid_argument(format!("ambiguous prefix, matches {n} torrents"))
|
||||||
|
}
|
||||||
|
ResolveError::Internal(err) => Status::internal(err.to_string()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[tonic::async_trait]
|
#[tonic::async_trait]
|
||||||
impl Torrents for GrpcTorrents {
|
impl Torrents for GrpcTorrents {
|
||||||
async fn add(&self, request: Request<AddRequest>) -> Result<Response<AddResponse>, Status> {
|
async fn add(&self, request: Request<AddRequest>) -> Result<Response<AddResponse>, Status> {
|
||||||
@@ -210,25 +380,84 @@ impl Torrents for GrpcTorrents {
|
|||||||
&self,
|
&self,
|
||||||
request: Request<StatusRequest>,
|
request: Request<StatusRequest>,
|
||||||
) -> Result<Response<TorrentStatus>, Status> {
|
) -> Result<Response<TorrentStatus>, Status> {
|
||||||
let id = Uuid::parse_str(&request.into_inner().id)
|
let id = self
|
||||||
.map_err(|_| Status::invalid_argument("invalid id"))?;
|
|
||||||
let row = self
|
|
||||||
.manager
|
.manager
|
||||||
.get(id)
|
.resolve_id(&request.into_inner().id)
|
||||||
|
.await
|
||||||
|
.map_err(resolve_err)?;
|
||||||
|
let status = self
|
||||||
|
.manager
|
||||||
|
.get_status(id)
|
||||||
.await
|
.await
|
||||||
.map_err(|err| Status::internal(err.to_string()))?
|
.map_err(|err| Status::internal(err.to_string()))?
|
||||||
.ok_or_else(|| Status::not_found("torrent not found"))?;
|
.ok_or_else(|| Status::not_found("torrent not found"))?;
|
||||||
Ok(Response::new(row_to_status(row)))
|
Ok(Response::new(status))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn list(&self, _request: Request<ListRequest>) -> Result<Response<ListResponse>, Status> {
|
async fn list(&self, _request: Request<ListRequest>) -> Result<Response<ListResponse>, Status> {
|
||||||
let rows = self
|
let torrents = self
|
||||||
.manager
|
.manager
|
||||||
.list()
|
.list_statuses()
|
||||||
.await
|
.await
|
||||||
.map_err(|err| Status::internal(err.to_string()))?;
|
.map_err(|err| Status::internal(err.to_string()))?;
|
||||||
Ok(Response::new(ListResponse {
|
Ok(Response::new(ListResponse { torrents }))
|
||||||
torrents: rows.into_iter().map(row_to_status).collect(),
|
}
|
||||||
}))
|
|
||||||
|
async fn remove(
|
||||||
|
&self,
|
||||||
|
request: Request<RemoveRequest>,
|
||||||
|
) -> Result<Response<RemoveResponse>, Status> {
|
||||||
|
let req = request.into_inner();
|
||||||
|
let id = self
|
||||||
|
.manager
|
||||||
|
.resolve_id(&req.id)
|
||||||
|
.await
|
||||||
|
.map_err(resolve_err)?;
|
||||||
|
let row = self
|
||||||
|
.manager
|
||||||
|
.remove(id, req.delete_files)
|
||||||
|
.await
|
||||||
|
.map_err(|err| Status::internal(err.to_string()))?
|
||||||
|
.ok_or_else(|| Status::not_found("torrent not found"))?;
|
||||||
|
info!(id = %id, name = ?row.name, "torrent removed");
|
||||||
|
Ok(Response::new(RemoveResponse {}))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn pause(
|
||||||
|
&self,
|
||||||
|
request: Request<PauseRequest>,
|
||||||
|
) -> Result<Response<PauseResponse>, Status> {
|
||||||
|
let id = self
|
||||||
|
.manager
|
||||||
|
.resolve_id(&request.into_inner().id)
|
||||||
|
.await
|
||||||
|
.map_err(resolve_err)?;
|
||||||
|
let row = self
|
||||||
|
.manager
|
||||||
|
.pause(id)
|
||||||
|
.await
|
||||||
|
.map_err(|err| Status::internal(err.to_string()))?
|
||||||
|
.ok_or_else(|| Status::not_found("torrent not found"))?;
|
||||||
|
info!(id = %id, name = ?row.name, "torrent paused");
|
||||||
|
Ok(Response::new(PauseResponse {}))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn resume(
|
||||||
|
&self,
|
||||||
|
request: Request<ResumeRequest>,
|
||||||
|
) -> Result<Response<ResumeResponse>, Status> {
|
||||||
|
let id = self
|
||||||
|
.manager
|
||||||
|
.resolve_id(&request.into_inner().id)
|
||||||
|
.await
|
||||||
|
.map_err(resolve_err)?;
|
||||||
|
let row = self
|
||||||
|
.manager
|
||||||
|
.resume(id)
|
||||||
|
.await
|
||||||
|
.map_err(|err| Status::internal(err.to_string()))?
|
||||||
|
.ok_or_else(|| Status::not_found("torrent not found"))?;
|
||||||
|
info!(id = %id, name = ?row.name, "torrent resumed");
|
||||||
|
Ok(Response::new(ResumeResponse {}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+32
-1
@@ -3,12 +3,18 @@ syntax = "proto3";
|
|||||||
package torrent;
|
package torrent;
|
||||||
|
|
||||||
service Torrents {
|
service Torrents {
|
||||||
// Add a torrent from a magnet link and start downloading it.
|
// Add a torrent from a magnet link or .torrent file and start downloading it.
|
||||||
rpc Add(AddRequest) returns (AddResponse);
|
rpc Add(AddRequest) returns (AddResponse);
|
||||||
// Get the current status of a single torrent.
|
// Get the current status of a single torrent.
|
||||||
rpc Status(StatusRequest) returns (TorrentStatus);
|
rpc Status(StatusRequest) returns (TorrentStatus);
|
||||||
// List all torrents known to the daemon.
|
// List all torrents known to the daemon.
|
||||||
rpc List(ListRequest) returns (ListResponse);
|
rpc List(ListRequest) returns (ListResponse);
|
||||||
|
// Remove a torrent from tracking.
|
||||||
|
rpc Remove(RemoveRequest) returns (RemoveResponse);
|
||||||
|
// Pause a downloading torrent.
|
||||||
|
rpc Pause(PauseRequest) returns (PauseResponse);
|
||||||
|
// Resume a paused torrent.
|
||||||
|
rpc Resume(ResumeRequest) returns (ResumeResponse);
|
||||||
}
|
}
|
||||||
|
|
||||||
message AddRequest {
|
message AddRequest {
|
||||||
@@ -31,6 +37,25 @@ message ListResponse {
|
|||||||
repeated TorrentStatus torrents = 1;
|
repeated TorrentStatus torrents = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message RemoveRequest {
|
||||||
|
string id = 1;
|
||||||
|
bool delete_files = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message RemoveResponse {}
|
||||||
|
|
||||||
|
message PauseRequest {
|
||||||
|
string id = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message PauseResponse {}
|
||||||
|
|
||||||
|
message ResumeRequest {
|
||||||
|
string id = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ResumeResponse {}
|
||||||
|
|
||||||
enum State {
|
enum State {
|
||||||
STATE_UNSPECIFIED = 0;
|
STATE_UNSPECIFIED = 0;
|
||||||
PENDING = 1;
|
PENDING = 1;
|
||||||
@@ -50,4 +75,10 @@ message TorrentStatus {
|
|||||||
uint64 downloaded_bytes = 7;
|
uint64 downloaded_bytes = 7;
|
||||||
State state = 8;
|
State state = 8;
|
||||||
string error_message = 9;
|
string error_message = 9;
|
||||||
|
uint64 download_speed_bps = 10;
|
||||||
|
uint64 eta_seconds = 11;
|
||||||
|
uint64 uploaded_bytes = 12;
|
||||||
|
uint64 upload_speed_bps = 13;
|
||||||
|
uint32 peers = 14;
|
||||||
|
uint32 seeds = 15;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user