From 721571b240827a3c7827712dc7d6242df4e5d460 Mon Sep 17 00:00:00 2001 From: Joel Wachsler Date: Sun, 10 Jul 2022 17:06:19 +0200 Subject: [PATCH] Add source files --- .devcontainer/Dockerfile | 20 + .devcontainer/devcontainer.json | 40 + .devcontainer/docker-compose.yml | 35 + .../qbittorrent-web-api.code-workspace | 17 + .devcontainer/setup.sh | 12 + .gitignore | 2 + Cargo.lock | 1134 ++ Cargo.toml | 16 + api-gen/Cargo.lock | 1187 ++ api-gen/Cargo.toml | 24 + api-gen/src/api-4_1.md | 3345 ++++ api-gen/src/group.rs | 455 + api-gen/src/lib.rs | 33 + api-gen/src/skeleton.rs | 104 + api-gen/src/util.rs | 19 + api-gen/tests/add_torrent.rs | 20 + api-gen/tests/another_struct_name.rs | 16 + api-gen/tests/default_parameters.rs | 26 + api-gen/tests/login.rs | 16 + api-gen/tests/logout.rs | 17 + api-gen/tests/return_type.rs | 19 + api-gen/tests/return_type_enum.rs | 24 + .../tests/return_type_with_optional_params.rs | 25 + api-gen/tests/tests.rs | 21 + api-gen/tests/without_parameters.rs | 20 + md-parser/Cargo.lock | 7 + md-parser/Cargo.toml | 6 + md-parser/src/lib.rs | 318 + parser/Cargo.lock | 54 + parser/Cargo.toml | 9 + parser/groups.txt | 5696 +++++++ parser/src/api-4_1.md | 3345 ++++ parser/src/group_parser/description.rs | 51 + parser/src/group_parser/mod.rs | 65 + parser/src/group_parser/parameters.rs | 48 + parser/src/group_parser/return_type.rs | 51 + parser/src/group_parser/url_parser.rs | 23 + parser/src/lib.rs | 85 + parser/src/object_types.rs | 38 + parser/src/types.rs | 119 + parser/src/util.rs | 27 + parser/token_tree.txt | 13793 ++++++++++++++++ src/lib.rs | 4 + 43 files changed, 30386 insertions(+) create mode 100644 .devcontainer/Dockerfile create mode 100644 .devcontainer/devcontainer.json create mode 100644 .devcontainer/docker-compose.yml create mode 100644 .devcontainer/qbittorrent-web-api.code-workspace create mode 100755 .devcontainer/setup.sh create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 api-gen/Cargo.lock create mode 100644 api-gen/Cargo.toml create mode 100644 api-gen/src/api-4_1.md create mode 100644 api-gen/src/group.rs create mode 100644 api-gen/src/lib.rs create mode 100644 api-gen/src/skeleton.rs create mode 100644 api-gen/src/util.rs create mode 100644 api-gen/tests/add_torrent.rs create mode 100644 api-gen/tests/another_struct_name.rs create mode 100644 api-gen/tests/default_parameters.rs create mode 100644 api-gen/tests/login.rs create mode 100644 api-gen/tests/logout.rs create mode 100644 api-gen/tests/return_type.rs create mode 100644 api-gen/tests/return_type_enum.rs create mode 100644 api-gen/tests/return_type_with_optional_params.rs create mode 100644 api-gen/tests/tests.rs create mode 100644 api-gen/tests/without_parameters.rs create mode 100644 md-parser/Cargo.lock create mode 100644 md-parser/Cargo.toml create mode 100644 md-parser/src/lib.rs create mode 100644 parser/Cargo.lock create mode 100644 parser/Cargo.toml create mode 100644 parser/groups.txt create mode 100644 parser/src/api-4_1.md create mode 100644 parser/src/group_parser/description.rs create mode 100644 parser/src/group_parser/mod.rs create mode 100644 parser/src/group_parser/parameters.rs create mode 100644 parser/src/group_parser/return_type.rs create mode 100644 parser/src/group_parser/url_parser.rs create mode 100644 parser/src/lib.rs create mode 100644 parser/src/object_types.rs create mode 100644 parser/src/types.rs create mode 100644 parser/src/util.rs create mode 100644 parser/token_tree.txt create mode 100644 src/lib.rs diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 0000000..57300f3 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,20 @@ +# See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.238.0/containers/rust/.devcontainer/base.Dockerfile + +# [Choice] Debian OS version (use bullseye on local arm64/Apple Silicon): buster, bullseye +ARG VARIANT="buster" +FROM mcr.microsoft.com/vscode/devcontainers/rust:0-${VARIANT} + +# [Optional] Uncomment this section to install additional packages. +RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ + && apt-get -y install --no-install-recommends fish + +USER vscode + +# RUN rustup default nightly \ +# && cargo install cargo-expand \ +# && rustup component add rustfmt \ +# && rustup component add clippy + +RUN cargo install cargo-expand \ + && rustup component add rustfmt \ + && rustup component add clippy diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..b2a00db --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,40 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at: +// https://github.com/microsoft/vscode-dev-containers/tree/v0.238.0/containers/rust +{ + "name": "Rust", + "dockerComposeFile": "docker-compose.yml", + "service": "app", + "workspaceFolder": "/workspace", + // Configure tool-specific properties. + "customizations": { + // Configure properties specific to VS Code. + "vscode": { + // Set *default* container specific settings.json values on container create. + "settings": { + "lldb.executable": "/usr/bin/lldb", + // VS Code don't watch files under ./target + "files.watcherExclude": { + "**/target/**": true + }, + "rust-analyzer.checkOnSave.command": "clippy" + }, + // Add the IDs of extensions you want installed when the container is created. + "extensions": [ + "vadimcn.vscode-lldb", + "mutantdino.resourcemonitor", + "rust-lang.rust-analyzer", + "tamasfe.even-better-toml", + "serayuzgur.crates", + "redhat.vscode-yaml" + ] + } + }, + // Use 'forwardPorts' to make a list of ports inside the container available locally. + "forwardPorts": [ + 8080 + ], + // Use 'postCreateCommand' to run commands after the container is created. + "postCreateCommand": "./.devcontainer/setup.sh", + // Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root. + "remoteUser": "vscode" +} \ No newline at end of file diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml new file mode 100644 index 0000000..c7f496b --- /dev/null +++ b/.devcontainer/docker-compose.yml @@ -0,0 +1,35 @@ +version: '3.8' + +services: + app: + build: + context: . + dockerfile: Dockerfile + args: + VARIANT: buster + CARGO_HOME: /workspace/.cargo + + volumes: + - ..:/workspace:cached + - /var/run/docker.sock:/var/run/docker.sock + + # Overrides default command so things don't shut down after the process ends. + command: sleep infinity + + security_opt: + - seccomp:unconfined + cap_add: + - SYS_PTRACE + + # Runs app on the same network as the database container, allows "forwardPorts" in devcontainer.json function. + network_mode: service:qbittorrent + + # Uncomment the next line to use a non-root user for all processes. + # user: node + + # Use "forwardPorts" in **devcontainer.json** to forward an app port locally. + # (Adding the "ports" property to this file will not forward from a Codespace.) + + qbittorrent: + image: linuxserver/qbittorrent:4.4.3 + restart: unless-stopped diff --git a/.devcontainer/qbittorrent-web-api.code-workspace b/.devcontainer/qbittorrent-web-api.code-workspace new file mode 100644 index 0000000..ce11255 --- /dev/null +++ b/.devcontainer/qbittorrent-web-api.code-workspace @@ -0,0 +1,17 @@ +{ + "folders": [ + { + "path": ".." + }, + { + "path": "../parser" + }, + { + "path": "../md-parser" + }, + { + "path": "../api-gen" + }, + ], + "settings": {} +} \ No newline at end of file diff --git a/.devcontainer/setup.sh b/.devcontainer/setup.sh new file mode 100755 index 0000000..175d9f3 --- /dev/null +++ b/.devcontainer/setup.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash + +set -ex + +git config pull.rebase true + +mkdir -p /home/vscode/.config/fish +cat < /home/vscode/.config/fish/config.fish +set fish_greeting "" +set -gx FORCE_COLOR true +fish_vi_key_bindings +EOF diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c5dd462 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +target +.env diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..b2ff261 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,1134 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +dependencies = [ + "memchr", +] + +[[package]] +name = "api-gen" +version = "0.1.0" +dependencies = [ + "case", + "parser", + "proc-macro2", + "quote", + "regex", + "serde", + "serde_json", + "syn", + "thiserror", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "base64" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bumpalo" +version = "3.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37ccbd214614c6783386c1af30caf03192f17891059cecc394b4fb119e363de3" + +[[package]] +name = "bytes" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" + +[[package]] +name = "case" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd6c0e7b807d60291f42f33f58480c0bfafe28ed08286446f45e463728cf9c1c" + +[[package]] +name = "cc" +version = "1.0.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "core-foundation" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" + +[[package]] +name = "encoding_rs" +version = "0.8.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "fastrand" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf" +dependencies = [ + "instant", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" +dependencies = [ + "matches", + "percent-encoding", +] + +[[package]] +name = "futures-channel" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3" + +[[package]] +name = "futures-sink" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868" + +[[package]] +name = "futures-task" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c66a976bf5909d801bbef33416c41372779507e7a6b3a5e25e4749c58f776a" + +[[package]] +name = "futures-util" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "pin-utils", +] + +[[package]] +name = "h2" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37a82c6d637fc9515a4694bbf1cb2457b79d81ce52b3108bdeea58b07dd34a57" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db0d4cf898abf0081f964436dc980e96670a0f36863e4b83aaacdb65c9d7ccc3" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "http" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "496ce29bb5a52785b44e0f7ca2847ae0bb839c9bd28f69acac9b99d461c0c04c" + +[[package]] +name = "httpdate" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" + +[[package]] +name = "hyper" +version = "0.14.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42dc3c131584288d375f2d07f822b0cb012d8c6fb899a5b9fdb3cb7eb9b6004f" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper", + "native-tls", + "tokio", + "tokio-native-tls", +] + +[[package]] +name = "idna" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c6392766afd7964e2531940894cffe4bd8d7d17dbc3c1c4857040fd4b33bdb3" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "ipnet" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879d54834c8c76457ef4293a689b2a8c59b076067ad77b15efafbb05f92a592b" + +[[package]] +name = "itoa" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d" + +[[package]] +name = "js-sys" +version = "0.3.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3fac17f7123a73ca62df411b1bf727ccc805daa070338fda671c86dac1bdc27" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" + +[[package]] +name = "lock_api" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "matches" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" + +[[package]] +name = "md-parser" +version = "0.1.0" + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "mime" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" + +[[package]] +name = "mime_guess" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" +dependencies = [ + "mime", + "unicase", +] + +[[package]] +name = "mio" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys", +] + +[[package]] +name = "native-tls" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd7e2f3618557f980e0b17e8856252eee3c97fa12c54dff0ca290fb6266ca4a9" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "num_cpus" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7709cef83f0c1f58f666e746a08b21e0085f7440fa6a29cc194d68aac97a4225" + +[[package]] +name = "openssl" +version = "0.10.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb81a6430ac911acb25fe5ac8f1d2af1b4ea8a4fdfda0f1ee4292af2e2d8eb0e" +dependencies = [ + "bitflags", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "835363342df5fba8354c5b453325b110ffd54044e588c539cf2f20a8014e4cb1" +dependencies = [ + "autocfg", + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-sys", +] + +[[package]] +name = "parser" +version = "0.1.0" +dependencies = [ + "case", + "md-parser", + "regex", +] + +[[package]] +name = "percent-encoding" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" + +[[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae" + +[[package]] +name = "proc-macro2" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd96a1e8ed2596c337f8eae5f24924ec83f5ad5ab21ea8e455d3566c69fbcaf7" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "qbittorrent-web-api" +version = "0.1.0" +dependencies = [ + "api-gen", + "reqwest", + "serde", + "serde_json", + "thiserror", + "tokio", +] + +[[package]] +name = "quote" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" + +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi", +] + +[[package]] +name = "reqwest" +version = "0.11.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b75aa69a3f06bbcc66ede33af2af253c6f7a86b1ca0033f60c580a27074fbf92" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-tls", + "ipnet", + "js-sys", + "lazy_static", + "log", + "mime", + "mime_guess", + "native-tls", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "serde_urlencoded", + "tokio", + "tokio-native-tls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg", +] + +[[package]] +name = "ryu" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695" + +[[package]] +name = "schannel" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88d6731146462ea25d9244b2ed5fd1d716d25c52e4d54aa4fb0f3c4e9854dbe2" +dependencies = [ + "lazy_static", + "windows-sys", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "security-framework" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dc14f172faf8a0194a3aded622712b0de276821addc574fa54fc0a1167e10dc" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0160a13a177a45bfb43ce71c01580998474f556ad854dcbca936dd2841a5c556" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "serde" +version = "1.0.138" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1578c6245786b9d168c5447eeacfb96856573ca56c9d68fdcf394be134882a47" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.138" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "023e9b1467aef8a10fb88f25611870ada9800ef7e22afce356bb0d2387b6f27c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82c2c1fdcd807d1098552c5b9a36e425e42e9fbd7c6a37a8425f390f781f7fa7" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" +dependencies = [ + "libc", +] + +[[package]] +name = "slab" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb703cfe953bccee95685111adeedb76fabe4e97549a58d16f03ea7b9367bb32" + +[[package]] +name = "smallvec" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83" + +[[package]] +name = "socket2" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "syn" +version = "1.0.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tempfile" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +dependencies = [ + "cfg-if", + "fastrand", + "libc", + "redox_syscall", + "remove_dir_all", + "winapi", +] + +[[package]] +name = "thiserror" +version = "1.0.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" + +[[package]] +name = "tokio" +version = "1.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c51a52ed6686dd62c320f9b89299e9dfb46f730c7a48e635c19f21d116cb1439" +dependencies = [ + "bytes", + "libc", + "memchr", + "mio", + "num_cpus", + "once_cell", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "winapi", +] + +[[package]] +name = "tokio-macros" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc463cd8deddc3770d20f9852143d50bf6094e640b485cb2e189a2099085ff45" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tracing" +version = "0.1.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a400e31aa60b9d44a52a8ee0343b5b18566b03a8321e0d321f695cf56e940160" +dependencies = [ + "cfg-if", + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7709595b8878a4965ce5e87ebf880a7d39c9afc6837721b21a5a816a8117d921" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" + +[[package]] +name = "unicase" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" +dependencies = [ + "version_check", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" + +[[package]] +name = "unicode-ident" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c" + +[[package]] +name = "unicode-normalization" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "url" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" +dependencies = [ + "form_urlencoded", + "idna", + "matches", + "percent-encoding", +] + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "want" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +dependencies = [ + "log", + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c53b543413a17a202f4be280a7e5c62a1c69345f5de525ee64f8cfdbc954994" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5491a68ab4500fa6b4d726bd67408630c3dbe9c4fe7bda16d5c82a1fd8c7340a" +dependencies = [ + "bumpalo", + "lazy_static", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de9a9cec1733468a8c657e57fa2413d2ae2c0129b95e87c5b72b8ace4d13f31f" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c441e177922bc58f1e12c022624b6216378e5febc2f0533e41ba443d505b80aa" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d94ac45fcf608c1f45ef53e748d35660f168490c10b23704c7779ab8f5c3048" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a89911bd99e5f3659ec4acf9c4d93b0a90fe4a2a11f15328472058edc5261be" + +[[package]] +name = "web-sys" +version = "0.3.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fed94beee57daf8dd7d51f2b15dc2bcde92d7a72304cdf662a4371008b71b90" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" +dependencies = [ + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" + +[[package]] +name = "windows_i686_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" + +[[package]] +name = "windows_i686_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" + +[[package]] +name = "winreg" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +dependencies = [ + "winapi", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..5c3fc2e --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "qbittorrent-web-api" +version = "0.1.0" +edition = "2021" + +[lib] +name = "qbittorrent_web_api" +path = "src/lib.rs" + +[dependencies] +reqwest = { version = "0.11.11", features = ["json", "multipart"] } +tokio = { version = "1.19.2", features = ["full"] } +api-gen = { path = "./api-gen" } +serde = { version = "1.0.138", features = ["derive"] } +serde_json = "1.0.82" +thiserror = "1.0.31" diff --git a/api-gen/Cargo.lock b/api-gen/Cargo.lock new file mode 100644 index 0000000..121f5d3 --- /dev/null +++ b/api-gen/Cargo.lock @@ -0,0 +1,1187 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +dependencies = [ + "memchr", +] + +[[package]] +name = "anyhow" +version = "1.0.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb07d2053ccdbe10e2af2995a2f116c1330396493dc1269f6a91d0ae82e19704" + +[[package]] +name = "api-gen" +version = "0.1.0" +dependencies = [ + "anyhow", + "case", + "parser", + "proc-macro2", + "quote", + "regex", + "reqwest", + "serde", + "serde_json", + "syn", + "thiserror", + "tokio", + "trybuild", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "base64" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bumpalo" +version = "3.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37ccbd214614c6783386c1af30caf03192f17891059cecc394b4fb119e363de3" + +[[package]] +name = "bytes" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" + +[[package]] +name = "case" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd6c0e7b807d60291f42f33f58480c0bfafe28ed08286446f45e463728cf9c1c" + +[[package]] +name = "cc" +version = "1.0.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "core-foundation" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" + +[[package]] +name = "dissimilar" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c97b9233581d84b8e1e689cdd3a47b6f69770084fc246e86a7f78b0d9c1d4a5" + +[[package]] +name = "encoding_rs" +version = "0.8.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "fastrand" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf" +dependencies = [ + "instant", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" +dependencies = [ + "matches", + "percent-encoding", +] + +[[package]] +name = "futures-channel" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3" + +[[package]] +name = "futures-sink" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868" + +[[package]] +name = "futures-task" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c66a976bf5909d801bbef33416c41372779507e7a6b3a5e25e4749c58f776a" + +[[package]] +name = "futures-util" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "pin-utils", +] + +[[package]] +name = "glob" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" + +[[package]] +name = "h2" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37a82c6d637fc9515a4694bbf1cb2457b79d81ce52b3108bdeea58b07dd34a57" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db0d4cf898abf0081f964436dc980e96670a0f36863e4b83aaacdb65c9d7ccc3" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "http" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "496ce29bb5a52785b44e0f7ca2847ae0bb839c9bd28f69acac9b99d461c0c04c" + +[[package]] +name = "httpdate" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" + +[[package]] +name = "hyper" +version = "0.14.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02c929dc5c39e335a03c405292728118860721b10190d98c2a0f0efd5baafbac" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper", + "native-tls", + "tokio", + "tokio-native-tls", +] + +[[package]] +name = "idna" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "ipnet" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879d54834c8c76457ef4293a689b2a8c59b076067ad77b15efafbb05f92a592b" + +[[package]] +name = "itoa" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d" + +[[package]] +name = "js-sys" +version = "0.3.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3fac17f7123a73ca62df411b1bf727ccc805daa070338fda671c86dac1bdc27" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" + +[[package]] +name = "lock_api" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "matches" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" + +[[package]] +name = "md-parser" +version = "0.1.0" + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "mime" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" + +[[package]] +name = "mime_guess" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" +dependencies = [ + "mime", + "unicase", +] + +[[package]] +name = "mio" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys", +] + +[[package]] +name = "native-tls" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd7e2f3618557f980e0b17e8856252eee3c97fa12c54dff0ca290fb6266ca4a9" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "num_cpus" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1" + +[[package]] +name = "openssl" +version = "0.10.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb81a6430ac911acb25fe5ac8f1d2af1b4ea8a4fdfda0f1ee4292af2e2d8eb0e" +dependencies = [ + "bitflags", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "835363342df5fba8354c5b453325b110ffd54044e588c539cf2f20a8014e4cb1" +dependencies = [ + "autocfg", + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-sys", +] + +[[package]] +name = "parser" +version = "0.1.0" +dependencies = [ + "case", + "md-parser", + "regex", +] + +[[package]] +name = "percent-encoding" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" + +[[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae" + +[[package]] +name = "proc-macro2" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd96a1e8ed2596c337f8eae5f24924ec83f5ad5ab21ea8e455d3566c69fbcaf7" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" + +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi", +] + +[[package]] +name = "reqwest" +version = "0.11.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b75aa69a3f06bbcc66ede33af2af253c6f7a86b1ca0033f60c580a27074fbf92" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-tls", + "ipnet", + "js-sys", + "lazy_static", + "log", + "mime", + "mime_guess", + "native-tls", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "serde_urlencoded", + "tokio", + "tokio-native-tls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg", +] + +[[package]] +name = "ryu" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695" + +[[package]] +name = "schannel" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88d6731146462ea25d9244b2ed5fd1d716d25c52e4d54aa4fb0f3c4e9854dbe2" +dependencies = [ + "lazy_static", + "windows-sys", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "security-framework" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dc14f172faf8a0194a3aded622712b0de276821addc574fa54fc0a1167e10dc" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0160a13a177a45bfb43ce71c01580998474f556ad854dcbca936dd2841a5c556" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "serde" +version = "1.0.138" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1578c6245786b9d168c5447eeacfb96856573ca56c9d68fdcf394be134882a47" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.138" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "023e9b1467aef8a10fb88f25611870ada9800ef7e22afce356bb0d2387b6f27c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82c2c1fdcd807d1098552c5b9a36e425e42e9fbd7c6a37a8425f390f781f7fa7" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" +dependencies = [ + "libc", +] + +[[package]] +name = "slab" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb703cfe953bccee95685111adeedb76fabe4e97549a58d16f03ea7b9367bb32" + +[[package]] +name = "smallvec" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1" + +[[package]] +name = "socket2" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "syn" +version = "1.0.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tempfile" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +dependencies = [ + "cfg-if", + "fastrand", + "libc", + "redox_syscall", + "remove_dir_all", + "winapi", +] + +[[package]] +name = "termcolor" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "thiserror" +version = "1.0.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" + +[[package]] +name = "tokio" +version = "1.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c51a52ed6686dd62c320f9b89299e9dfb46f730c7a48e635c19f21d116cb1439" +dependencies = [ + "bytes", + "libc", + "memchr", + "mio", + "num_cpus", + "once_cell", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "winapi", +] + +[[package]] +name = "tokio-macros" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc463cd8deddc3770d20f9852143d50bf6094e640b485cb2e189a2099085ff45" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + +[[package]] +name = "toml" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" +dependencies = [ + "serde", +] + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tracing" +version = "0.1.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a400e31aa60b9d44a52a8ee0343b5b18566b03a8321e0d321f695cf56e940160" +dependencies = [ + "cfg-if", + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b7358be39f2f274f322d2aaed611acc57f382e8eb1e5b48cb9ae30933495ce7" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" + +[[package]] +name = "trybuild" +version = "1.0.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "764b9e244b482a9b81bde596aa37aa6f1347bf8007adab25e59f901b32b4e0a0" +dependencies = [ + "dissimilar", + "glob", + "once_cell", + "serde", + "serde_derive", + "serde_json", + "termcolor", + "toml", +] + +[[package]] +name = "unicase" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" +dependencies = [ + "version_check", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" + +[[package]] +name = "unicode-ident" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c" + +[[package]] +name = "unicode-normalization" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "854cbdc4f7bc6ae19c820d44abdc3277ac3e1b2b93db20a636825d9322fb60e6" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "url" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" +dependencies = [ + "form_urlencoded", + "idna", + "matches", + "percent-encoding", +] + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "want" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +dependencies = [ + "log", + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c53b543413a17a202f4be280a7e5c62a1c69345f5de525ee64f8cfdbc954994" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5491a68ab4500fa6b4d726bd67408630c3dbe9c4fe7bda16d5c82a1fd8c7340a" +dependencies = [ + "bumpalo", + "lazy_static", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de9a9cec1733468a8c657e57fa2413d2ae2c0129b95e87c5b72b8ace4d13f31f" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c441e177922bc58f1e12c022624b6216378e5febc2f0533e41ba443d505b80aa" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d94ac45fcf608c1f45ef53e748d35660f168490c10b23704c7779ab8f5c3048" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a89911bd99e5f3659ec4acf9c4d93b0a90fe4a2a11f15328472058edc5261be" + +[[package]] +name = "web-sys" +version = "0.3.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fed94beee57daf8dd7d51f2b15dc2bcde92d7a72304cdf662a4371008b71b90" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" +dependencies = [ + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" + +[[package]] +name = "windows_i686_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" + +[[package]] +name = "windows_i686_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" + +[[package]] +name = "winreg" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +dependencies = [ + "winapi", +] diff --git a/api-gen/Cargo.toml b/api-gen/Cargo.toml new file mode 100644 index 0000000..61c26f2 --- /dev/null +++ b/api-gen/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "api-gen" +version = "0.1.0" +edition = "2021" + +[lib] +proc-macro = true + +[dependencies] +parser = { path = "../parser" } +syn = { version = "1.0.98", features = ["extra-traits"]} +quote = "1.0.20" +proc-macro2 = "1.0.40" +case = "1.0.0" +thiserror = "1.0.31" +serde = { version = "1.0.138", features = ["derive"] } +serde_json = "1.0.82" +regex = "1.6.0" + +[dev-dependencies] +trybuild = { version = "1.0.63", features = ["diff"] } +anyhow = "1.0.58" +tokio = { version = "1.19.2", features = ["full"] } +reqwest = { version = "0.11.11", features = ["json", "multipart"] } diff --git a/api-gen/src/api-4_1.md b/api-gen/src/api-4_1.md new file mode 100644 index 0000000..cf94502 --- /dev/null +++ b/api-gen/src/api-4_1.md @@ -0,0 +1,3345 @@ +This WebUI API documentation applies to qBittorrent v4.1+. For other WebUI API versions, visit [WebUI API](https://github.com/qbittorrent/qBittorrent/wiki#WebUI-API). + +# Table of Contents # + +1. [Changes](#changes) + 1. [API v2.0](#api-v20) + 1. [API v2.0.1](#api-v201) + 1. [API v2.0.2](#api-v202) + 1. [API v2.1.0](#api-v210) + 1. [API v2.1.1](#api-v211) + 1. [API v2.2.0](#api-v220) + 1. [API v2.2.1](#api-v221) + 1. [API v2.3.0](#api-v230) + 1. [API v2.4.0](#api-v240) + 1. [API v2.4.1](#api-v241) + 1. [API v2.5.0](#api-v250) + 1. [API v2.5.1](#api-v251) + 1. [API v2.6.0](#api-v260) + 1. [API v2.6.1](#api-v261) + 1. [API v2.6.2](#api-v262) + 1. [API v2.7.0](#api-v270) + 1. [API v2.8.0](#api-v280) + 1. [API v2.8.1](#api-v281) + 1. [API v2.8.2](#api-v282) + 1. [API v2.8.3](#api-v283) +1. [General information](#general-information) +1. [Authentication](#authentication) + 1. [Login](#login) + 1. [Logout](#logout) +1. [Application](#application) + 1. [Get application version](#get-application-version) + 1. [Get API version](#get-api-version) + 1. [Get build info](#get-build-info) + 1. [Shutdown application](#shutdown-application) + 1. [Get application preferences](#get-application-preferences) + 1. [Set application preferences](#set-application-preferences) + 1. [Get default save path](#get-default-save-path) +1. [Log](#log) + 1. [Get log](#get-log) + 1. [Get peer log](#get-peer-log) +1. [Sync](#sync) + 1. [Get main data](#get-main-data) + 1. [Get torrent peers data](#get-torrent-peers-data) +1. [Transfer info](#transfer-info) + 1. [Get global transfer info](#get-global-transfer-info) + 1. [Get alternative speed limits state](#get-alternative-speed-limits-state) + 1. [Toggle alternative speed limits](#toggle-alternative-speed-limits) + 1. [Get global download limit](#get-global-download-limit) + 1. [Set global download limit](#set-global-download-limit) + 1. [Get global upload limit](#get-global-upload-limit) + 1. [Set global upload limit](#set-global-upload-limit) + 1. [Ban peers](#ban-peers) +1. [Torrent management](#torrent-management) + 1. [Get torrent list](#get-torrent-list) + 1. [Get torrent generic properties](#get-torrent-generic-properties) + 1. [Get torrent trackers](#get-torrent-trackers) + 1. [Get torrent web seeds](#get-torrent-web-seeds) + 1. [Get torrent contents](#get-torrent-contents) + 1. [Get torrent pieces' states](#get-torrent-pieces-states) + 1. [Get torrent pieces' hashes](#get-torrent-pieces-hashes) + 1. [Pause torrents](#pause-torrents) + 1. [Resume torrents](#resume-torrents) + 1. [Delete torrents](#delete-torrents) + 1. [Recheck torrents](#recheck-torrents) + 1. [Reannounce torrents](#reannounce-torrents) + 1. [Edit trackers](#edit-trackers) + 1. [Remove trackers](#remove-trackers) + 1. [Add peers](#add-peers) + 1. [Add new torrent](#add-new-torrent) + 1. [Add trackers to torrent](#add-trackers-to-torrent) + 1. [Increase torrent priority](#increase-torrent-priority) + 1. [Decrease torrent priority](#decrease-torrent-priority) + 1. [Maximal torrent priority](#maximal-torrent-priority) + 1. [Minimal torrent priority](#minimal-torrent-priority) + 1. [Set file priority](#set-file-priority) + 1. [Get torrent download limit](#get-torrent-download-limit) + 1. [Set torrent download limit](#set-torrent-download-limit) + 1. [Set torrent share limit](#set-torrent-share-limit) + 1. [Get torrent upload limit](#get-torrent-upload-limit) + 1. [Set torrent upload limit](#set-torrent-upload-limit) + 1. [Set torrent location](#set-torrent-location) + 1. [Set torrent name](#set-torrent-name) + 1. [Set torrent category](#set-torrent-category) + 1. [Get all categories](#get-all-categories) + 1. [Add new category](#add-new-category) + 1. [Edit category](#edit-category) + 1. [Remove categories](#remove-categories) + 1. [Add torrent tags](#add-torrent-tags) + 1. [Remove torrent tags](#remove-torrent-tags) + 1. [Get all tags](#get-all-tags) + 1. [Create tags](#create-tags) + 1. [Delete tags](#delete-tags) + 1. [Set automatic torrent management](#set-automatic-torrent-management) + 1. [Toggle sequential download](#toggle-sequential-download) + 1. [Set first/last piece priority](#set-firstlast-piece-priority) + 1. [Set force start](#set-force-start) + 1. [Set super seeding](#set-super-seeding) + 1. [Rename file](#rename-file) + 1. [Rename folder](#rename-folder) +1. [RSS (experimental)](#rss-experimental) + 1. [Add folder](#add-folder) + 1. [Add feed](#add-feed) + 1. [Remove item](#remove-item) + 1. [Move item](#move-item) + 1. [Get all items](#get-all-items) + 1. [Mark as read](#mark-as-read) + 1. [Refresh item](#refresh-item) + 1. [Set auto-downloading rule](#set-auto-downloading-rule) + 1. [Rename auto-downloading rule](#rename-auto-downloading-rule) + 1. [Remove auto-downloading rule](#remove-auto-downloading-rule) + 1. [Get all auto-downloading rules](#get-all-auto-downloading-rules) + 1. [Get all articles matching a rule](#get-all-articles-matching-a-rule) +1. [Search](#search) + 1. [Start search](#start-search) + 1. [Stop search](#stop-search) + 1. [Get search status](#get-search-status) + 1. [Get search results](#get-search-results) + 1. [Delete search](#delete-search) + 1. [Get search plugins](#get-search-plugins) + 1. [Install search plugin](#install-search-plugin) + 1. [Uninstall search plugin](#uninstall-search-plugin) + 1. [Enable search plugin](#enable-search-plugin) + 1. [Update search plugins](#update-search-plugins) +1. [WebAPI versioning](#webapi-versioning) + +*** + +# Changes # + +## API v2.0 ## + +- New version naming scheme: X.Y.Z (where X - major version, Y - minor version, Z - release version) +- New API paths. All API methods are under `api/vX/` (where X is API major version) +- API methods are under new scopes + +## API v2.0.1 ## + +- Add `hashes` field to `/torrents/info` ([#8782](https://github.com/qbittorrent/qBittorrent/pull/8782)) +- Add `/torrents/setShareLimits/` method ([#8598](https://github.com/qbittorrent/qBittorrent/pull/8598)) + +## API v2.0.2 ## + +- Add `/torrents/reannounce` method ([#9229](https://github.com/qbittorrent/qBittorrent/pull/9229)) + +## API v2.1.0 ## + +- Change `/sync/maindata` `categories` field from `array` to `object` ([#9228](https://github.com/qbittorrent/qBittorrent/pull/9228)) +- Add `savePath` field to `/torrents/createCategory` ([#9228](https://github.com/qbittorrent/qBittorrent/pull/9228)). This method now requires the category to already exist and will not create new categories. +- Add `/torrents/editCategory` method ([#9228](https://github.com/qbittorrent/qBittorrent/pull/9228)) + +## API v2.1.1 ## + +- Add `/torrents/categories` method ([#9586](https://github.com/qbittorrent/qBittorrent/pull/9586)) +- Add `/search/` methods ([#8584](https://github.com/qbittorrent/qBittorrent/pull/8584)) +- Add `free_space_on_disk` field to `/sync/maindata` ([#8217](https://github.com/qbittorrent/qBittorrent/pull/8217)) + +## API v2.2.0 ## + +- Add `/torrents/editTracker` and `/torrents/removeTracker` methods ([#9375](https://github.com/qbittorrent/qBittorrent/pull/9375)) +- Add `tier`, `num_seeds`, `num_leeches`, and `num_downloaded` fields to `/torrents/trackers` ([#9375](https://github.com/qbittorrent/qBittorrent/pull/9375)) +- Change `status` field from translated string to an integer for `/torrents/trackers` ([#9375](https://github.com/qbittorrent/qBittorrent/pull/9375)) +- Change `/torrents/filePrio` `id` field to accept multiple ids ([#9541](https://github.com/qbittorrent/qBittorrent/pull/9541)) +- Throw additional errors for failed requests to `/torrents/filePrio` ([#9541](https://github.com/qbittorrent/qBittorrent/pull/9541)) +- Add `autoTMM` field to `/torrents/add` ([#9752](https://github.com/qbittorrent/qBittorrent/pull/9752)) +- Add various fields to `/app/getPreferences` and `/app/setPreferences` (`create_subfolder_enabled`, `start_paused_enabled`, `auto_delete_mode`, `preallocate_all`, `incomplete_files_ext`, `auto_tmm_enabled`, `torrent_changed_tmm_enabled`, `save_path_changed_tmm_enabled`, `category_changed_tmm_enabled`, `mail_notification_sender`, `limit_lan_peers`, `slow_torrent_dl_rate_threshold`, `slow_torrent_ul_rate_threshold`, `slow_torrent_inactive_timer`, `alternative_webui_enabled`, `alternative_webui_path`) ([#9752](https://github.com/qbittorrent/qBittorrent/pull/9752)) + +## API v2.2.1 ## + +- Add `rss/refreshItem` ([#11067](https://github.com/qbittorrent/qBittorrent/pull/11067)) + +## API v2.3.0 ## + +- Remove `web_ui_password` field from `/app/preferences`, this field is still writable in `/app/setPreferences` method ([#9942](https://github.com/qbittorrent/qBittorrent/pull/9942)) +- Add `/app/buildInfo` method ([#10096](https://github.com/qbittorrent/qBittorrent/pull/10096)) +- Always use `/` as path separator in `/torrents/files` response ([#10153](https://github.com/qbittorrent/qBittorrent/pull/10153/)) +- Add `/torrents/addPeers` and `/transfer/banPeers` methods ([#10158](https://github.com/qbittorrent/qBittorrent/pull/10158)) +- Add `/torrents/addTags`, `/torrents/removeTags`, `/torrents/tags`, `/torrents/createTags`, `/torrents/deleteTags` methods ([#10527](https://github.com/qbittorrent/qBittorrent/pull/10527)) + +## API v2.4.0 ## + +- Add `/torrents/renameFile` method ([#11029](https://github.com/qbittorrent/qBittorrent/pull/11029)) + +## API v2.4.1 ## + +- Add `stalled`, `stalled_uploading` and `stalled_downloading` as possible values for the `filter` parameter in `/torrents/info` ([#11825](https://github.com/qbittorrent/qBittorrent/pull/11825)) +- Add various fields to `/app/preferences` and `/app/setPreferences` (`piece_extent_affinity`, `web_ui_secure_cookie_enabled`, `web_ui_max_auth_fail_count`, `web_ui_ban_duration`, `stop_tracker_timeout`) ([#11781](https://github.com/qbittorrent/qBittorrent/pull/11781), [#11726](https://github.com/qbittorrent/qBittorrent/pull/11726), [#12004](https://github.com/qbittorrent/qBittorrent/pull/12004), [#11834](https://github.com/qbittorrent/qBittorrent/pull/11834)) + +## API v2.5.0 ## +- Removes `enable_super_seeding` as fields from `/app/preferences` and `/app/setPreferences` ([#12423](https://github.com/qbittorrent/qBittorrent/pull/12423)) + +## API v2.5.1 ## +- Add `web_ui_use_custom_http_headers_enabled`, `web_ui_custom_http_headers`, `rss_download_repack_proper_episodes` and `rss_smart_episode_filters` as fields to `/app/preferences` and `/app/setPreferences` ([#12579](https://github.com/qbittorrent/qBittorrent/pull/12579), [#12549](https://github.com/qbittorrent/qBittorrent/pull/12549)) +- Add `/rss/markAsRead` and `/rss/matchingArticles` methods ([#12549](https://github.com/qbittorrent/qBittorrent/pull/12549)) + +## API v2.6.0 ## +- Removed `/search/categories` method and modified `/search/plugins` method's response ([#12705](https://github.com/qbittorrent/qBittorrent/pull/12705)) + +## API v2.6.1 ## +- Exposed `contentPath` via the `content_path` field in the response to `/torrents/info` ([#13625](https://github.com/qbittorrent/qBittorrent/pull/13625)) + +## API v2.6.2 ## +- Added `tags` optional field to `/torrents/add` ([#13882](https://github.com/qbittorrent/qBittorrent/pull/13882)) + +## API v2.8.0 ## +- Added `/torrents/renameFolder` method and modified `/torrents/renameFile` method's parameters ([#13995](https://github.com/qbittorrent/qBittorrent/pull/13995)) + +Note that this change was released in qBittorrent v4.3.3, but the WebAPI version was incorrectly set to v2.7.0 (see [#14275](https://github.com/qbittorrent/qBittorrent/pull/14275#issuecomment-766310862) for details) + +## API v2.8.1 ## +- Added `ratioLimit` and `seedingTimeLimit` optional fields to `/torrents/add` ([#14519](https://github.com/qbittorrent/qBittorrent/pull/14519)) +- Added `seeding_time` field to `/torrents/info` ([#14554](https://github.com/qbittorrent/qBittorrent/pull/14554)) + +## API v2.8.2 ## +- Added `indexes` optional parameter to `/torrents/files` ([#14795](https://github.com/qbittorrent/qBittorrent/pull/14795)) +- Added `index` field to `/torrents/files` response ([#14795](https://github.com/qbittorrent/qBittorrent/pull/14795)) + +## API v2.8.3 ## +- Added `tag` optional parameter to `/torrents/info` ([#15152](https://github.com/qbittorrent/qBittorrent/pull/15152)) + +# General Information # + +- All API methods are under `/api/v2/APIName/methodName`, where `APIName` is a certain subgroup of API methods whose functionality is related. +- Either `GET` or `POST` can be used as the request type for all API methods. +- All API methods require [authentication](#authentication) (except the `/api/v2/auth/login` method itself, obviously). + +# Authentication # + +All Authentication API methods are under "auth", e.g.: `/api/v2/auth/methodName`. + +qBittorrent uses cookie-based authentication. + +## Login ## + +Name: `login` + +**Parameters:** + +Parameter | Type | Description +----------------------------------|---------|------------ +`username` | string | Username used to access the WebUI +`password` | string | Password used to access the WebUI + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +403 | User's IP is banned for too many failed login attempts +200 | All other scenarios + +Upon success, the response will contain a cookie with your SID. You must supply the cookie whenever you want to perform an operation that requires authentication. + +Example showing how to login and execute a command that requires authentication using `curl`: + +```sh +$ curl -i --header 'Referer: http://localhost:8080' --data 'username=admin&password=adminadmin' http://localhost:8080/api/v2/auth/login +HTTP/1.1 200 OK +Content-Encoding: +Content-Length: 3 +Content-Type: text/plain; charset=UTF-8 +Set-Cookie: SID=hBc7TxF76ERhvIw0jQQ4LZ7Z1jQUV0tQ; path=/ +$ curl http://localhost:8080/api/v2/torrents/info --cookie "SID=hBc7TxF76ERhvIw0jQQ4LZ7Z1jQUV0tQ" +``` + +Note: Set `Referer` or `Origin` header to the exact same domain and port as used in the HTTP query `Host` header. + +## Logout ## + +Name: `logout` + +**Parameters:** + +None + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios + +# Application # + +All Application API methods are under "app", e.g.: `/api/v2/app/methodName`. + +## Get application version ## + +Name: `version` + +**Parameters:** + +None + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios + +The response is a string with the application version, e.g. `v4.1.3` + +## Get API version ## + +Name: `webapiVersion` + +**Parameters:** + +None + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios + +The response is a string with the WebAPI version, e.g. `2.0` + +## Get build info ## + +Name: `buildInfo` + +**Parameters:** + +None + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios- see JSON below + +The response is a JSON object containing the following fields + +Property | Type | Description +-------------|---------|------------ +`qt` | string | QT version +`libtorrent` | string | libtorrent version +`boost` | string | Boost version +`openssl` | string | OpenSSL version +`bitness` | int | Application bitness (e.g. 64-bit) + +## Shutdown application ## + +Name: `shutdown` + +**Parameters:** + +None + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios + +## Get application preferences ## + +Name: `preferences` + +**Parameters:** + +None + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios- see JSON below + +The response is a JSON object with several fields (key-value) pairs representing the application's settings. The contents may vary depending on which settings are present in qBittorrent.ini. + +Possible fields: + +Property | Type | Description +-----------------------------------------|---------|------------ +`locale` | string | Currently selected language (e.g. en_GB for English) +`create_subfolder_enabled` | bool | True if a subfolder should be created when adding a torrent +`start_paused_enabled` | bool | True if torrents should be added in a Paused state +`auto_delete_mode` | integer | TODO +`preallocate_all` | bool | True if disk space should be pre-allocated for all files +`incomplete_files_ext` | bool | True if ".!qB" should be appended to incomplete files +`auto_tmm_enabled` | bool | True if Automatic Torrent Management is enabled by default +`torrent_changed_tmm_enabled` | bool | True if torrent should be relocated when its Category changes +`save_path_changed_tmm_enabled` | bool | True if torrent should be relocated when the default save path changes +`category_changed_tmm_enabled` | bool | True if torrent should be relocated when its Category's save path changes +`save_path` | string | Default save path for torrents, separated by slashes +`temp_path_enabled` | bool | True if folder for incomplete torrents is enabled +`temp_path` | string | Path for incomplete torrents, separated by slashes +`scan_dirs` | object | Property: directory to watch for torrent files, value: where torrents loaded from this directory should be downloaded to (see list of possible values below). Slashes are used as path separators; multiple key/value pairs can be specified +`export_dir` | string | Path to directory to copy .torrent files to. Slashes are used as path separators +`export_dir_fin` | string | Path to directory to copy .torrent files of completed downloads to. Slashes are used as path separators +`mail_notification_enabled` | bool | True if e-mail notification should be enabled +`mail_notification_sender` | string | e-mail where notifications should originate from +`mail_notification_email` | string | e-mail to send notifications to +`mail_notification_smtp` | string | smtp server for e-mail notifications +`mail_notification_ssl_enabled` | bool | True if smtp server requires SSL connection +`mail_notification_auth_enabled` | bool | True if smtp server requires authentication +`mail_notification_username` | string | Username for smtp authentication +`mail_notification_password` | string | Password for smtp authentication +`autorun_enabled` | bool | True if external program should be run after torrent has finished downloading +`autorun_program` | string | Program path/name/arguments to run if `autorun_enabled` is enabled; path is separated by slashes; you can use `%f` and `%n` arguments, which will be expanded by qBittorent as path_to_torrent_file and torrent_name (from the GUI; not the .torrent file name) respectively +`queueing_enabled` | bool | True if torrent queuing is enabled +`max_active_downloads` | integer | Maximum number of active simultaneous downloads +`max_active_torrents` | integer | Maximum number of active simultaneous downloads and uploads +`max_active_uploads` | integer | Maximum number of active simultaneous uploads +`dont_count_slow_torrents` | bool | If true torrents w/o any activity (stalled ones) will not be counted towards `max_active_*` limits; see [dont_count_slow_torrents](https://www.libtorrent.org/reference-Settings.html#dont_count_slow_torrents) for more information +`slow_torrent_dl_rate_threshold` | integer | Download rate in KiB/s for a torrent to be considered "slow" +`slow_torrent_ul_rate_threshold` | integer | Upload rate in KiB/s for a torrent to be considered "slow" +`slow_torrent_inactive_timer` | integer | Seconds a torrent should be inactive before considered "slow" +`max_ratio_enabled` | bool | True if share ratio limit is enabled +`max_ratio` | float | Get the global share ratio limit +`max_ratio_act` | integer | Action performed when a torrent reaches the maximum share ratio. See list of possible values here below. +`listen_port` | integer | Port for incoming connections +`upnp` | bool | True if UPnP/NAT-PMP is enabled +`random_port` | bool | True if the port is randomly selected +`dl_limit` | integer | Global download speed limit in KiB/s; `-1` means no limit is applied +`up_limit` | integer | Global upload speed limit in KiB/s; `-1` means no limit is applied +`max_connec` | integer | Maximum global number of simultaneous connections +`max_connec_per_torrent` | integer | Maximum number of simultaneous connections per torrent +`max_uploads` | integer | Maximum number of upload slots +`max_uploads_per_torrent` | integer | Maximum number of upload slots per torrent +`stop_tracker_timeout` | integer | Timeout in seconds for a `stopped` announce request to trackers +`enable_piece_extent_affinity` | bool | True if the advanced libtorrent option `piece_extent_affinity` is enabled +`bittorrent_protocol` | integer | Bittorrent Protocol to use (see list of possible values below) +`limit_utp_rate` | bool | True if `[du]l_limit` should be applied to uTP connections; this option is only available in qBittorent built against libtorrent version 0.16.X and higher +`limit_tcp_overhead` | bool | True if `[du]l_limit` should be applied to estimated TCP overhead (service data: e.g. packet headers) +`limit_lan_peers` | bool | True if `[du]l_limit` should be applied to peers on the LAN +`alt_dl_limit` | integer | Alternative global download speed limit in KiB/s +`alt_up_limit` | integer | Alternative global upload speed limit in KiB/s +`scheduler_enabled` | bool | True if alternative limits should be applied according to schedule +`schedule_from_hour` | integer | Scheduler starting hour +`schedule_from_min` | integer | Scheduler starting minute +`schedule_to_hour` | integer | Scheduler ending hour +`schedule_to_min` | integer | Scheduler ending minute +`scheduler_days` | integer | Scheduler days. See possible values here below +`dht` | bool | True if DHT is enabled +`pex` | bool | True if PeX is enabled +`lsd` | bool | True if LSD is enabled +`encryption` | integer | See list of possible values here below +`anonymous_mode` | bool | If true anonymous mode will be enabled; read more [here](Anonymous-Mode); this option is only available in qBittorent built against libtorrent version 0.16.X and higher +`proxy_type` | integer | See list of possible values here below +`proxy_ip` | string | Proxy IP address or domain name +`proxy_port` | integer | Proxy port +`proxy_peer_connections` | bool | True if peer and web seed connections should be proxified; this option will have any effect only in qBittorent built against libtorrent version 0.16.X and higher +`proxy_auth_enabled` | bool | True proxy requires authentication; doesn't apply to SOCKS4 proxies +`proxy_username` | string | Username for proxy authentication +`proxy_password` | string | Password for proxy authentication +`proxy_torrents_only` | bool | True if proxy is only used for torrents +`ip_filter_enabled` | bool | True if external IP filter should be enabled +`ip_filter_path` | string | Path to IP filter file (.dat, .p2p, .p2b files are supported); path is separated by slashes +`ip_filter_trackers` | bool | True if IP filters are applied to trackers +`web_ui_domain_list` | string | Comma-separated list of domains to accept when performing Host header validation +`web_ui_address` | string | IP address to use for the WebUI +`web_ui_port` | integer | WebUI port +`web_ui_upnp` | bool | True if UPnP is used for the WebUI port +`web_ui_username` | string | WebUI username +`web_ui_password` | string | For API ≥ v2.3.0: Plaintext WebUI password, not readable, write-only. For API < v2.3.0: MD5 hash of WebUI password, hash is generated from the following string: `username:Web UI Access:plain_text_web_ui_password` +`web_ui_csrf_protection_enabled` | bool | True if WebUI CSRF protection is enabled +`web_ui_clickjacking_protection_enabled` | bool | True if WebUI clickjacking protection is enabled +`web_ui_secure_cookie_enabled` | bool | True if WebUI cookie `Secure` flag is enabled +`web_ui_max_auth_fail_count` | integer | Maximum number of authentication failures before WebUI access ban +`web_ui_ban_duration` | integer | WebUI access ban duration in seconds +`web_ui_session_timeout` | integer | Seconds until WebUI is automatically signed off +`web_ui_host_header_validation_enabled` | bool | True if WebUI host header validation is enabled +`bypass_local_auth` | bool | True if authentication challenge for loopback address (127.0.0.1) should be disabled +`bypass_auth_subnet_whitelist_enabled` | bool | True if webui authentication should be bypassed for clients whose ip resides within (at least) one of the subnets on the whitelist +`bypass_auth_subnet_whitelist` | string | (White)list of ipv4/ipv6 subnets for which webui authentication should be bypassed; list entries are separated by commas +`alternative_webui_enabled` | bool | True if an alternative WebUI should be used +`alternative_webui_path` | string | File path to the alternative WebUI +`use_https` | bool | True if WebUI HTTPS access is enabled +`ssl_key` | string | For API < v2.0.1: SSL keyfile contents (this is a not a path) +`ssl_cert` | string | For API < v2.0.1: SSL certificate contents (this is a not a path) +`web_ui_https_key_path` | string | For API ≥ v2.0.1: Path to SSL keyfile +`web_ui_https_cert_path` | string | For API ≥ v2.0.1: Path to SSL certificate +`dyndns_enabled` | bool | True if server DNS should be updated dynamically +`dyndns_service` | integer | See list of possible values here below +`dyndns_username` | string | Username for DDNS service +`dyndns_password` | string | Password for DDNS service +`dyndns_domain` | string | Your DDNS domain name +`rss_refresh_interval` | integer | RSS refresh interval +`rss_max_articles_per_feed` | integer | Max stored articles per RSS feed +`rss_processing_enabled` | bool | Enable processing of RSS feeds +`rss_auto_downloading_enabled` | bool | Enable auto-downloading of torrents from the RSS feeds +`rss_download_repack_proper_episodes` | bool | For API ≥ v2.5.1: Enable downloading of repack/proper Episodes +`rss_smart_episode_filters` | string | For API ≥ v2.5.1: List of RSS Smart Episode Filters +`add_trackers_enabled` | bool | Enable automatic adding of trackers to new torrents +`add_trackers` | string | List of trackers to add to new torrent +`web_ui_use_custom_http_headers_enabled` | bool | For API ≥ v2.5.1: Enable custom http headers +`web_ui_custom_http_headers` | string | For API ≥ v2.5.1: List of custom http headers +`max_seeding_time_enabled` | bool | True enables max seeding time +`max_seeding_time` | integer | Number of minutes to seed a torrent +`announce_ip` | string | TODO +`announce_to_all_tiers` | bool | True always announce to all tiers +`announce_to_all_trackers` | bool | True always announce to all trackers in a tier +`async_io_threads` | integer | Number of asynchronous I/O threads +`banned_IPs` | string | List of banned IPs +`checking_memory_use` | integer | Outstanding memory when checking torrents in MiB +`current_interface_address` | string | IP Address to bind to. Empty String means All addresses +`current_network_interface` | string | Network Interface used +`disk_cache` | integer | Disk cache used in MiB +`disk_cache_ttl` | integer | Disk cache expiry interval in seconds +`embedded_tracker_port` | integer | Port used for embedded tracker +`enable_coalesce_read_write` | bool | True enables coalesce reads & writes +`enable_embedded_tracker` | bool | True enables embedded tracker +`enable_multi_connections_from_same_ip` | bool | True allows multiple connections from the same IP address +`enable_os_cache` | bool | True enables os cache +`enable_upload_suggestions` | bool | True enables sending of upload piece suggestions +`file_pool_size` | integer | File pool size +`outgoing_ports_max` | integer | Maximal outgoing port (0: Disabled) +`outgoing_ports_min` | integer | Minimal outgoing port (0: Disabled) +`recheck_completed_torrents` | bool | True rechecks torrents on completion +`resolve_peer_countries` | bool | True resolves peer countries +`save_resume_data_interval` | integer | Save resume data interval in min +`send_buffer_low_watermark` | integer | Send buffer low watermark in KiB +`send_buffer_watermark` | integer | Send buffer watermark in KiB +`send_buffer_watermark_factor` | integer | Send buffer watermark factor in percent +`socket_backlog_size` | integer | Socket backlog size +`upload_choking_algorithm` | integer | Upload choking algorithm used (see list of possible values below) +`upload_slots_behavior` | integer | Upload slots behavior used (see list of possible values below) +`upnp_lease_duration` | integer | UPnP lease duration (0: Permanent lease) +`utp_tcp_mixed_mode` | integer | μTP-TCP mixed mode algorithm (see list of possible values below) + +Possible values of `scan_dirs`: + +Value | Description +----------------------------|------------ +`0` | Download to the monitored folder +`1` | Download to the default save path +`"/path/to/download/to"` | Download to this path + +Possible values of `scheduler_days`: + +Value | Description +-------|------------ +`0` | Every day +`1` | Every weekday +`2` | Every weekend +`3` | Every Monday +`4` | Every Tuesday +`5` | Every Wednesday +`6` | Every Thursday +`7` | Every Friday +`8` | Every Saturday +`9` | Every Sunday + +Possible values of `encryption`: + +Value | Description +-------|------------ +`0` | Prefer encryption +`1` | Force encryption on +`2` | Force encryption off + +NB: the first options allows you to use both encrypted and unencrypted connections (this is the default); other options are mutually exclusive: e.g. by forcing encryption on you won't be able to use unencrypted connections and vice versa. + +Possible values of `proxy_type`: + +Value | Description +------|------------ +`-1` | Proxy is disabled +`1` | HTTP proxy without authentication +`2` | SOCKS5 proxy without authentication +`3` | HTTP proxy with authentication +`4` | SOCKS5 proxy with authentication +`5` | SOCKS4 proxy without authentication + +Possible values of `dyndns_service`: + +Value | Description +-------|------------ +`0` | Use DyDNS +`1` | Use NOIP + +Possible values of `max_ratio_act`: + +Value | Description +------|------------ +`0` | Pause torrent +`1` | Remove torrent + +Possible values of `bittorrent_protocol`: + +Value | Description +-------|------------ +`0` | TCP and μTP +`1` | TCP +`2` | μTP + +Possible values of `upload_choking_algorithm`: + +Value | Description +-------|------------ +`0` | Round-robin +`1` | Fastest upload +`2` | Anti-leech + +Possible values of `upload_slots_behavior`: + +Value | Description +-------|------------ +`0` | Fixed slots +`1` | Upload rate based + +Possible values of `utp_tcp_mixed_mode`: + +Value | Description +-------|------------ +`0` | Prefer TCP +`1` | Peer proportional + +Example: + +```JSON +{ + "add_trackers": "", + "add_trackers_enabled": false, + "alt_dl_limit": 10240, + "alt_up_limit": 10240, + "alternative_webui_enabled": false, + "alternative_webui_path": "/home/user/Documents/qbit-webui", + "announce_ip": "", + "announce_to_all_tiers": true, + "announce_to_all_trackers": false, + "anonymous_mode": false, + "async_io_threads": 4, + "auto_delete_mode": 0, + "auto_tmm_enabled": false, + "autorun_enabled": false, + "autorun_program": "", + "banned_IPs": "", + "bittorrent_protocol": 0, + "bypass_auth_subnet_whitelist": "", + "bypass_auth_subnet_whitelist_enabled": false, + "bypass_local_auth": false, + "category_changed_tmm_enabled": false, + "checking_memory_use": 32, + "create_subfolder_enabled": true, + "current_interface_address": "", + "current_network_interface": "", + "dht": true, + "disk_cache": -1, + "disk_cache_ttl": 60, + "dl_limit": 0, + "dont_count_slow_torrents": false, + "dyndns_domain": "changeme.dyndns.org", + "dyndns_enabled": false, + "dyndns_password": "", + "dyndns_service": 0, + "dyndns_username": "", + "embedded_tracker_port": 9000, + "enable_coalesce_read_write": false, + "enable_embedded_tracker": false, + "enable_multi_connections_from_same_ip": false, + "enable_os_cache": true, + "enable_piece_extent_affinity": false, + "enable_upload_suggestions": false, + "encryption": 0, + "export_dir": "/home/user/Downloads/all", + "export_dir_fin": "/home/user/Downloads/completed", + "file_pool_size": 40, + "incomplete_files_ext": false, + "ip_filter_enabled": false, + "ip_filter_path": "", + "ip_filter_trackers": false, + "limit_lan_peers": true, + "limit_tcp_overhead": false, + "limit_utp_rate": true, + "listen_port": 58925, + "locale": "en", + "lsd": true, + "mail_notification_auth_enabled": false, + "mail_notification_email": "", + "mail_notification_enabled": false, + "mail_notification_password": "", + "mail_notification_sender": "qBittorrent_notification@example.com", + "mail_notification_smtp": "smtp.changeme.com", + "mail_notification_ssl_enabled": false, + "mail_notification_username": "", + "max_active_downloads": 3, + "max_active_torrents": 5, + "max_active_uploads": 3, + "max_connec": 500, + "max_connec_per_torrent": 100, + "max_ratio": -1, + "max_ratio_act": 0, + "max_ratio_enabled": false, + "max_seeding_time": -1, + "max_seeding_time_enabled": false, + "max_uploads": -1, + "max_uploads_per_torrent": -1, + "outgoing_ports_max": 0, + "outgoing_ports_min": 0, + "pex": true, + "preallocate_all": false, + "proxy_auth_enabled": false, + "proxy_ip": "0.0.0.0", + "proxy_password": "", + "proxy_peer_connections": false, + "proxy_port": 8080, + "proxy_torrents_only": false, + "proxy_type": 0, + "proxy_username": "", + "queueing_enabled": false, + "random_port": false, + "recheck_completed_torrents": false, + "resolve_peer_countries": true, + "rss_auto_downloading_enabled":true, + "rss_download_repack_proper_episodes":true, + "rss_max_articles_per_feed":50, + "rss_processing_enabled":true, + "rss_refresh_interval":30, + "rss_smart_episode_filters":"s(\\d+)e(\\d+)\n(\\d+)x(\\d+)\n(\\d{4}[.\\-]\\d{1,2}[.\\-]\\d{1,2})", + "save_path": "/home/user/Downloads/", + "save_path_changed_tmm_enabled": false, + "save_resume_data_interval": 60, + "scan_dirs": + { + "/home/user/Downloads/incoming/games": 0, + "/home/user/Downloads/incoming/movies": 1, + }, + "schedule_from_hour": 8, + "schedule_from_min": 0, + "schedule_to_hour": 20, + "schedule_to_min": 0, + "scheduler_days": 0, + "scheduler_enabled": false, + "send_buffer_low_watermark": 10, + "send_buffer_watermark": 500, + "send_buffer_watermark_factor": 50, + "slow_torrent_dl_rate_threshold": 2, + "slow_torrent_inactive_timer": 60, + "slow_torrent_ul_rate_threshold": 2, + "socket_backlog_size": 30, + "start_paused_enabled": false, + "stop_tracker_timeout": 1, + "temp_path": "/home/user/Downloads/temp", + "temp_path_enabled": false, + "torrent_changed_tmm_enabled": true, + "up_limit": 0, + "upload_choking_algorithm": 1, + "upload_slots_behavior": 0, + "upnp": true, + "use_https": false, + "utp_tcp_mixed_mode": 0, + "web_ui_address": "*", + "web_ui_ban_duration": 3600, + "web_ui_clickjacking_protection_enabled": true, + "web_ui_csrf_protection_enabled": true, + "web_ui_custom_http_headers": "", + "web_ui_domain_list": "*", + "web_ui_host_header_validation_enabled": true, + "web_ui_https_cert_path": "", + "web_ui_https_key_path": "", + "web_ui_max_auth_fail_count": 5, + "web_ui_port": 8080, + "web_ui_secure_cookie_enabled": true, + "web_ui_session_timeout": 3600, + "web_ui_upnp": false, + "web_ui_use_custom_http_headers_enabled": false, + "web_ui_username": "admin" +} +``` + +## Set application preferences ## + +Name: `setPreferences` + +**Parameters:** + +A json object with key-value pairs of the settings you want to change and their new values. + +Example: + +```JSON +json={"save_path":"C:/Users/Dayman/Downloads","queueing_enabled":false,"scan_dirs":{"C:/Games": 0,"D:/Downloads": 1}} +``` + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios + +**Notes**: + + 1. There is no need to pass all possible preferences' `token:value` pairs if you only want to change one option + 1. Paths in `scan_dirs` must exist, otherwise this option will have no effect + 1. String values must be quoted; integer and boolean values must never be quoted + +For a list of possible preference options see [Get application preferences](#get-application-preferences) + +## Get default save path ## + +Name: `defaultSavePath` + +**Parameters:** + +None + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios + +The response is a string with the default save path, e.g. `C:/Users/Dayman/Downloads`. + +# Log # + +All Log API methods are under "log", e.g.: `/api/v2/log/methodName`. + +## Get log ## + +Name: `main` + +**Parameters:** + +Parameter | Type | Description +----------------|---------|------------ +`normal` | bool | Include normal messages (default: `true`) +`info` | bool | Include info messages (default: `true`) +`warning` | bool | Include warning messages (default: `true`) +`critical` | bool | Include critical messages (default: `true`) +`last_known_id` | integer | Exclude messages with "message id" <= `last_known_id` (default: `-1`) + +Example: + +```http +/api/v2/log/main?normal=true&info=true&warning=true&critical=true&last_known_id=-1 +``` + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios- see JSON below + +The response is a JSON array in which each element is an entry of the log. + +Each element of the array has the following properties: + +Property | Type | Description +------------|---------|------------ +`id` | integer | ID of the message +`message` | string | Text of the message +`timestamp` | integer | Milliseconds since epoch +`type` | integer | Type of the message: Log::NORMAL: `1`, Log::INFO: `2`, Log::WARNING: `4`, Log::CRITICAL: `8` + +Example: + +```JSON +[ + { + "id":0, + "message":"qBittorrent v3.4.0 started", + "timestamp":1507969127860, + "type":1 + }, + { + "id":1, + "message":"qBittorrent is trying to listen on any interface port: 19036", + "timestamp":1507969127869, + "type":2 + }, + { + "id":2, + "message":"Peer ID: -qB3400-", + "timestamp":1507969127870, + "type":1 + }, + { + "id":3, + "message":"HTTP User-Agent is 'qBittorrent/3.4.0'", + "timestamp":1507969127870, + "type":1 + }, + { + "id":4, + "message":"DHT support [ON]", + "timestamp":1507969127871, + "type":2 + }, + { + "id":5, + "message":"Local Peer Discovery support [ON]", + "timestamp":1507969127871, + "type":2 + }, + { + "id":6, + "message":"PeX support [ON]", + "timestamp":1507969127871, + "type":2 + }, + { + "id":7, + "message":"Anonymous mode [OFF]", + "timestamp":1507969127871, + "type":2 + }, + { + "id":8, + "message":"Encryption support [ON]", + "timestamp":1507969127871, + "type":2 + }, + { + "id":9, + "message":"Embedded Tracker [OFF]", + "timestamp":1507969127871, + "type":2 + }, + { + "id":10, + "message":"UPnP / NAT-PMP support [ON]", + "timestamp":1507969127873, + "type":2 + }, + { + "id":11, + "message":"Web UI: Now listening on port 8080", + "timestamp":1507969127883, + "type":1 + }, + { + "id":12, + "message":"Options were saved successfully.", + "timestamp":1507969128055, + "type":1 + }, + { + "id":13, + "message":"qBittorrent is successfully listening on interface :: port: TCP/19036", + "timestamp":1507969128270, + "type":2 + }, + { + "id":14, + "message":"qBittorrent is successfully listening on interface 0.0.0.0 port: TCP/19036", + "timestamp":1507969128271, + "type":2 + }, + { + "id":15, + "message":"qBittorrent is successfully listening on interface 0.0.0.0 port: UDP/19036", + "timestamp":1507969128272, + "type":2 + } +] +``` + +## Get peer log ## + +Name: `peers` + +**Parameters:** + +Parameter | Type | Description +----------------|---------|------------ +`last_known_id` | integer | Exclude messages with "message id" <= `last_known_id` (default: `-1`) + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios- see JSON below + +The response a JSON array. Each element of the array of objects (each object is the information relative to a peer) containing the following fields + +Property | Type | Description +------------|---------|------------ +`id` | integer | ID of the peer +`ip` | string | IP of the peer +`timestamp` | integer | Milliseconds since epoch +`blocked` | boolean | Whether or not the peer was blocked +`reason` | string | Reason of the block + +# Sync # + +Sync API implements requests for obtaining changes since the last request. +All Sync API methods are under "sync", e.g.: `/api/v2/sync/methodName`. + +## Get main data ## + +Name: `maindata` + +**Parameters:** + +Parameter | Type | Description +----------|---------|------------ +`rid` | integer | Response ID. If not provided, `rid=0` will be assumed. If the given `rid` is different from the one of last server reply, `full_update` will be `true` (see the server reply details for more info) + +Example: + +```http +/api/v2/sync/maindata?rid=14 +``` + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios- see JSON below + +The response is a JSON object with the following possible fields + +Property | Type | Description +------------------------------|---------|------------ +`rid` | integer | Response ID +`full_update` | bool | Whether the response contains all the data or partial data +`torrents` | object | Property: torrent hash, value: same as [torrent list](#get-torrent-list) +`torrents_removed` | array | List of hashes of torrents removed since last request +`categories` | object | Info for categories added since last request +`categories_removed` | array | List of categories removed since last request +`tags` | array | List of tags added since last request +`tags_removed` | array | List of tags removed since last request +`server_state` | object | Global transfer info + +Example: + +```JSON +{ + "rid":15, + "torrents": + { + "8c212779b4abde7c6bc608063a0d008b7e40ce32": + { + "state":"pausedUP" + } + } +} +``` + +## Get torrent peers data ## + +Name: `torrentPeers` + +**Parameters:** + +Parameter | Type | Description +----------|---------|------------ +`hash` | string | Torrent hash +`rid` | integer | Response ID. If not provided, `rid=0` will be assumed. If the given `rid` is different from the one of last server reply, `full_update` will be `true` (see the server reply details for more info) + +Example: + +```http +/api/v2/sync/torrentPeers?hash=8c212779b4abde7c6bc608063a0d008b7e40ce32?rid=14 +``` + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +404 | Torrent hash was not found +200 | All other scenarios- see JSON below + +The response is TODO + +# Transfer info # + +All Transfer info API methods are under "transfer", e.g.: `/api/v2/transfer/methodName`. + +## Get global transfer info ## + +This method returns info you usually see in qBt status bar. + +Name: `info` + +**Parameters:** + +None + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios- see JSON below + +The response is a JSON object with the following fields + +Property | Type | Description +--------------------|---------|------------ +`dl_info_speed` | integer | Global download rate (bytes/s) +`dl_info_data` | integer | Data downloaded this session (bytes) +`up_info_speed` | integer | Global upload rate (bytes/s) +`up_info_data` | integer | Data uploaded this session (bytes) +`dl_rate_limit` | integer | Download rate limit (bytes/s) +`up_rate_limit` | integer | Upload rate limit (bytes/s) +`dht_nodes` | integer | DHT nodes connected to +`connection_status` | string | Connection status. See possible values here below + +In addition to the above in partial data requests (see [Get partial data](#get-partial-data) for more info): + +Property | Type | Description +-----------------------|---------|------------ +`queueing` | bool | True if torrent queueing is enabled +`use_alt_speed_limits` | bool | True if alternative speed limits are enabled +`refresh_interval` | integer | Transfer list refresh interval (milliseconds) + +Possible values of `connection_status`: + +Value | +--------------------| +`connected` | +`firewalled` | +`disconnected` | + +Example: + +```JSON +{ + "connection_status":"connected", + "dht_nodes":386, + "dl_info_data":681521119, + "dl_info_speed":0, + "dl_rate_limit":0, + "up_info_data":10747904, + "up_info_speed":0, + "up_rate_limit":1048576 +} +``` + +## Get alternative speed limits state ## + +Name: `speedLimitsMode` + +**Parameters:** + +None + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios + +The response is `1` if alternative speed limits are enabled, `0` otherwise. + +## Toggle alternative speed limits ## + +Name: `toggleSpeedLimitsMode` + +**Parameters:** + +None + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios + +## Get global download limit ## + +Name: `downloadLimit` + +**Parameters:** + +None + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios + +The response is the value of current global download speed limit in bytes/second; this value will be zero if no limit is applied. + +## Set global download limit ## + +Name: `setDownloadLimit` + +**Parameters:** + +Parameter | Type | Description +----------------------------------|---------|------------ +`limit` | integer | The global download speed limit to set in bytes/second + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios + +## Get global upload limit ## + +Name: `uploadLimit` + +**Parameters:** + +None + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios + +The response is the value of current global upload speed limit in bytes/second; this value will be zero if no limit is applied. + +## Set global upload limit ## + +Name: `setUploadLimit` + +**Parameters:** + +Parameter | Type | Description +----------------------------------|---------|------------ +`limit` | integer | The global upload speed limit to set in bytes/second + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios + +## Ban peers ## + +Name: `banPeers` + +**Parameters:** + +Parameter | Type | Description +----------------------------------|---------|------------ +`peers` | string | The peer to ban, or multiple peers separated by a pipe `\|`. Each peer is a colon-separated `host:port` + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios + +# Torrent management # + +All Torrent management API methods are under "torrents", e.g.: `/api/v2/torrents/methodName`. + +## Get torrent list ## + +Name: `info` + +**Parameters:** + +Parameter | Type | Description +----------------------|---------|------------ +`filter` _optional_ | string | Filter torrent list by state. Allowed state filters: `all`, `downloading`, `seeding`, `completed`, `paused`, `active`, `inactive`, `resumed`, `stalled`, `stalled_uploading`, `stalled_downloading`, `errored` +`category` _optional_ | string | Get torrents with the given category (empty string means "without category"; no "category" parameter means "any category" <- broken until [#11748](https://github.com/qbittorrent/qBittorrent/issues/11748) is resolved). Remember to URL-encode the category name. For example, `My category` becomes `My%20category` +`tag` _optional_ since 2.8.3 | string | Get torrents with the given tag (empty string means "without tag"; no "tag" parameter means "any tag". Remember to URL-encode the category name. For example, `My tag` becomes `My%20tag` +`sort` _optional_ | string | Sort torrents by given key. They can be sorted using any field of the response's JSON array (which are documented below) as the sort key. +`reverse` _optional_ | bool | Enable reverse sorting. Defaults to `false` +`limit` _optional_ | integer | Limit the number of torrents returned +`offset` _optional_ | integer | Set offset (if less than 0, offset from end) +`hashes` _optional_ | string | Filter by hashes. Can contain multiple hashes separated by `\|` + +Example: + +```http +/api/v2/torrents/info?filter=downloading&category=sample%20category&sort=ratio +``` + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios- see JSON below + +The response is a JSON array with the following fields + +Property | Type | Description +---------------------|---------|------------ +`added_on` | integer | Time (Unix Epoch) when the torrent was added to the client +`amount_left` | integer | Amount of data left to download (bytes) +`auto_tmm` | bool | Whether this torrent is managed by Automatic Torrent Management +`availability` | float | Percentage of file pieces currently available +`category` | string | Category of the torrent +`completed` | integer | Amount of transfer data completed (bytes) +`completion_on` | integer | Time (Unix Epoch) when the torrent completed +`content_path` | string | Absolute path of torrent content (root path for multifile torrents, absolute file path for singlefile torrents) +`dl_limit` | integer | Torrent download speed limit (bytes/s). `-1` if ulimited. +`dlspeed` | integer | Torrent download speed (bytes/s) +`downloaded` | integer | Amount of data downloaded +`downloaded_session` | integer | Amount of data downloaded this session +`eta` | integer | Torrent ETA (seconds) +`f_l_piece_prio` | bool | True if first last piece are prioritized +`force_start` | bool | True if force start is enabled for this torrent +`hash` | string | Torrent hash +`last_activity` | integer | Last time (Unix Epoch) when a chunk was downloaded/uploaded +`magnet_uri` | string | Magnet URI corresponding to this torrent +`max_ratio` | float | Maximum share ratio until torrent is stopped from seeding/uploading +`max_seeding_time` | integer | Maximum seeding time (seconds) until torrent is stopped from seeding +`name` | string | Torrent name +`num_complete` | integer | Number of seeds in the swarm +`num_incomplete` | integer | Number of leechers in the swarm +`num_leechs` | integer | Number of leechers connected to +`num_seeds` | integer | Number of seeds connected to +`priority` | integer | Torrent priority. Returns -1 if queuing is disabled or torrent is in seed mode +`progress` | float | Torrent progress (percentage/100) +`ratio` | float | Torrent share ratio. Max ratio value: 9999. +`ratio_limit` | float | TODO (what is different from `max_ratio`?) +`save_path` | string | Path where this torrent's data is stored +`seeding_time` | integer | Torrent elapsed time while complete (seconds) +`seeding_time_limit` | integer | TODO (what is different from `max_seeding_time`?) seeding_time_limit is a per torrent setting, when Automatic Torrent Management is disabled, furthermore then max_seeding_time is set to seeding_time_limit for this torrent. If Automatic Torrent Management is enabled, the value is -2. And if max_seeding_time is unset it have a default value -1. +`seen_complete` | integer | Time (Unix Epoch) when this torrent was last seen complete +`seq_dl` | bool | True if sequential download is enabled +`size` | integer | Total size (bytes) of files selected for download +`state` | string | Torrent state. See table here below for the possible values +`super_seeding` | bool | True if super seeding is enabled +`tags` | string | Comma-concatenated tag list of the torrent +`time_active` | integer | Total active time (seconds) +`total_size` | integer | Total size (bytes) of all file in this torrent (including unselected ones) +`tracker` | string | The first tracker with working status. Returns empty string if no tracker is working. +`up_limit` | integer | Torrent upload speed limit (bytes/s). `-1` if ulimited. +`uploaded` | integer | Amount of data uploaded +`uploaded_session` | integer | Amount of data uploaded this session +`upspeed` | integer | Torrent upload speed (bytes/s) + +Possible values of `state`: + +Value | Description +--------------|------------ +`error` | Some error occurred, applies to paused torrents +`missingFiles`| Torrent data files is missing +`uploading` | Torrent is being seeded and data is being transferred +`pausedUP` | Torrent is paused and has finished downloading +`queuedUP` | Queuing is enabled and torrent is queued for upload +`stalledUP` | Torrent is being seeded, but no connection were made +`checkingUP` | Torrent has finished downloading and is being checked +`forcedUP` | Torrent is forced to uploading and ignore queue limit +`allocating` | Torrent is allocating disk space for download +`downloading` | Torrent is being downloaded and data is being transferred +`metaDL` | Torrent has just started downloading and is fetching metadata +`pausedDL` | Torrent is paused and has NOT finished downloading +`queuedDL` | Queuing is enabled and torrent is queued for download +`stalledDL` | Torrent is being downloaded, but no connection were made +`checkingDL` | Same as checkingUP, but torrent has NOT finished downloading +`forcedDL` | Torrent is forced to downloading to ignore queue limit +`checkingResumeData`| Checking resume data on qBt startup +`moving` | Torrent is moving to another location +`unknown` | Unknown status + +Example: + +```JSON +[ + { + "dlspeed":9681262, + "eta":87, + "f_l_piece_prio":false, + "force_start":false, + "hash":"8c212779b4abde7c6bc608063a0d008b7e40ce32", + "category":"", + "tags": "", + "name":"debian-8.1.0-amd64-CD-1.iso", + "num_complete":-1, + "num_incomplete":-1, + "num_leechs":2, + "num_seeds":54, + "priority":1, + "progress":0.16108787059783936, + "ratio":0, + "seq_dl":false, + "size":657457152, + "state":"downloading", + "super_seeding":false, + "upspeed":0 + }, + { + another_torrent_info + } +] +``` + +## Get torrent generic properties ## + +Requires knowing the torrent hash. You can get it from [torrent list](#get-torrent-list). + +Name: `properties` + +**Parameters:** + +Parameter | Type | Description +----------|--------|------------ +`hash` | string | The hash of the torrent you want to get the generic properties of + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +404 | Torrent hash was not found +200 | All other scenarios- see JSON below + +The response is: + +- empty, if the torrent hash is invalid +- otherwise, a JSON object with the following fields + +Property | Type | Description +--------------------------|---------|------------ +`save_path` | string | Torrent save path +`creation_date` | integer | Torrent creation date (Unix timestamp) +`piece_size` | integer | Torrent piece size (bytes) +`comment` | string | Torrent comment +`total_wasted` | integer | Total data wasted for torrent (bytes) +`total_uploaded` | integer | Total data uploaded for torrent (bytes) +`total_uploaded_session` | integer | Total data uploaded this session (bytes) +`total_downloaded` | integer | Total data downloaded for torrent (bytes) +`total_downloaded_session`| integer | Total data downloaded this session (bytes) +`up_limit` | integer | Torrent upload limit (bytes/s) +`dl_limit` | integer | Torrent download limit (bytes/s) +`time_elapsed` | integer | Torrent elapsed time (seconds) +`seeding_time` | integer | Torrent elapsed time while complete (seconds) +`nb_connections` | integer | Torrent connection count +`nb_connections_limit` | integer | Torrent connection count limit +`share_ratio` | float | Torrent share ratio +`addition_date` | integer | When this torrent was added (unix timestamp) +`completion_date` | integer | Torrent completion date (unix timestamp) +`created_by` | string | Torrent creator +`dl_speed_avg` | integer | Torrent average download speed (bytes/second) +`dl_speed` | integer | Torrent download speed (bytes/second) +`eta` | integer | Torrent ETA (seconds) +`last_seen` | integer | Last seen complete date (unix timestamp) +`peers` | integer | Number of peers connected to +`peers_total` | integer | Number of peers in the swarm +`pieces_have` | integer | Number of pieces owned +`pieces_num` | integer | Number of pieces of the torrent +`reannounce` | integer | Number of seconds until the next announce +`seeds` | integer | Number of seeds connected to +`seeds_total` | integer | Number of seeds in the swarm +`total_size` | integer | Torrent total size (bytes) +`up_speed_avg` | integer | Torrent average upload speed (bytes/second) +`up_speed` | integer | Torrent upload speed (bytes/second) + +NB: `-1` is returned if the type of the property is integer but its value is not known. + +Example: + +```JSON +{ + "addition_date":1438429165, + "comment":"\"Debian CD from cdimage.debian.org\"", + "completion_date":1438429234, + "created_by":"", + "creation_date":1433605214, + "dl_limit":-1, + "dl_speed":0, + "dl_speed_avg":9736015, + "eta":8640000, + "last_seen":1438430354, + "nb_connections":3, + "nb_connections_limit":250, + "peers":1, + "peers_total":89, + "piece_size":524288, + "pieces_have":1254, + "pieces_num":1254, + "reannounce":672, + "save_path":"/Downloads/debian-8.1.0-amd64-CD-1.iso", + "seeding_time":1128, + "seeds":1, + "seeds_total":254, + "share_ratio":0.00072121022562178299, + "time_elapsed":1197, + "total_downloaded":681521119, + "total_downloaded_session":681521119, + "total_size":657457152, + "total_uploaded":491520, + "total_uploaded_session":491520, + "total_wasted":23481724, + "up_limit":-1, + "up_speed":0, + "up_speed_avg":410 +} +``` + +## Get torrent trackers ## + +Requires knowing the torrent hash. You can get it from [torrent list](#get-torrent-list). + +Name: `trackers` + +**Parameters:** + +Parameter | Type | Description +----------|--------|------------ +`hash` | string | The hash of the torrent you want to get the trackers of + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +404 | Torrent hash was not found +200 | All other scenarios- see JSON below + +The response is a JSON array, where each element contains info about one tracker, with the following fields + +Property | Type | Description +-----------------|----------|------------- +`url` | string | Tracker url +`status` | integer | Tracker status. See the table below for possible values +`tier` | integer | Tracker priority tier. Lower tier trackers are tried before higher tiers. Tier numbers are valid when `>= 0`, `< 0` is used as placeholder when `tier` does not exist for special entries (such as DHT). +`num_peers` | integer | Number of peers for current torrent, as reported by the tracker +`num_seeds` | integer | Number of seeds for current torrent, asreported by the tracker +`num_leeches` | integer | Number of leeches for current torrent, as reported by the tracker +`num_downloaded` | integer | Number of completed downlods for current torrent, as reported by the tracker +`msg` | string | Tracker message (there is no way of knowing what this message is - it's up to tracker admins) + +Possible values of `status`: + +Value | Description +-------|------------ +0 | Tracker is disabled (used for DHT, PeX, and LSD) +1 | Tracker has not been contacted yet +2 | Tracker has been contacted and is working +3 | Tracker is updating +4 | Tracker has been contacted, but it is not working (or doesn't send proper replies) + +Example: + +```JSON +[ + { + "msg":"", + "num_peers":100, + "status":2, + "url":"http://bttracker.debian.org:6969/announce" + }, + { + another_tracker_info + } +] +``` + +## Get torrent web seeds ## + +Requires knowing the torrent hash. You can get it from [torrent list](#get-torrent-list). + +Name: `webseeds` + +**Parameters:** + +Parameter | Type | Description +----------|--------|------------ +`hash` | string | The hash of the torrent you want to get the webseeds of + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +404 | Torrent hash was not found +200 | All other scenarios- see JSON below + +The response is a JSON array, where each element is information about one webseed, with the following fields + +Property | Type | Description +--------------|----------|------------ +`url` | string | URL of the web seed + +Example: + +```JSON +[ + { + "url":"http://some_url/" + }, + { + "url":"http://some_other_url/" + } +] +``` + +## Get torrent contents ## + +Requires knowing the torrent hash. You can get it from [torrent list](#get-torrent-list). + +Name: `files` + +**Parameters:** + +Parameter | Type | Description +----------|--------|------------ +`hash` | string | The hash of the torrent you want to get the contents of +`indexes` _optional_ since 2.8.2 | string | The indexes of the files you want to retrieve. `indexes` can contain multiple values separated by `\|`. + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +404 | Torrent hash was not found +200 | All other scenarios- see JSON below + +The response is: + +- empty, if the torrent hash is invalid +- otherwise, a JSON array, where each element contains info about one file, with the following fields + +Property | Type | Description +---------------|---------------|------------- +`index` since 2.8.2 | integer | File index +`name` | string | File name (including relative path) +`size` | integer | File size (bytes) +`progress` | float | File progress (percentage/100) +`priority` | integer | File priority. See possible values here below +`is_seed` | bool | True if file is seeding/complete +`piece_range` | integer array | The first number is the starting piece index and the second number is the ending piece index (inclusive) +`availability` | float | Percentage of file pieces currently available (percentage/100) + +Possible values of `priority`: + +Value | Description +-----------|------------ +`0` | Do not download +`1` | Normal priority +`6` | High priority +`7` | Maximal priority + +Example: + +```JSON + +[ + { + "index":0, + "is_seed":false, + "name":"debian-8.1.0-amd64-CD-1.iso", + "piece_range":[0,1253], + "priority":1, + "progress":0, + "size":657457152, + "availability":0.5, + } +] +``` + +## Get torrent pieces' states ## + +Requires knowing the torrent hash. You can get it from [torrent list](#get-torrent-list). + +Name: `pieceStates` + +**Parameters:** + +Parameter | Type | Description +----------|--------|------------ +`hash` | string | The hash of the torrent you want to get the pieces' states of + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +404 | Torrent hash was not found +200 | All other scenarios- see JSON below + +The response is: + +- empty, if the torrent hash is invalid +- otherwise, an array of states (integers) of all pieces (in order) of a specific torrent. + +Value meanings are defined as below: + +Value | Description +-----------|------------ +`0` | Not downloaded yet +`1` | Now downloading +`2` | Already downloaded + +Example: + +```JSON +[0,0,2,1,0,0,2,1] +``` + +## Get torrent pieces' hashes ## + +Requires knowing the torrent hash. You can get it from [torrent list](#get-torrent-list). + +Name: `pieceHashes` + +**Parameters:** + +Parameter | Type | Description +----------|--------|------------ +`hash` | string | The hash of the torrent you want to get the pieces' hashes of + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +404 | Torrent hash was not found +200 | All other scenarios- see JSON below + +The response is: + +- empty, if the torrent hash is invalid +- otherwise, an array of hashes (strings) of all pieces (in order) of a specific torrent. + +Example: + +```JSON +["54eddd830a5b58480a6143d616a97e3a6c23c439","f8a99d225aa4241db100f88407fc3bdaead583ab","928fb615b9bd4dd8f9e9022552c8f8f37ef76f58"] +``` + +## Pause torrents ## + +Requires knowing the torrent hashes. You can get it from [torrent list](#get-torrent-list). + +Name: `pause` + +**Parameters:** + +Parameter | Type | Description +------------|----------|------------ +`hashes` | string | The hashes of the torrents you want to pause. `hashes` can contain multiple hashes separated by `\|`, to pause multiple torrents, or set to `all`, to pause all torrents. + +Example: + +```http +/api/v2/torrents/pause?hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32|54eddd830a5b58480a6143d616a97e3a6c23c439 +``` + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios + +## Resume torrents ## + +Requires knowing the torrent hashes. You can get it from [torrent list](#get-torrent-list). + +Name: `resume` + +**Parameters:** + +Parameter | Type | Description +------------|----------|------------ +`hashes` | string | The hashes of the torrents you want to resume. `hashes` can contain multiple hashes separated by `\|`, to resume multiple torrents, or set to `all`, to resume all torrents. + +Example: + +```http +/api/v2/torrents/resume?hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32|54eddd830a5b58480a6143d616a97e3a6c23c439 +``` + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios + +## Delete torrents ## + +Requires knowing the torrent hashes. You can get it from [torrent list](#get-torrent-list). + +Name: `delete` + +**Parameters:** + +Parameter | Type | Description +------------|----------|------------ +`hashes` | string | The hashes of the torrents you want to delete. `hashes` can contain multiple hashes separated by `\|`, to delete multiple torrents, or set to `all`, to delete all torrents. +`deleteFiles` | If set to `true`, the downloaded data will also be deleted, otherwise has no effect. + +Example: + +```http +/api/v2/torrents/delete?hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32&deleteFiles=false +``` + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios + +## Recheck torrents ## + +Requires knowing the torrent hashes. You can get it from [torrent list](#get-torrent-list). + +Name: `recheck` + +**Parameters:** + +Parameter | Type | Description +------------|----------|------------ +`hashes` | string | The hashes of the torrents you want to recheck. `hashes` can contain multiple hashes separated by `\|`, to recheck multiple torrents, or set to `all`, to recheck all torrents. + +Example: + +```http +/api/v2/torrents/recheck?hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32|54eddd830a5b58480a6143d616a97e3a6c23c439 +``` + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios + +## Reannounce torrents ## + +Requires knowing the torrent hashes. You can get it from [torrent list](#get-torrent-list). + +Name: `reannounce` + +**Parameters:** + +Parameter | Type | Description +------------|----------|------------ +`hashes` | string | The hashes of the torrents you want to reannounce. `hashes` can contain multiple hashes separated by `\|`, to reannounce multiple torrents, or set to `all`, to reannounce all torrents. + +Example: + +```http +/api/v2/torrents/reannounce?hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32|54eddd830a5b58480a6143d616a97e3a6c23c439 +``` + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios + +## Add new torrent ## + +This method can add torrents from server local file or from URLs. `http://`, `https://`, `magnet:` and `bc://bt/` links are supported. + +Name: `add` + +Add torrent from URLs example: + +```http +POST /api/v2/torrents/add HTTP/1.1 +User-Agent: Fiddler +Host: 127.0.0.1 +Cookie: SID=your_sid +Content-Type: multipart/form-data; boundary=---------------------------6688794727912 +Content-Length: length + +-----------------------------6688794727912 +Content-Disposition: form-data; name="urls" + +https://torcache.net/torrent/3B1A1469C180F447B77021074DBBCCAEF62611E7.torrent +https://torcache.net/torrent/3B1A1469C180F447B77021074DBBCCAEF62611E8.torrent +-----------------------------6688794727912 +Content-Disposition: form-data; name="savepath" + +C:/Users/qBit/Downloads +-----------------------------6688794727912 +Content-Disposition: form-data; name="cookie" + +ui=28979218048197 +-----------------------------6688794727912 +Content-Disposition: form-data; name="category" + +movies +-----------------------------6688794727912 +Content-Disposition: form-data; name="skip_checking" + +true +-----------------------------6688794727912 +Content-Disposition: form-data; name="paused" + +true +-----------------------------6688794727912 +Content-Disposition: form-data; name="root_folder" + +true +-----------------------------6688794727912-- +``` + +Add torrents from files example: + +```http +POST /api/v2/torrents/add HTTP/1.1 +Content-Type: multipart/form-data; boundary=-------------------------acebdf13572468 +User-Agent: Fiddler +Host: 127.0.0.1 +Cookie: SID=your_sid +Content-Length: length + +---------------------------acebdf13572468 +Content-Disposition: form-data; name="torrents"; filename="8f18036b7a205c9347cb84a253975e12f7adddf2.torrent" +Content-Type: application/x-bittorrent + +file_binary_data_goes_here +---------------------------acebdf13572468 +Content-Disposition: form-data; name="torrents"; filename="UFS.torrent" +Content-Type: application/x-bittorrent + +file_binary_data_goes_here +---------------------------acebdf13572468-- + +``` + +The above example will add two torrent files. `file_binary_data_goes_here` represents raw data of torrent file (basically a byte array). + +**Parameters:** + +Property | Type | Description +--------------------------------|---------|------------ +`urls` | string | URLs separated with newlines +`torrents` | raw | Raw data of torrent file. `torrents` can be presented multiple times. +`savepath` _optional_ | string | Download folder +`cookie` _optional_ | string | Cookie sent to download the .torrent file +`category` _optional_ | string | Category for the torrent +`tags` _optional_ | string | Tags for the torrent, split by ',' +`skip_checking` _optional_ | string | Skip hash checking. Possible values are `true`, `false` (default) +`paused` _optional_ | string | Add torrents in the paused state. Possible values are `true`, `false` (default) +`root_folder` _optional_ | string | Create the root folder. Possible values are `true`, `false`, unset (default) +`rename` _optional_ | string | Rename torrent +`upLimit` _optional_ | integer | Set torrent upload speed limit. Unit in bytes/second +`dlLimit` _optional_ | integer | Set torrent download speed limit. Unit in bytes/second +`ratioLimit` _optional_ since 2.8.1 | float | Set torrent share ratio limit +`seedingTimeLimit` _optional_ since 2.8.1 | integer | Set torrent seeding time limit. Unit in seconds +`autoTMM` _optional_ | bool | Whether Automatic Torrent Management should be used +`sequentialDownload` _optional_ | string | Enable sequential download. Possible values are `true`, `false` (default) +`firstLastPiecePrio` _optional_ | string | Prioritize download first last piece. Possible values are `true`, `false` (default) + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +415 | Torrent file is not valid +200 | All other scenarios + +## Add trackers to torrent ## + +Requires knowing the torrent hash. You can get it from [torrent list](#get-torrent-list). + +Name: `addTrackers` + +```http +POST /api/v2/torrents/addTrackers HTTP/1.1 +User-Agent: Fiddler +Host: 127.0.0.1 +Cookie: SID=your_sid +Content-Type: application/x-www-form-urlencoded +Content-Length: length + +hash=8c212779b4abde7c6bc608063a0d008b7e40ce32&urls=http://192.168.0.1/announce%0Audp://192.168.0.1:3333/dummyAnnounce +``` + +This adds two trackers to torrent with hash `8c212779b4abde7c6bc608063a0d008b7e40ce32`. Note `%0A` (aka LF newline) between trackers. Ampersand in tracker urls **MUST** be escaped. + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +404 | Torrent hash was not found +200 | All other scenarios + +## Edit trackers ## + +Name: `editTracker` + +**Parameters:** + +Parameter | Type | Description +----------------------------------|---------|------------ +`hash` | string | The hash of the torrent +`origUrl` | string | The tracker URL you want to edit +`newUrl` | string | The new URL to replace the `origUrl` + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +400 | `newUrl` is not a valid URL +404 | Torrent hash was not found +409 | `newUrl` already exists for the torrent +409 | `origUrl` was not found +200 | All other scenarios + +## Remove trackers ## + +Name: `removeTrackers` + +**Parameters:** + +Parameter | Type | Description +----------------------------------|---------|------------ +`hash` | string | The hash of the torrent +`urls` | string | URLs to remove, separated by `\|` + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +404 | Torrent hash was not found +409 | All `urls` were not found +200 | All other scenarios + +## Add peers ## + +Name: `addPeers` + +**Parameters:** + +Parameter | Type | Description +----------------------------------|---------|------------ +`hashes` | string | The hash of the torrent, or multiple hashes separated by a pipe `\|` +`peers` | string | The peer to add, or multiple peers separated by a pipe `\|`. Each peer is a colon-separated `host:port` + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +400 | None of the supplied peers are valid +200 | All other scenarios + +## Increase torrent priority ## + +Requires knowing the torrent hash. You can get it from [torrent list](#get-torrent-list). + +Name: `increasePrio` + +**Parameters:** + +Parameter | Type | Description +------------|----------|------------ +`hashes` | string | The hashes of the torrents you want to increase the priority of. `hashes` can contain multiple hashes separated by `\|`, to increase the priority of multiple torrents, or set to `all`, to increase the priority of all torrents. + +Example: + +```http +/api/v2/torrents/increasePrio?hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32|54eddd830a5b58480a6143d616a97e3a6c23c439 +``` + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +409 | Torrent queueing is not enabled +200 | All other scenarios + +## Decrease torrent priority ## + +Requires knowing the torrent hash. You can get it from [torrent list](#get-torrent-list). + +Name: `decreasePrio` + +**Parameters:** + +Parameter | Type | Description +------------|----------|------------ +`hashes` | string | The hashes of the torrents you want to decrease the priority of. `hashes` can contain multiple hashes separated by `\|`, to decrease the priority of multiple torrents, or set to `all`, to decrease the priority of all torrents. + +Example: + +```http +/api/v2/torrents/decreasePrio?hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32|54eddd830a5b58480a6143d616a97e3a6c23c439 +``` + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +409 | Torrent queueing is not enabled +200 | All other scenarios + +## Maximal torrent priority ## + +Requires knowing the torrent hash. You can get it from [torrent list](#get-torrent-list). + +Name: `topPrio` + +**Parameters:** + +Parameter | Type | Description +------------|----------|------------ +`hashes` | string | The hashes of the torrents you want to set to the maximum priority. `hashes` can contain multiple hashes separated by `\|`, to set multiple torrents to the maximum priority, or set to `all`, to set all torrents to the maximum priority. + +Example: + +```http +/api/v2/torrents/topPrio?hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32|54eddd830a5b58480a6143d616a97e3a6c23c439 +``` + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +409 | Torrent queueing is not enabled +200 | All other scenarios + +## Minimal torrent priority ## + +Requires knowing the torrent hash. You can get it from [torrent list](#get-torrent-list). + +Name: `bottomPrio` + +**Parameters:** + +Parameter | Type | Description +------------|----------|------------ +`hashes` | string | The hashes of the torrents you want to set to the minimum priority. `hashes` can contain multiple hashes separated by `\|`, to set multiple torrents to the minimum priority, or set to `all`, to set all torrents to the minimum priority. + +Example: + +```http +/api/v2/torrents/bottomPrio?hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32|54eddd830a5b58480a6143d616a97e3a6c23c439 +``` + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +409 | Torrent queueing is not enabled +200 | All other scenarios + +## Set file priority ## + +Name: `filePrio` + +**Parameters:** + +Parameter | Type | Description +----------------------------------|---------|------------ +`hash` | string | The hash of the torrent +`id` | string | File ids, separated by `\|` +`priority` | number | File priority to set (consult [torrent contents API](#get-torrent-contents) for possible values) + +`id` values correspond to file position inside the array returned by [torrent contents API](#get-torrent-contents), e.g. `id=0` for first file, `id=1` for second file, etc. + +Since 2.8.2 it is reccomended to use `index` field returned by [torrent contents API](#get-torrent-contents) (since the files can be filtered and the `index` value may differ from the position inside the response array). + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +400 | Priority is invalid +400 | At least one file `id` is not a valid integer +404 | Torrent hash was not found +409 | Torrent metadata hasn't downloaded yet +409 | At least one file `id` was not found +200 | All other scenarios + +## Get torrent download limit ## + +Requires knowing the torrent hash. You can get it from [torrent list](#get-torrent-list). + +Name: `downloadLimit` + +```http +POST /api/v2/torrents/downloadLimit HTTP/1.1 +User-Agent: Fiddler +Host: 127.0.0.1 +Cookie: SID=your_sid +Content-Type: application/x-www-form-urlencoded +Content-Length: length + +hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32|284b83c9c7935002391129fd97f43db5d7cc2ba0 +``` + +`hashes` can contain multiple hashes separated by `|` or set to `all` + +Server reply (example): + +```http +HTTP/1.1 200 OK +content-type: application/json +content-length: length + +{"8c212779b4abde7c6bc608063a0d008b7e40ce32":338944,"284b83c9c7935002391129fd97f43db5d7cc2ba0":123} +``` + +`8c212779b4abde7c6bc608063a0d008b7e40ce32` is the hash of the torrent and `338944` its download speed limit in bytes per second; this value will be zero if no limit is applied. + +## Set torrent download limit ## + +Requires knowing the torrent hash. You can get it from [torrent list](#get-torrent-list). + +```http +POST /api/v2/torrents/setDownloadLimit HTTP/1.1 +User-Agent: Fiddler +Host: 127.0.0.1 +Cookie: SID=your_sid +Content-Type: application/x-www-form-urlencoded +Content-Length: length + +hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32|284b83c9c7935002391129fd97f43db5d7cc2ba0&limit=131072 +``` + +`hashes` can contain multiple hashes separated by `|` or set to `all` +`limit` is the download speed limit in bytes per second you want to set. + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios + +## Set torrent share limit ## + +Requires knowing the torrent hash. You can get it from [torrent list](#get-torrent-list). + +Name: `setShareLimits` + +```http +POST /api/v2/torrents/setShareLimits HTTP/1.1 +User-Agent: Fiddler +Host: 127.0.0.1 +Cookie: SID=your_sid +Content-Type: application/x-www-form-urlencoded +Content-Length: length + +hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32|284b83c9c7935002391129fd97f43db5d7cc2ba0&ratioLimit=1.0&seedingTimeLimit=60 +``` + +`hashes` can contain multiple hashes separated by `|` or set to `all` +`ratioLimit` is the max ratio the torrent should be seeded until. `-2` means the global limit should be used, `-1` means no limit. +`seedingTimeLimit` is the max amount of time the torrent should be seeded. `-2` means the global limit should be used, `-1` means no limit. + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios + +## Get torrent upload limit ## + +Requires knowing the torrent hash. You can get it from [torrent list](#get-torrent-list). + +Name: `uploadLimit` + +```http +POST /api/v2/torrents/uploadLimit HTTP/1.1 +User-Agent: Fiddler +Host: 127.0.0.1 +Cookie: SID=your_sid +Content-Type: application/x-www-form-urlencoded +Content-Length: length + +hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32|284b83c9c7935002391129fd97f43db5d7cc2ba0 +``` + +`hashes` can contain multiple hashes separated by `|` or set to `all` + +Server reply (example): + +```http +HTTP/1.1 200 OK +content-type: application/json +content-length: length + +{"8c212779b4abde7c6bc608063a0d008b7e40ce32":338944,"284b83c9c7935002391129fd97f43db5d7cc2ba0":123} +``` + +`8c212779b4abde7c6bc608063a0d008b7e40ce32` is the hash of the torrent in the request and `338944` its upload speed limit in bytes per second; this value will be zero if no limit is applied. + +## Set torrent upload limit ## + +Requires knowing the torrent hash. You can get it from [torrent list](#get-torrent-list). + +Name: `setUploadLimit` + +```http +POST /api/v2/torrents/setUploadLimit HTTP/1.1 +User-Agent: Fiddler +Host: 127.0.0.1 +Cookie: SID=your_sid +Content-Type: application/x-www-form-urlencoded +Content-Length: length + +hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32|284b83c9c7935002391129fd97f43db5d7cc2ba0&limit=131072 +``` + +`hashes` can contain multiple hashes separated by `|` or set to `all` +`limit` is the upload speed limit in bytes per second you want to set. + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios + +## Set torrent location ## + +Requires knowing the torrent hash. You can get it from [torrent list](#get-torrent-list). + +Name: `setLocation` + +```http +POST /api/v2/torrents/setLocation HTTP/1.1 +User-Agent: Fiddler +Host: 127.0.0.1 +Cookie: SID=your_sid +Content-Type: application/x-www-form-urlencoded +Content-Length: length + +hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32|284b83c9c7935002391129fd97f43db5d7cc2ba0&location=/mnt/nfs/media +``` + +`hashes` can contain multiple hashes separated by `|` or set to `all` +`location` is the location to download the torrent to. If the location doesn't exist, the torrent's location is unchanged. + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +400 | Save path is empty +403 | User does not have write access to directory +409 | Unable to create save path directory +200 | All other scenarios + +## Set torrent name ## + +Requires knowing the torrent hash. You can get it from [torrent list](#get-torrent-list). + +Name: `rename` + +```http +POST /api/v2/torrents/rename HTTP/1.1 +User-Agent: Fiddler +Host: 127.0.0.1 +Cookie: SID=your_sid +Content-Type: application/x-www-form-urlencoded +Content-Length: length + +hash=8c212779b4abde7c6bc608063a0d008b7e40ce32&name=This%20is%20a%20test +``` + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +404 | Torrent hash is invalid +409 | Torrent name is empty +200 | All other scenarios + +## Set torrent category ## + +Requires knowing the torrent hash. You can get it from [torrent list](#get-torrent-list). + +Name: `setCategory` + +```http +POST /api/v2/torrents/setCategory HTTP/1.1 +User-Agent: Fiddler +Host: 127.0.0.1 +Cookie: SID=your_sid +Content-Type: application/x-www-form-urlencoded +Content-Length: length + +hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32|284b83c9c7935002391129fd97f43db5d7cc2ba0&category=CategoryName +``` + +`hashes` can contain multiple hashes separated by `|` or set to `all` + +`category` is the torrent category you want to set. + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +409 | Category name does not exist +200 | All other scenarios + +## Get all categories ## + +Name: `categories` + +Parameters: + +None + +Returns all categories in JSON format, e.g.: + +```JSON +{ + "Video": { + "name": "Video", + "savePath": "/home/user/torrents/video/" + }, + "eBooks": { + "name": "eBooks", + "savePath": "/home/user/torrents/eBooks/" + } +} +``` +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios + +## Add new category ## + +Name: `createCategory` + +```http +POST /api/v2/torrents/createCategory HTTP/1.1 +User-Agent: Fiddler +Host: 127.0.0.1 +Cookie: SID=your_sid +Content-Type: application/x-www-form-urlencoded +Content-Length: length + +category=CategoryName&savePath=/path/to/dir +``` + +`category` is the category you want to create. + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +400 | Category name is empty +409 | Category name is invalid +200 | All other scenarios + +## Edit category ## + +Name: `editCategory` + +```http +POST /api/v2/torrents/editCategory HTTP/1.1 +User-Agent: Fiddler +Host: 127.0.0.1 +Cookie: SID=your_sid +Content-Type: application/x-www-form-urlencoded +Content-Length: length + +category=CategoryName&savePath=/path/to/save/torrents/to +``` + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +400 | Category name is empty +409 | Category editing failed +200 | All other scenarios + +## Remove categories ## + +Name: `removeCategories` + +```http +POST /api/v2/torrents/removeCategories HTTP/1.1 +User-Agent: Fiddler +Host: 127.0.0.1 +Cookie: SID=your_sid +Content-Type: application/x-www-form-urlencoded +Content-Length: length + +categories=Category1%0ACategory2 +``` + +`categories` can contain multiple cateogies separated by `\n` (%0A urlencoded) + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios + +## Add torrent tags ## + +Requires knowing the torrent hash. You can get it from [torrent list](#get-torrent-list). + +Name: `addTags` + +```http +POST /api/v2/torrents/addTags HTTP/1.1 +User-Agent: Fiddler +Host: 127.0.0.1 +Cookie: SID=your_sid +Content-Type: application/x-www-form-urlencoded +Content-Length: length + +hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32|284b83c9c7935002391129fd97f43db5d7cc2ba0&tags=TagName1,TagName2 +``` + +`hashes` can contain multiple hashes separated by `|` or set to `all` + +`tags` is the list of tags you want to add to passed torrents. + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios + +## Remove torrent tags ## + +Requires knowing the torrent hash. You can get it from [torrent list](#get-torrent-list). + +Name: `removeTags` + +```http +POST /api/v2/torrents/removeTags HTTP/1.1 +User-Agent: Fiddler +Host: 127.0.0.1 +Cookie: SID=your_sid +Content-Type: application/x-www-form-urlencoded +Content-Length: length + +hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32|284b83c9c7935002391129fd97f43db5d7cc2ba0&tags=TagName1,TagName2 +``` + +`hashes` can contain multiple hashes separated by `|` or set to `all` + +`tags` is the list of tags you want to remove from passed torrents. +Empty list removes all tags from relevant torrents. + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios + +## Get all tags ## + +Name: `tags` + +Parameters: + +None + +Returns all tags in JSON format, e.g.: + +```JSON +[ + "Tag 1", + "Tag 2" +] +``` +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios + +## Create tags ## + +Name: `createTags` + +```http +POST /api/v2/torrents/createTags HTTP/1.1 +User-Agent: Fiddler +Host: 127.0.0.1 +Cookie: SID=your_sid +Content-Type: application/x-www-form-urlencoded +Content-Length: length + +tags=TagName1,TagName2 +``` +`tags` is a list of tags you want to create. +Can contain multiple tags separated by `,`. + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios + +## Delete tags ## + +Name: `deleteTags` + +```http +POST /api/v2/torrents/deleteTags HTTP/1.1 +User-Agent: Fiddler +Host: 127.0.0.1 +Cookie: SID=your_sid +Content-Type: application/x-www-form-urlencoded +Content-Length: length + +tags=TagName1,TagName2 +``` + +`tags` is a list of tags you want to delete. +Can contain multiple tags separated by `,`. + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios + +## Set automatic torrent management ## + +Requires knowing the torrent hash. You can get it from [torrent list](#get-torrent-list). + +Name: `setAutoManagement` + +```http +POST /api/v2/torrents/setAutoManagement HTTP/1.1 +User-Agent: Fiddler +Host: 127.0.0.1 +Cookie: SID=your_sid +Content-Type: application/x-www-form-urlencoded +Content-Length: length + +hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32|284b83c9c7935002391129fd97f43db5d7cc2ba0&enable=true +``` + +`hashes` can contain multiple hashes separated by `|` or set to `all` +`enable` is a boolean, affects the torrents listed in `hashes`, default is `false` + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios + +## Toggle sequential download ## + +Requires knowing the torrent hash. You can get it from [torrent list](#get-torrent-list). + +Name: `toggleSequentialDownload` + +**Parameters:** + +Parameter | Type | Description +------------|----------|------------ +`hashes` | string | The hashes of the torrents you want to toggle sequential download for. `hashes` can contain multiple hashes separated by `\|`, to toggle sequential download for multiple torrents, or set to `all`, to toggle sequential download for all torrents. + +Example: + +```http +/api/v2/torrents/toggleSequentialDownload?hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32|54eddd830a5b58480a6143d616a97e3a6c23c439 +``` + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios + +## Set first/last piece priority ## + +Requires knowing the torrent hash. You can get it from [torrent list](#get-torrent-list). + +Name: `toggleFirstLastPiecePrio` + +**Parameters:** + +Parameter | Type | Description +------------|----------|------------ +`hashes` | string | The hashes of the torrents you want to toggle the first/last piece priority for. `hashes` can contain multiple hashes separated by `\|`, to toggle the first/last piece priority for multiple torrents, or set to `all`, to toggle the first/last piece priority for all torrents. + +Example: + +```http +/api/v2/torrents/toggleFirstLastPiecePrio?hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32|54eddd830a5b58480a6143d616a97e3a6c23c439 +``` + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios + +## Set force start ## + +Requires knowing the torrent hash. You can get it from [torrent list](#get-torrent-list). + +Name: `setForceStart` + +```http +POST /api/v2/torrents/setForceStart HTTP/1.1 +User-Agent: Fiddler +Host: 127.0.0.1 +Cookie: SID=your_sid +Content-Type: application/x-www-form-urlencoded +Content-Length: length + +hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32?value=true +``` + +`hashes` can contain multiple hashes separated by `|` or set to `all` +`value` is a boolean, affects the torrents listed in `hashes`, default is `false` + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios + +## Set super seeding ## + +Requires knowing the torrent hash. You can get it from [torrent list](#get-torrent-list). + +Name: `setSuperSeeding` + +```http +POST /api/v2/torrents/setSuperSeeding HTTP/1.1 +User-Agent: Fiddler +Host: 127.0.0.1 +Cookie: SID=your_sid +Content-Type: application/x-www-form-urlencoded +Content-Length: length + +hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32?value=true +``` + +`hashes` can contain multiple hashes separated by `|` or set to `all` +`value` is a boolean, affects the torrents listed in `hashes`, default is `false` + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios + +## Rename file ## + +Name: `renameFile` + +**Parameters:** + +Parameter | Type | Description +----------------------------------|----------|------------ +`hash` | string | The hash of the torrent +`oldPath` | string | The old path of the torrent +`newPath` | string | The new path to use for the file + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +400 | Missing `newPath` parameter +409 | Invalid `newPath` or `oldPath`, or `newPath` already in use +200 | All other scenarios + +## Rename folder ## + +Name: `renameFolder` + +**Parameters:** + +Parameter | Type | Description +----------------------------------|----------|------------ +`hash` | string | The hash of the torrent +`oldPath` | string | The old path of the torrent +`newPath` | string | The new path to use for the file + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +400 | Missing `newPath` parameter +409 | Invalid `newPath` or `oldPath`, or `newPath` already in use +200 | All other scenarios + +# RSS (experimental) # + +All RSS API methods are under "rss", e.g.: `/api/v2/rss/methodName`. + +## Add folder ## + +Name: `addFolder` + +Parameters: + +Parameter | Type | Description +----------------------------------|---------|------------ +`path` | string | Full path of added folder (e.g. "The Pirate Bay\Top100") + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +409 | Failure to add folder +200 | All other scenarios + +## Add feed ## + +Name: `addFeed` + +Parameters: + +Parameter | Type | Description +----------------------------------|---------|------------ +`url` | string | URL of RSS feed (e.g. "[http://thepiratebay.org/rss//top100/200](http://thepiratebay.org/rss//top100/200)") +`path` _optional_ | string | Full path of added folder (e.g. "The Pirate Bay\Top100\Video") + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +409 | Failure to add feed +200 | All other scenarios + +## Remove item ## + +Removes folder or feed. + +Name: `removeItem` + +Parameters: + +Parameter | Type | Description +----------------------------------|---------|------------ +`path` | string | Full path of removed item (e.g. "The Pirate Bay\Top100") + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +409 | Failure to remove item +200 | All other scenarios + +## Move item ## + +Moves/renames folder or feed. + +Name: `moveItem` + +Parameters: + +Parameter | Type | Description +----------------------------------|---------|------------ +`itemPath` | string | Current full path of item (e.g. "The Pirate Bay\Top100") +`destPath` | string | New full path of item (e.g. "The Pirate Bay") + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +409 | Failure to move item +200 | All other scenarios + +## Get all items ## + +Name: `items` + +Parameters: + +Parameter | Type | Description +----------------------------------|---------|------------ +`withData` _optional_ | bool | True if you need current feed articles + +Returns all RSS items in JSON format, e.g.: + +```JSON +{ + "HD-Torrents.org": "https://hd-torrents.org/rss.php", + "PowerfulJRE": "https://www.youtube.com/feeds/videos.xml?channel_id=UCzQUP1qoWDoEbmsQxvdjxgQ", + "The Pirate Bay": { + "Audio": "https://thepiratebay.org/rss//top100/100", + "Video": "https://thepiratebay.org/rss//top100/200" + } +} +``` + +## Mark as read ## + +If `articleId` is provided only the article is marked as read otherwise the whole feed is going to be marked as read. + +Name: `markAsRead` + +Parameters: + +Parameter | Type | Description +----------------------------------|---------|------------ +`itemPath` | string | Current full path of item (e.g. "The Pirate Bay\Top100") +`articleId` _optional_ | string | ID of article + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios + +## Refresh item ## + +Refreshes folder or feed. + +Name: `refreshItem` + +Parameters: + +Parameter | Type | Description +----------------------------------|---------|------------ +`itemPath` | string | Current full path of item (e.g. "The Pirate Bay\Top100") + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios + +## Set auto-downloading rule ## + +Name: `setRule` + +Parameters: + +Parameter | Type | Description +----------------------------------|---------|------------ +`ruleName` | string | Rule name (e.g. "Punisher") +`ruleDef` | string | JSON encoded rule definition + +Rule definition is JSON encoded dictionary with the following fields: + +Field | Type | Description +----------------------------------|---------|------------ +`enabled` | bool | Whether the rule is enabled +`mustContain` | string | The substring that the torrent name must contain +`mustNotContain` | string | The substring that the torrent name must not contain +`useRegex` | bool | Enable regex mode in "mustContain" and "mustNotContain" +`episodeFilter` | string | Episode filter definition +`smartFilter` | bool | Enable smart episode filter +`previouslyMatchedEpisodes` | list | The list of episode IDs already matched by smart filter +`affectedFeeds` | list | The feed URLs the rule applied to +`ignoreDays` | number | Ignore sunsequent rule matches +`lastMatch` | string | The rule last match time +`addPaused` | bool | Add matched torrent in paused mode +`assignedCategory` | string | Assign category to the torrent +`savePath` | string | Save torrent to the given directory + +E.g.: + +```JSON +{ + "enabled": false, + "mustContain": "The *Punisher*", + "mustNotContain": "", + "useRegex": false, + "episodeFilter": "1x01-;", + "smartFilter": false, + "previouslyMatchedEpisodes": [ + ], + "affectedFeeds": [ + "http://showrss.info/user/134567.rss?magnets=true" + ], + "ignoreDays": 0, + "lastMatch": "20 Nov 2017 09:05:11", + "addPaused": true, + "assignedCategory": "", + "savePath": "C:/Users/JohnDoe/Downloads/Punisher" +} +``` + +## Rename auto-downloading rule ## + +Name: `renameRule` + +Parameters: + +Parameter | Type | Description +----------------------------------|---------|------------ +`ruleName` | string | Rule name (e.g. "Punisher") +`newRuleName` | string | New rule name (e.g. "The Punisher") + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios + +## Remove auto-downloading rule ## + +Name: `removeRule` + +Parameters: + +Parameter | Type | Description +----------------------------------|---------|------------ +`ruleName` | string | Rule name (e.g. "Punisher") + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios + + +## Get all auto-downloading rules ## + +Name: `rules` + +Returns all auto-downloading rules in JSON format, e.g.: + +```JSON +{ + "The Punisher": { + "enabled": false, + "mustContain": "The *Punisher*", + "mustNotContain": "", + "useRegex": false, + "episodeFilter": "1x01-;", + "smartFilter": false, + "previouslyMatchedEpisodes": [ + ], + "affectedFeeds": [ + "http://showrss.info/user/134567.rss?magnets=true" + ], + "ignoreDays": 0, + "lastMatch": "20 Nov 2017 09:05:11", + "addPaused": true, + "assignedCategory": "", + "savePath": "C:/Users/JohnDoe/Downloads/Punisher" + } +} +``` +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios + +## Get all articles matching a rule ## + +Name: `matchingArticles` + +Parameter | Type | Description +----------------------------------|---------|------------ +`ruleName` | string | Rule name (e.g. "Linux") + + +Returns all articles that match a rule by feed name in JSON format, e.g.: + +```JSON +{ + "DistroWatch":[ + "sparkylinux-5.11-i686-minimalgui.iso.torrent", + "sparkylinux-5.11-x86_64-minimalgui.iso.torrent", + "sparkylinux-5.11-i686-xfce.iso.torrent", + "bluestar-linux-5.6.3-2020.04.09-x86_64.iso.torrent", + "robolinux64-mate3d-v10.10.iso.torrent", + ], + "Linuxtracker":[ + "[Alpine Linux] alpine-extended-3.11.6", + "[Alpine Linux] alpine-standard-3.11.6", + "[Linuxfx] linuxfx10-wxs-lts-beta5.iso", + "[Linux Lite] linux-lite-5.0-rc1-64bit.iso (MULTI)", + "[Scientific Linux] SL-7.8-x86_64-Pack", + "[NixOS] nixos-plasma5-20.03.1418.5272327b81e-x86_64-linux.iso" + ] +} +``` + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios + + +# Search # + +All Search API methods are under "search", e.g.: `/api/v2/search/methodName`. + +## Start search ## + +Name: `start` + +**Parameters:** + +Parameter | Type | Description +----------------------------------|---------|------------ +`pattern` | string | Pattern to search for (e.g. "Ubuntu 18.04") +`plugins` | string | Plugins to use for searching (e.g. "legittorrents"). Supports multiple plugins separated by `\|`. Also supports `all` and `enabled` +`category` | string | Categories to limit your search to (e.g. "legittorrents"). Available categories depend on the specified `plugins`. Also supports `all` + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +409 | User has reached the limit of max `Running` searches (currently set to 5) +200 | All other scenarios- see JSON below + +The response is a JSON object with the following fields + +Field | Type | Description +----------------------------------|---------|------------ +`id` | number | ID of the search job + +Example: + +```JSON +{ + "id": 12345 +} +``` + +## Stop search ## + +Name: `stop` + +**Parameters:** + +Parameter | Type | Description +----------------------------------|---------|------------ +`id` | number | ID of the search job + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +404 | Search job was not found +200 | All other scenarios + +## Get search status ## + +Name: `status` + +**Parameters:** + +Parameter | Type | Description +----------------------------------|---------|------------ +`id` _optional_ | number | ID of the search job. If not specified, all search jobs are returned + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +404 | Search job was not found +200 | All other scenarios- see JSON below + +The response is a JSON array of objects containing the following fields + +Field | Type | Description +----------------------------------|---------|------------ +`id` | number | ID of the search job +`status` | string | Current status of the search job (either `Running` or `Stopped`) +`total` | number | Total number of results. If the status is `Running` this number may contineu to increase + +Example: + +```JSON +[ + { + "id": 12345, + "status": "Running", + "total": 170 + } +] +``` + +## Get search results ## + +Name: `results` + +**Parameters:** + +Parameter | Type | Description +----------------------------------|---------|------------ +`id` | number | ID of the search job +`limit` _optional_ | number | max number of results to return. 0 or negative means no limit +`offset` _optional_ | number | result to start at. A negative number means count backwards (e.g. `-2` returns the 2 most recent results) + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +404 | Search job was not found +409 | Offset is too large, or too small (e.g. absolute value of negative number is greater than # results) +200 | All other scenarios- see JSON below + +The response is a JSON object with the following fields + +Field | Type | Description +----------------------------------|---------|------------ +`results` | array | Array of `result` objects- see table below +`status` | string | Current status of the search job (either `Running` or `Stopped`) +`total` | number | Total number of results. If the status is `Running` this number may continue to increase + +**Result object:** + +Field | Type | Description +----------------------------------|---------|------------ +`descrLink` | string | URL of the torrent's description page +`fileName` | string | Name of the file +`fileSize` | number | Size of the file in Bytes +`fileUrl` | string | Torrent download link (usually either .torrent file or magnet link) +`nbLeechers` | number | Number of leechers +`nbSeeders` | number | Number of seeders +`siteUrl` | string | URL of the torrent site + +Example: + +```JSON +{ + "results": [ + { + "descrLink": "http://www.legittorrents.info/index.php?page=torrent-details&id=8d5f512e1acb687029b8d7cc6c5a84dce51d7a41", + "fileName": "Ubuntu-10.04-32bit-NeTV.ova", + "fileSize": -1, + "fileUrl": "http://www.legittorrents.info/download.php?id=8d5f512e1acb687029b8d7cc6c5a84dce51d7a41&f=Ubuntu-10.04-32bit-NeTV.ova.torrent", + "nbLeechers": 1, + "nbSeeders": 0, + "siteUrl": "http://www.legittorrents.info" + }, + { + "descrLink": "http://www.legittorrents.info/index.php?page=torrent-details&id=d5179f53e105dc2c2401bcfaa0c2c4936a6aa475", + "fileName": "mangOH-Legato-17_06-Ubuntu-16_04.ova", + "fileSize": -1, + "fileUrl": "http://www.legittorrents.info/download.php?id=d5179f53e105dc2c2401bcfaa0c2c4936a6aa475&f=mangOH-Legato-17_06-Ubuntu-16_04.ova.torrent", + "nbLeechers": 0, + "nbSeeders": 59, + "siteUrl": "http://www.legittorrents.info" + } + ], + "status": "Running", + "total": 2 +} +``` + +## Delete search ## + +Name: `delete` + +**Parameters:** + +Parameter | Type | Description +----------------------------------|---------|------------ +`id` | number | ID of the search job + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +404 | Search job was not found +200 | All other scenarios + +## Get search plugins ## + +Name: `plugins` + +**Parameters:** + +None + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios- see JSON below + +The response is a JSON array of objects containing the following fields + +Field | Type | Description +----------------------------------|---------|------------ +`enabled` | bool | Whether the plugin is enabled +`fullName` | string | Full name of the plugin +`name` | string | Short name of the plugin +`supportedCategories` | array | List of category objects +`url` | string | URL of the torrent site +`version` | string | Installed version of the plugin + +```JSON +[ + { + "enabled": true, + "fullName": "Legit Torrents", + "name": "legittorrents", + "supportedCategories": [{ + "id": "all", + "name": "All categories" + }, { + "id": "anime", + "name": "Anime" + }, { + "id": "books", + "name": "Books" + }, { + "id": "games", + "name": "Games" + }, { + "id": "movies", + "name": "Movies" + }, { + "id": "music", + "name": "Music" + }, { + "id": "tv", + "name": "TV shows" + }], + "url": "http://www.legittorrents.info", + "version": "2.3" + } +] +``` + +## Install search plugin ## + +Name: `installPlugin` + +**Parameters:** + +Parameter | Type | Description +----------------------------------|---------|------------ +`sources` | string | Url or file path of the plugin to install (e.g. "[https://raw.githubusercontent.com/qbittorrent/search-plugins/master/nova3/engines/legittorrents.py](https://raw.githubusercontent.com/qbittorrent/search-plugins/master/nova3/engines/legittorrents.py)"). Supports multiple sources separated by `\|` + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios + +## Uninstall search plugin ## + +Name: `uninstallPlugin` + +**Parameters:** + +Parameter | Type | Description +----------------------------------|---------|------------ +`names` | string | Name of the plugin to uninstall (e.g. "legittorrents"). Supports multiple names separated by `\|` + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios + +## Enable search plugin ## + +Name: `enablePlugin` + +**Parameters:** + +Parameter | Type | Description +----------------------------------|---------|------------ +`names` | string | Name of the plugin to enable/disable (e.g. "legittorrents"). Supports multiple names separated by `\|` +`enable` | bool | Whether the plugins should be enabled + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios + +## Update search plugins ## + +Name: `updatePlugins` + +**Parameters:** + +None + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios + +# WebAPI versioning # +WebAPI uses the following versioning: `1.2.3`: +1. Main version. Should be changed only on some global changes (e.g. total redesign/relayout) +2. Changed on incompatible API changes (i.e. if it breaks outdated clients). E.g. if you change/remove something +3. Changed on compatible API changes (i.e. if it doesn't break outdated clients). E.g. if you add something new outdated clients still can access old subset of API. diff --git a/api-gen/src/group.rs b/api-gen/src/group.rs new file mode 100644 index 0000000..9dfc9d3 --- /dev/null +++ b/api-gen/src/group.rs @@ -0,0 +1,455 @@ +use std::{collections::HashMap, vec::Vec}; + +use case::CaseExt; +use parser::types::TypeInfo; +use quote::{format_ident, quote}; +use regex::Regex; + +use crate::{skeleton::auth_ident, util}; + +pub fn generate_groups(groups: Vec) -> proc_macro2::TokenStream { + let gr = groups + .iter() + // implemented manually + .filter(|group| group.name != "authentication") + .map(generate_group); + + quote! { + #(#gr)* + } +} + +fn generate_group(group: &parser::ApiGroup) -> proc_macro2::TokenStream { + let group_name_camel = util::to_ident(&group.name.to_camel()); + let group_name_snake = util::to_ident(&group.name.to_snake()); + let auth = auth_ident(); + let methods = generate_methods(group, &auth, &group_name_camel); + + let group_method = util::add_docs( + &group.description, + quote! { + pub fn #group_name_snake(&self) -> #group_name_camel { + #group_name_camel::new(self) + } + }, + ); + + quote! { + pub struct #group_name_camel<'a> { + auth: &'a #auth, + } + + #methods + + impl #auth { + #group_method + } + } +} + +fn generate_methods( + group: &parser::ApiGroup, + auth: &syn::Ident, + group_name_camel: &syn::Ident, +) -> proc_macro2::TokenStream { + let methods_and_param_structs = group + .methods + .iter() + .map(|method| generate_method(group, method)); + + let methods = methods_and_param_structs.clone().map(|(method, ..)| method); + let structs = methods_and_param_structs.flat_map(|(_, s)| s); + + quote! { + impl <'a> #group_name_camel<'a> { + pub fn new(auth: &'a #auth) -> Self { + Self { auth } + } + + #(#methods)* + } + + #(#structs)* + } +} + +fn generate_method( + group: &parser::ApiGroup, + method: &parser::ApiMethod, +) -> (proc_macro2::TokenStream, Option) { + let method_name = util::to_ident(&method.name.to_snake()); + let url = format!("/api/v2/{}/{}", group.url, method.url); + + match &method.parameters { + Some(params) => create_method_with_params(group, method, params, &method_name, &url), + None => create_method_without_params(group, method, method_name, &url), + } +} + +fn create_method_without_params( + group: &parser::ApiGroup, + method: &parser::ApiMethod, + method_name: proc_macro2::Ident, + url: &str, +) -> (proc_macro2::TokenStream, Option) { + match create_return_type(group, method) { + Some((return_type_name, return_type)) => ( + util::add_docs( + &method.description, + quote! { + pub async fn #method_name(&self) -> Result<#return_type_name> { + let res = self.auth + .authenticated_client(#url) + .send() + .await? + .json::<#return_type_name>() + .await?; + + Ok(res) + } + }, + ), + Some(return_type), + ), + None => ( + util::add_docs( + &method.description, + quote! { + pub async fn #method_name(&self) -> Result { + let res = self.auth + .authenticated_client(#url) + .send() + .await? + .text() + .await?; + + Ok(res) + } + }, + ), // assume that all methods without a return type returns a string + None, + ), + } +} + +fn create_method_with_params( + group: &parser::ApiGroup, + method: &parser::ApiMethod, + params: &[parser::types::Type], + method_name: &proc_macro2::Ident, + url: &str, +) -> (proc_macro2::TokenStream, Option) { + let parameter_type = util::to_ident(&format!( + "{}{}Parameters", + group.name.to_camel(), + method.name.to_camel() + )); + + let mandatory_params = params + .iter() + .filter(|param| !param.get_type_info().is_optional); + + let mandatory_param_args = mandatory_params.clone().map(|param| { + let name = util::to_ident(¶m.get_type_info().name.to_snake()); + let t = util::to_ident(¶m.to_borrowed_type()); + + if param.should_borrow() { + quote! { + #name: &#t + } + } else { + quote! { + #name: #t + } + } + }); + + let mandatory_param_names = mandatory_params.clone().map(|param| { + let name = util::to_ident(¶m.get_type_info().name.to_snake()); + + quote! { + #name + } + }); + + let mandatory_param_args_clone = mandatory_param_args.clone(); + let mandatory_param_form_build = mandatory_params.map(|param| { + let n = ¶m.get_type_info().name; + let name = util::to_ident(&n.to_snake()); + + quote! { + let form = form.text(#n, #name.to_string()); + } + }); + + let optional_params = params + .iter() + .filter(|param| param.get_type_info().is_optional) + .map(|param| { + let n = ¶m.get_type_info().name; + let name = util::to_ident(&n.to_snake()); + let t = util::to_ident(¶m.to_borrowed_type()); + + let method = if param.should_borrow() { + quote! { + pub fn #name(mut self, value: &#t) -> Self { + self.form = self.form.text(#n, value.to_string()); + self + } + } + } else { + quote! { + pub fn #name(mut self, value: #t) -> Self { + self.form = self.form.text(#n, value.to_string()); + self + } + } + }; + + util::add_docs(¶m.get_type_info().description, method) + }); + + let group_name = util::to_ident(&group.name.to_camel()); + + let send = match create_return_type(group, method) { + Some((return_type_name, return_type)) => { + quote! { + impl<'a> #parameter_type<'a> { + fn new(group: &'a #group_name, #(#mandatory_param_args),*) -> Self { + let form = reqwest::multipart::Form::new(); + #(#mandatory_param_form_build)* + Self { group, form } + } + + #(#optional_params)* + + pub async fn send(self) -> Result<#return_type_name> { + let res = self.group + .auth + .authenticated_client(#url) + .multipart(self.form) + .send() + .await? + .json::<#return_type_name>() + .await?; + + Ok(res) + } + } + + #return_type + } + } + None => { + quote! { + impl<'a> #parameter_type<'a> { + fn new(group: &'a #group_name, #(#mandatory_param_args),*) -> Self { + let form = reqwest::multipart::Form::new(); + #(#mandatory_param_form_build)* + Self { group, form } + } + + #(#optional_params)* + + pub async fn send(self) -> Result { + let res = self.group + .auth + .authenticated_client(#url) + .multipart(self.form) + .send() + .await? + .text() + .await?; + + Ok(res) + } + } + } + } + }; + + ( + util::add_docs( + &method.description, + quote! { + pub fn #method_name(&self, #(#mandatory_param_args_clone),*) -> #parameter_type { + #parameter_type::new(self, #(#mandatory_param_names),*) + } + }, + ), + Some(quote! { + pub struct #parameter_type<'a> { + group: &'a #group_name<'a>, + form: reqwest::multipart::Form, + } + + #send + }), + ) +} + +fn create_return_type( + group: &parser::ApiGroup, + method: &parser::ApiMethod, +) -> Option<(proc_macro2::TokenStream, proc_macro2::TokenStream)> { + let return_type = match &method.return_type { + Some(t) => t, + None => return None, + }; + + let to_enum_name = |name: &str| { + format!( + "{}{}{}", + group.name.to_camel(), + method.name.to_camel(), + name.to_camel() + ) + }; + + let enum_types_with_names = + return_type + .parameters + .iter() + .flat_map(|parameter| match ¶meter.return_type { + parser::types::Type::Number(TypeInfo { + ref name, + type_description: Some(type_description), + .. + }) => { + let enum_fields = type_description.values.iter().map(|value| { + let v = &value.value; + let re = Regex::new(r#"\(.*\)"#).unwrap(); + let desc = &value + .description + .replace(' ', "_") + .replace('-', "_") + .replace(',', "_"); + let desc_without_parentheses = re.replace_all(desc, ""); + let ident = util::to_ident(&desc_without_parentheses.to_camel()); + + util::add_docs( + &Some(value.description.clone()), + quote! { + #[serde(rename = #v)] + #ident + }, + ) + }); + + let enum_name = util::to_ident(&to_enum_name(name)); + + Some(( + name, + quote! { + #[allow(clippy::enum_variant_names)] + #[derive(Debug, Deserialize, PartialEq, Eq)] + pub enum #enum_name { + #(#enum_fields,)* + } + }, + )) + } + parser::types::Type::String(TypeInfo { + ref name, + type_description: Some(type_description), + .. + }) => { + let enum_fields = type_description.values.iter().map(|type_description| { + let value = &type_description.value; + let value_as_ident = util::to_ident(&value.to_camel()); + + util::add_docs( + &Some(type_description.description.clone()), + quote! { + #[serde(rename = #value)] + #value_as_ident + }, + ) + }); + + let enum_name = util::to_ident(&to_enum_name(name)); + + Some(( + name, + quote! { + #[allow(clippy::enum_variant_names)] + #[derive(Debug, Deserialize, PartialEq, Eq)] + pub enum #enum_name { + #(#enum_fields,)* + } + }, + )) + } + _ => None, + }); + + let enum_names: HashMap<&String, String> = enum_types_with_names + .clone() + .map(|(enum_name, _)| (enum_name, to_enum_name(enum_name))) + .collect(); + + let enum_types = enum_types_with_names.map(|(_, enum_type)| enum_type); + + let parameters = return_type.parameters.iter().map(|parameter| { + let namestr = ¶meter.name; + let name = util::to_ident(&namestr.to_snake().replace("__", "_")); + let rtype = if let Some(enum_type) = enum_names.get(namestr) { + util::to_ident(enum_type) + } else { + util::to_ident(¶meter.return_type.to_owned_type()) + }; + let type_info = parameter.return_type.get_type_info(); + + let rtype_as_quote = if type_info.is_list { + quote! { + std::vec::Vec<#rtype> + } + } else { + quote! { + #rtype + } + }; + + // "type" is a reserved keyword in Rust, so we use a different name. + if namestr == "type" { + let non_reserved_name = format_ident!("t_{}", name); + quote! { + #[serde(rename = #namestr)] + pub #non_reserved_name: #rtype_as_quote + } + } else { + quote! { + #[serde(rename = #namestr)] + pub #name: #rtype_as_quote + } + } + }); + + let return_type_name = util::to_ident(&format!( + "{}{}Result", + &group.name.to_camel(), + &method.name.to_camel() + )); + + let result_type = if return_type.is_list { + quote! { + std::vec::Vec<#return_type_name> + } + } else { + quote! { + #return_type_name + } + }; + + Some(( + result_type, + quote! { + #[derive(Debug, Deserialize)] + pub struct #return_type_name { + #(#parameters,)* + } + + #(#enum_types)* + }, + )) +} diff --git a/api-gen/src/lib.rs b/api-gen/src/lib.rs new file mode 100644 index 0000000..a9a670f --- /dev/null +++ b/api-gen/src/lib.rs @@ -0,0 +1,33 @@ +mod group; +mod skeleton; +mod util; + +use case::CaseExt; +use proc_macro::TokenStream; +use quote::quote; +use skeleton::generate_skeleton; +use syn::parse_macro_input; + +use crate::group::generate_groups; + +const API_CONTENT: &str = include_str!("api-4_1.md"); + +#[proc_macro_derive(QBittorrentApiGen, attributes(api_gen))] +pub fn derive(input: TokenStream) -> TokenStream { + let ast = parse_macro_input!(input as syn::DeriveInput); + let ident = &ast.ident; + + let api_groups = parser::parse_api_groups(API_CONTENT); + + let skeleton = generate_skeleton(ident); + let groups = generate_groups(api_groups); + let impl_ident = syn::Ident::new(&format!("{}_impl", ident).to_snake(), ident.span()); + + quote! { + mod #impl_ident { + #skeleton + #groups + } + } + .into() +} diff --git a/api-gen/src/skeleton.rs b/api-gen/src/skeleton.rs new file mode 100644 index 0000000..e08050e --- /dev/null +++ b/api-gen/src/skeleton.rs @@ -0,0 +1,104 @@ +use quote::quote; + +use crate::util; + +pub const AUTH_IDENT: &str = "Authenticated"; + +pub fn auth_ident() -> proc_macro2::Ident { + util::to_ident(AUTH_IDENT) +} + +pub fn generate_skeleton(ident: &syn::Ident) -> proc_macro2::TokenStream { + let auth = auth_ident(); + + quote! { + use reqwest::RequestBuilder; + use serde::Deserialize; + use thiserror::Error; + + use super::#ident; + + impl #ident { + /// Creates an authenticated client. + /// base_url is the url to the qbittorrent instance, i.e. http://localhost:8080 + pub async fn login( + base_url: &str, + username: &str, + password: &str, + ) -> Result<#auth> { + let client = reqwest::Client::new(); + + let form = reqwest::multipart::Form::new() + .text("username", username.to_string()) + .text("password", password.to_string()); + + let auth_resp = client + .post(format!("{}/api/v2/auth/login", base_url)) + .multipart(form) + .send() + .await?; + + let cookie_header = match auth_resp.headers().get("set-cookie") { + Some(header) => header.to_str().unwrap(), + None => { + return Err(Error::InvalidUsernameOrPassword); + } + }; + + fn parse_cookie(input: &str) -> Result<&str> { + match input.split(';').next() { + Some(res) => Ok(res), + _ => Err(Error::AuthCookieParseError), + } + } + + let auth_cookie = parse_cookie(cookie_header)?; + + Ok(#auth { + client, + auth_cookie: auth_cookie.to_string(), + base_url: base_url.to_string(), + }) + } + } + + #[allow(clippy::enum_variant_names)] + #[derive(Debug, Error)] + pub enum Error { + #[error("failed to parse auth cookie")] + AuthCookieParseError, + #[error("invalid username or password (failed to parse auth cookie)")] + InvalidUsernameOrPassword, + #[error("request error: {0}")] + HttpError(#[from] reqwest::Error), + } + + type Result = std::result::Result; + + #[derive(Debug)] + pub struct #auth { + auth_cookie: String, + base_url: String, + client: reqwest::Client, + } + + impl #auth { + fn authenticated_client(&self, url: &str) -> RequestBuilder { + let url = format!("{}{}", self.base_url, url); + let cookie = self.auth_cookie.clone(); + + self.client + .post(url) + .header("cookie", cookie) + } + + pub async fn logout(self) -> Result<()> { + self.authenticated_client("/api/v2/auth/logout") + .send() + .await?; + + Ok(()) + } + } + } +} diff --git a/api-gen/src/util.rs b/api-gen/src/util.rs new file mode 100644 index 0000000..fae60f5 --- /dev/null +++ b/api-gen/src/util.rs @@ -0,0 +1,19 @@ +use quote::quote; +use quote::ToTokens; + +pub fn to_ident(name: &str) -> proc_macro2::Ident { + syn::Ident::new(name, proc_macro2::Span::call_site()) +} + +pub fn add_docs(docs: &Option, stream: T) -> proc_macro2::TokenStream { + if let Some(docs) = docs { + quote! { + #[doc = #docs] + #stream + } + } else { + quote! { + #stream + } + } +} diff --git a/api-gen/tests/add_torrent.rs b/api-gen/tests/add_torrent.rs new file mode 100644 index 0000000..b493a0c --- /dev/null +++ b/api-gen/tests/add_torrent.rs @@ -0,0 +1,20 @@ +use anyhow::Result; +use api_gen::QBittorrentApiGen; + +const USERNAME: &str = "admin"; +const PASSWORD: &str = "adminadmin"; +const BASE_URL: &str = "http://localhost:8080"; + +#[derive(QBittorrentApiGen)] +struct Api {} + +#[tokio::main] +async fn main() -> Result<()> { + let api = Api::login(BASE_URL, USERNAME, PASSWORD).await?; + + // assuming this torrent will exist for a while: http://www.legittorrents.info/index.php?page=torrent-details&id=5cc013e801095be61d768e609e3039da58616fd0 + const TORRENT_URL: &str = "http://www.legittorrents.info/download.php?id=5cc013e801095be61d768e609e3039da58616fd0&f=Oddepoxy%20-%20Oddepoxy%20(2013)%20[OGG%20320%20CBR].torrent"; + let _ = api.torrent_management().add(TORRENT_URL).send().await?; + + Ok(()) +} diff --git a/api-gen/tests/another_struct_name.rs b/api-gen/tests/another_struct_name.rs new file mode 100644 index 0000000..c7e8ccf --- /dev/null +++ b/api-gen/tests/another_struct_name.rs @@ -0,0 +1,16 @@ +use anyhow::Result; +use api_gen::QBittorrentApiGen; + +const USERNAME: &str = "admin"; +const PASSWORD: &str = "adminadmin"; +const BASE_URL: &str = "http://localhost:8080"; + +#[derive(QBittorrentApiGen)] +struct Foo {} + +#[tokio::main] +async fn main() -> Result<()> { + let _ = Foo::login(BASE_URL, USERNAME, PASSWORD).await?; + + Ok(()) +} diff --git a/api-gen/tests/default_parameters.rs b/api-gen/tests/default_parameters.rs new file mode 100644 index 0000000..d6e7548 --- /dev/null +++ b/api-gen/tests/default_parameters.rs @@ -0,0 +1,26 @@ +use anyhow::Result; +use api_gen::QBittorrentApiGen; + +const USERNAME: &str = "admin"; +const PASSWORD: &str = "adminadmin"; +const BASE_URL: &str = "http://localhost:8080"; + +#[derive(QBittorrentApiGen)] +struct Api {} + +#[tokio::main] +async fn main() -> Result<()> { + let api = Api::login(BASE_URL, USERNAME, PASSWORD).await?; + + let _ = api + .log() + .main() + .normal(true) + .info(false) + .warning(true) + .critical(false) + .send() + .await?; + + Ok(()) +} diff --git a/api-gen/tests/login.rs b/api-gen/tests/login.rs new file mode 100644 index 0000000..0ab62fd --- /dev/null +++ b/api-gen/tests/login.rs @@ -0,0 +1,16 @@ +use anyhow::Result; +use api_gen::QBittorrentApiGen; + +const USERNAME: &str = "admin"; +const PASSWORD: &str = "adminadmin"; +const BASE_URL: &str = "http://localhost:8080"; + +#[derive(QBittorrentApiGen)] +struct Api {} + +#[tokio::main] +async fn main() -> Result<()> { + let _ = Api::login(BASE_URL, USERNAME, PASSWORD).await?; + + Ok(()) +} diff --git a/api-gen/tests/logout.rs b/api-gen/tests/logout.rs new file mode 100644 index 0000000..664ceba --- /dev/null +++ b/api-gen/tests/logout.rs @@ -0,0 +1,17 @@ +use anyhow::Result; +use api_gen::QBittorrentApiGen; + +const USERNAME: &str = "admin"; +const PASSWORD: &str = "adminadmin"; +const BASE_URL: &str = "http://localhost:8080"; + +#[derive(QBittorrentApiGen)] +struct Api {} + +#[tokio::main] +async fn main() -> Result<()> { + let api = Api::login(BASE_URL, USERNAME, PASSWORD).await?; + api.logout().await?; + + Ok(()) +} diff --git a/api-gen/tests/return_type.rs b/api-gen/tests/return_type.rs new file mode 100644 index 0000000..8f54447 --- /dev/null +++ b/api-gen/tests/return_type.rs @@ -0,0 +1,19 @@ +use anyhow::Result; +use api_gen::QBittorrentApiGen; + +const USERNAME: &str = "admin"; +const PASSWORD: &str = "adminadmin"; +const BASE_URL: &str = "http://localhost:8080"; + +#[derive(QBittorrentApiGen)] +struct Api {} + +#[tokio::main] +async fn main() -> Result<()> { + let api = Api::login(BASE_URL, USERNAME, PASSWORD).await?; + + let build_info = api.application().build_info().await?; + assert!(!build_info.qt.is_empty()); + + Ok(()) +} diff --git a/api-gen/tests/return_type_enum.rs b/api-gen/tests/return_type_enum.rs new file mode 100644 index 0000000..6b9faec --- /dev/null +++ b/api-gen/tests/return_type_enum.rs @@ -0,0 +1,24 @@ +use anyhow::Result; +use api_gen::QBittorrentApiGen; + +const USERNAME: &str = "admin"; +const PASSWORD: &str = "adminadmin"; +const BASE_URL: &str = "http://localhost:8080"; + +#[derive(QBittorrentApiGen)] +struct Api {} + +#[tokio::main] +async fn main() -> Result<()> { + let api = Api::login(BASE_URL, USERNAME, PASSWORD).await?; + + // need a torrent in order for info to work + const TORRENT_URL: &str = "http://www.legittorrents.info/download.php?id=5cc013e801095be61d768e609e3039da58616fd0&f=Oddepoxy%20-%20Oddepoxy%20(2013)%20[OGG%20320%20CBR].torrent"; + let _ = api.torrent_management().add(TORRENT_URL).send().await?; + + let info = api.torrent_management().info().send().await?; + let first = &info[0]; + assert_ne!(first.state, api_impl::TorrentManagementInfoState::Unknown); + + Ok(()) +} diff --git a/api-gen/tests/return_type_with_optional_params.rs b/api-gen/tests/return_type_with_optional_params.rs new file mode 100644 index 0000000..37b7eb2 --- /dev/null +++ b/api-gen/tests/return_type_with_optional_params.rs @@ -0,0 +1,25 @@ +use anyhow::Result; +use api_gen::QBittorrentApiGen; + +const USERNAME: &str = "admin"; +const PASSWORD: &str = "adminadmin"; +const BASE_URL: &str = "http://localhost:8080"; + +#[derive(QBittorrentApiGen)] +struct Api {} + +#[tokio::main] +async fn main() -> Result<()> { + let api = Api::login(BASE_URL, USERNAME, PASSWORD).await?; + + // need a torrent in order for info to work + const TORRENT_URL: &str = "http://www.legittorrents.info/download.php?id=5cc013e801095be61d768e609e3039da58616fd0&f=Oddepoxy%20-%20Oddepoxy%20(2013)%20[OGG%20320%20CBR].torrent"; + let _ = api.torrent_management().add(TORRENT_URL).send().await?; + + let info = api.torrent_management().info().send().await?; + let first = &info[0]; + // just check that something is there + assert_ne!(first.added_on, 0); + + Ok(()) +} diff --git a/api-gen/tests/tests.rs b/api-gen/tests/tests.rs new file mode 100644 index 0000000..b2bbc2b --- /dev/null +++ b/api-gen/tests/tests.rs @@ -0,0 +1,21 @@ +#[test] +fn tests() { + let t = trybuild::TestCases::new(); + // --- Auth --- + t.pass("tests/login.rs"); + t.pass("tests/logout.rs"); + + // --- Parameters --- + t.pass("tests/without_parameters.rs"); + // t.pass("tests/with_parameters.rs"); + t.pass("tests/default_parameters.rs"); + + // --- Return types --- + t.pass("tests/return_type.rs"); + t.pass("tests/return_type_with_optional_params.rs"); + t.pass("tests/return_type_enum.rs"); + + // --- Misc --- + t.pass("tests/add_torrent.rs"); + t.pass("tests/another_struct_name.rs"); +} diff --git a/api-gen/tests/without_parameters.rs b/api-gen/tests/without_parameters.rs new file mode 100644 index 0000000..a97db3e --- /dev/null +++ b/api-gen/tests/without_parameters.rs @@ -0,0 +1,20 @@ +use anyhow::Result; +use api_gen::QBittorrentApiGen; + +const USERNAME: &str = "admin"; +const PASSWORD: &str = "adminadmin"; +const BASE_URL: &str = "http://localhost:8080"; + +#[derive(QBittorrentApiGen)] +struct Api {} + +#[tokio::main] +async fn main() -> Result<()> { + let api = Api::login(BASE_URL, USERNAME, PASSWORD).await?; + let version = api.application().version().await?; + + // don't be too specific + assert!(version.starts_with("v4.4"), "got: {}", version); + + Ok(()) +} diff --git a/md-parser/Cargo.lock b/md-parser/Cargo.lock new file mode 100644 index 0000000..1317185 --- /dev/null +++ b/md-parser/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "md-parser" +version = "0.1.0" diff --git a/md-parser/Cargo.toml b/md-parser/Cargo.toml new file mode 100644 index 0000000..00ef5df --- /dev/null +++ b/md-parser/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "md-parser" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/md-parser/src/lib.rs b/md-parser/src/lib.rs new file mode 100644 index 0000000..fbe1876 --- /dev/null +++ b/md-parser/src/lib.rs @@ -0,0 +1,318 @@ +use std::{cell::RefCell, rc::Rc}; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum MdContent { + Text(String), + Asterix(String), + Table(Table), +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Table { + pub header: TableRow, + pub split: String, + pub rows: Vec, +} + +impl Table { + fn raw(&self) -> String { + let mut output = Vec::new(); + output.push(self.header.raw.clone()); + output.push(self.split.clone()); + for row in self.rows.clone() { + output.push(row.raw); + } + + output.join("\n") + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct TableRow { + raw: String, + pub columns: Vec, +} + +impl MdContent { + pub fn inner_value_as_string(&self) -> String { + match self { + MdContent::Text(text) => text.into(), + MdContent::Asterix(text) => text.into(), + MdContent::Table(table) => table.raw(), + } + } +} + +#[derive(Debug, Clone)] +pub struct Header { + level: i32, + content: String, +} + +/// These are the only relevant tokens we need for the api generation. +#[derive(Debug)] +pub enum MdToken { + Header(Header), + Content(MdContent), +} + +impl MdToken { + fn parse_token(line: &str) -> MdToken { + if line.starts_with('#') { + let mut level = 0; + for char in line.chars() { + if char != '#' { + break; + } + + level += 1; + } + + MdToken::Header(Header { + level, + content: line.trim_matches('#').trim().to_string(), + }) + } else if line.starts_with('*') { + MdToken::Content(MdContent::Asterix( + line.trim_matches('*').trim().to_string(), + )) + } else { + MdToken::Content(MdContent::Text(line.to_string())) + } + } + + fn from(content: &str) -> Vec { + let mut output = Vec::new(); + + let mut iter = content.lines().into_iter(); + while let Some(line) = iter.next() { + // assume this is a table + if line.contains('|') { + let to_columns = |column_line: &str| { + column_line + .replace('`', "") + .split('|') + .map(|s| s.trim().to_string()) + .collect() + }; + + let table_header = TableRow { + raw: line.into(), + columns: to_columns(line), + }; + let table_split = iter.next().unwrap(); + let mut table_rows = Vec::new(); + while let Some(row_line) = iter.next() { + if !row_line.contains('|') { + // we've reached the end of the table, let's go back one step + iter.next_back(); + break; + } + + let table_row = TableRow { + raw: row_line.into(), + columns: to_columns(row_line), + }; + + table_rows.push(table_row); + } + + output.push(MdToken::Content(MdContent::Table(Table { + header: table_header, + split: table_split.to_string(), + rows: table_rows, + }))); + } else { + output.push(MdToken::parse_token(line)); + } + } + + output + } +} + +#[derive(Debug)] +pub struct TokenTree { + pub title: Option, + pub content: Vec, + pub children: Vec, +} + +impl From> for TokenTree { + fn from(builder: Rc) -> Self { + let children = builder + .children + .clone() + .into_inner() + .into_iter() + .map(|child| child.into()) + .collect::>(); + + let content = builder.content.clone().into_inner(); + + TokenTree { + title: builder.title.clone(), + content, + children, + } + } +} + +#[derive(Debug, Default)] +pub struct TokenTreeFactory { + title: Option, + content: RefCell>, + children: RefCell>>, + level: i32, +} + +impl TokenTreeFactory { + fn new(title: &str, level: i32) -> Self { + Self { + title: if title.is_empty() { + None + } else { + Some(title.to_string()) + }, + level, + ..Default::default() + } + } + + fn add_content(&self, content: MdContent) { + self.content.borrow_mut().push(content); + } + + fn append(&self, child: &Rc) { + self.children.borrow_mut().push(child.clone()); + } + + pub fn create(content: &str) -> TokenTree { + let tokens = MdToken::from(content); + + let mut stack = Vec::new(); + let root = Rc::new(TokenTreeFactory::default()); + stack.push(root.clone()); + + for token in tokens { + match token { + MdToken::Header(Header { level, content }) => { + let new_header = Rc::new(TokenTreeFactory::new(&content, level)); + + // go back until we're at the same or lower level. + while let Some(current) = stack.pop() { + if current.level < level { + current.append(&new_header); + stack.push(current); + break; + } + } + + stack.push(new_header.clone()); + } + MdToken::Content(content) => { + let current = stack.pop().unwrap(); + current.add_content(content); + stack.push(current); + } + } + } + + root.into() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn should_remove_surrounding_asterix() { + // given + let input = r#" +# A +**B** + "# + .trim_matches('\n') + .trim(); + + // when + let tree = TokenTreeFactory::create(input); + + // then + println!("{:#?}", tree); + let first = tree.children.first().unwrap(); + let content = first.content.first().unwrap(); + assert_eq!(*content, MdContent::Asterix("B".into())); + } + + #[test] + fn should_remove_surrounding_hash() { + // given + let input = r#" +# A # + "# + .trim_matches('\n') + .trim(); + + // when + let tree = TokenTreeFactory::create(input); + + // then + println!("{:#?}", tree); + assert_eq!(tree.children.first().unwrap().title, Some("A".into())); + } + + #[test] + fn single_level() { + // given + let input = r#" +# A +Foo + "# + .trim_matches('\n') + .trim(); + + // when + let tree = TokenTreeFactory::create(input); + + // then + println!("{:#?}", tree); + assert_eq!(tree.title, None); + let first_child = tree.children.first().unwrap(); + assert_eq!(first_child.title, Some("A".into())); + } + + #[test] + fn complex() { + // given + let input = r#" +# A +Foo +## B +# C +## D +Bar + "# + .trim_matches('\n') + .trim(); + + // when + let tree = TokenTreeFactory::create(input); + + // then + println!("{:#?}", tree); + assert_eq!(tree.title, None); + assert_eq!(tree.children.len(), 2); + + let first = tree.children.get(0).unwrap(); + assert_eq!(first.title, Some("A".into())); + assert_eq!(first.children.len(), 1); + assert_eq!(first.children.first().unwrap().title, Some("B".into())); + + let second = tree.children.get(1).unwrap(); + assert_eq!(second.title, Some("C".into())); + assert_eq!(second.children.len(), 1); + assert_eq!(second.children.first().unwrap().title, Some("D".into())); + } +} diff --git a/parser/Cargo.lock b/parser/Cargo.lock new file mode 100644 index 0000000..c9664af --- /dev/null +++ b/parser/Cargo.lock @@ -0,0 +1,54 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +dependencies = [ + "memchr", +] + +[[package]] +name = "case" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd6c0e7b807d60291f42f33f58480c0bfafe28ed08286446f45e463728cf9c1c" + +[[package]] +name = "md-parser" +version = "0.1.0" + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "parser" +version = "0.1.0" +dependencies = [ + "case", + "md-parser", + "regex", +] + +[[package]] +name = "regex" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" diff --git a/parser/Cargo.toml b/parser/Cargo.toml new file mode 100644 index 0000000..85fed80 --- /dev/null +++ b/parser/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "parser" +version = "0.1.0" +edition = "2021" + +[dependencies] +md-parser = { path = "../md-parser" } +case = "1.0.0" +regex = "1.6.0" diff --git a/parser/groups.txt b/parser/groups.txt new file mode 100644 index 0000000..96d8e85 --- /dev/null +++ b/parser/groups.txt @@ -0,0 +1,5696 @@ +[ + ApiGroup { + name: "authentication", + methods: [ + ApiMethod { + name: "login", + description: Some( + "Example showing how to login and execute a command that requires authentication using `curl`:\n\n```sh\n$ curl -i --header 'Referer: http://localhost:8080' --data 'username=admin&password=adminadmin' http://localhost:8080/api/v2/auth/login\nHTTP/1.1 200 OK\nContent-Encoding:\nContent-Length: 3\nContent-Type: text/plain; charset=UTF-8\nSet-Cookie: SID=hBc7TxF76ERhvIw0jQQ4LZ7Z1jQUV0tQ; path=/\n$ curl http://localhost:8080/api/v2/torrents/info --cookie \"SID=hBc7TxF76ERhvIw0jQQ4LZ7Z1jQUV0tQ\"\n```\n\nNote: Set `Referer` or `Origin` header to the exact same domain and port as used in the HTTP query `Host` header.", + ), + parameters: Some( + [ + String( + TypeInfo { + name: "username", + is_optional: false, + is_list: false, + description: Some( + "Username used to access the WebUI", + ), + type_description: None, + }, + ), + String( + TypeInfo { + name: "password", + is_optional: false, + is_list: false, + description: Some( + "Password used to access the WebUI", + ), + type_description: None, + }, + ), + ], + ), + return_type: None, + url: "login", + }, + ApiMethod { + name: "logout", + description: None, + parameters: None, + return_type: None, + url: "logout", + }, + ], + description: Some( + "All Authentication API methods are under \"auth\", e.g.: `/api/v2/auth/methodName`.\n\nqBittorrent uses cookie-based authentication.", + ), + url: "auth", + }, + ApiGroup { + name: "application", + methods: [ + ApiMethod { + name: "version", + description: None, + parameters: None, + return_type: None, + url: "version", + }, + ApiMethod { + name: "webapiVersion", + description: None, + parameters: None, + return_type: None, + url: "webapiVersion", + }, + ApiMethod { + name: "buildInfo", + description: None, + parameters: None, + return_type: Some( + ReturnType { + is_list: false, + parameters: [ + ReturnTypeParameter { + name: "qt", + description: "QT version", + return_type: String( + TypeInfo { + name: "qt", + is_optional: false, + is_list: false, + description: Some( + "QT version", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "libtorrent", + description: "libtorrent version", + return_type: String( + TypeInfo { + name: "libtorrent", + is_optional: false, + is_list: false, + description: Some( + "libtorrent version", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "boost", + description: "Boost version", + return_type: String( + TypeInfo { + name: "boost", + is_optional: false, + is_list: false, + description: Some( + "Boost version", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "openssl", + description: "OpenSSL version", + return_type: String( + TypeInfo { + name: "openssl", + is_optional: false, + is_list: false, + description: Some( + "OpenSSL version", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "bitness", + description: "Application bitness (e.g. 64-bit)", + return_type: Number( + TypeInfo { + name: "bitness", + is_optional: false, + is_list: false, + description: Some( + "Application bitness (e.g. 64-bit)", + ), + type_description: None, + }, + ), + }, + ], + }, + ), + url: "buildInfo", + }, + ApiMethod { + name: "shutdown", + description: None, + parameters: None, + return_type: None, + url: "shutdown", + }, + ApiMethod { + name: "preferences", + description: Some( + "Possible fields:\n\nPossible values of `scan_dirs`:\n\nPossible values of `scheduler_days`:\n\nPossible values of `encryption`:\n\nNB: the first options allows you to use both encrypted and unencrypted connections (this is the default); other options are mutually exclusive: e.g. by forcing encryption on you won't be able to use unencrypted connections and vice versa.\n\nPossible values of `proxy_type`:\n\nPossible values of `dyndns_service`:\n\nPossible values of `max_ratio_act`:\n\nPossible values of `bittorrent_protocol`:\n\nPossible values of `upload_choking_algorithm`:\n\nPossible values of `upload_slots_behavior`:\n\nPossible values of `utp_tcp_mixed_mode`:\n\nExample:\n\n```JSON\n{\n \"add_trackers\": \"\",\n \"add_trackers_enabled\": false,\n \"alt_dl_limit\": 10240,\n \"alt_up_limit\": 10240,\n \"alternative_webui_enabled\": false,\n \"alternative_webui_path\": \"/home/user/Documents/qbit-webui\",\n \"announce_ip\": \"\",\n \"announce_to_all_tiers\": true,\n \"announce_to_all_trackers\": false,\n \"anonymous_mode\": false,\n \"async_io_threads\": 4,\n \"auto_delete_mode\": 0,\n \"auto_tmm_enabled\": false,\n \"autorun_enabled\": false,\n \"autorun_program\": \"\",\n \"banned_IPs\": \"\",\n \"bittorrent_protocol\": 0,\n \"bypass_auth_subnet_whitelist\": \"\",\n \"bypass_auth_subnet_whitelist_enabled\": false,\n \"bypass_local_auth\": false,\n \"category_changed_tmm_enabled\": false,\n \"checking_memory_use\": 32,\n \"create_subfolder_enabled\": true,\n \"current_interface_address\": \"\",\n \"current_network_interface\": \"\",\n \"dht\": true,\n \"disk_cache\": -1,\n \"disk_cache_ttl\": 60,\n \"dl_limit\": 0,\n \"dont_count_slow_torrents\": false,\n \"dyndns_domain\": \"changeme.dyndns.org\",\n \"dyndns_enabled\": false,\n \"dyndns_password\": \"\",\n \"dyndns_service\": 0,\n \"dyndns_username\": \"\",\n \"embedded_tracker_port\": 9000,\n \"enable_coalesce_read_write\": false,\n \"enable_embedded_tracker\": false,\n \"enable_multi_connections_from_same_ip\": false,\n \"enable_os_cache\": true,\n \"enable_piece_extent_affinity\": false,\n \"enable_upload_suggestions\": false,\n \"encryption\": 0,\n \"export_dir\": \"/home/user/Downloads/all\",\n \"export_dir_fin\": \"/home/user/Downloads/completed\",\n \"file_pool_size\": 40,\n \"incomplete_files_ext\": false,\n \"ip_filter_enabled\": false,\n \"ip_filter_path\": \"\",\n \"ip_filter_trackers\": false,\n \"limit_lan_peers\": true,\n \"limit_tcp_overhead\": false,\n \"limit_utp_rate\": true,\n \"listen_port\": 58925,\n \"locale\": \"en\",\n \"lsd\": true,\n \"mail_notification_auth_enabled\": false,\n \"mail_notification_email\": \"\",\n \"mail_notification_enabled\": false,\n \"mail_notification_password\": \"\",\n \"mail_notification_sender\": \"qBittorrent_notification@example.com\",\n \"mail_notification_smtp\": \"smtp.changeme.com\",\n \"mail_notification_ssl_enabled\": false,\n \"mail_notification_username\": \"\",\n \"max_active_downloads\": 3,\n \"max_active_torrents\": 5,\n \"max_active_uploads\": 3,\n \"max_connec\": 500,\n \"max_connec_per_torrent\": 100,\n \"max_ratio\": -1,\n \"max_ratio_act\": 0,\n \"max_ratio_enabled\": false,\n \"max_seeding_time\": -1,\n \"max_seeding_time_enabled\": false,\n \"max_uploads\": -1,\n \"max_uploads_per_torrent\": -1,\n \"outgoing_ports_max\": 0,\n \"outgoing_ports_min\": 0,\n \"pex\": true,\n \"preallocate_all\": false,\n \"proxy_auth_enabled\": false,\n \"proxy_ip\": \"0.0.0.0\",\n \"proxy_password\": \"\",\n \"proxy_peer_connections\": false,\n \"proxy_port\": 8080,\n \"proxy_torrents_only\": false,\n \"proxy_type\": 0,\n \"proxy_username\": \"\",\n \"queueing_enabled\": false,\n \"random_port\": false,\n \"recheck_completed_torrents\": false,\n \"resolve_peer_countries\": true,\n \"rss_auto_downloading_enabled\":true,\n \"rss_download_repack_proper_episodes\":true,\n \"rss_max_articles_per_feed\":50,\n \"rss_processing_enabled\":true,\n \"rss_refresh_interval\":30,\n \"rss_smart_episode_filters\":\"s(\\\\d+)e(\\\\d+)\\n(\\\\d+)x(\\\\d+)\\n(\\\\d{4}[.\\\\-]\\\\d{1,2}[.\\\\-]\\\\d{1,2})\",\n \"save_path\": \"/home/user/Downloads/\",\n \"save_path_changed_tmm_enabled\": false,\n \"save_resume_data_interval\": 60,\n \"scan_dirs\":\n {\n \"/home/user/Downloads/incoming/games\": 0,\n \"/home/user/Downloads/incoming/movies\": 1,\n },\n \"schedule_from_hour\": 8,\n \"schedule_from_min\": 0,\n \"schedule_to_hour\": 20,\n \"schedule_to_min\": 0,\n \"scheduler_days\": 0,\n \"scheduler_enabled\": false,\n \"send_buffer_low_watermark\": 10,\n \"send_buffer_watermark\": 500,\n \"send_buffer_watermark_factor\": 50,\n \"slow_torrent_dl_rate_threshold\": 2,\n \"slow_torrent_inactive_timer\": 60,\n \"slow_torrent_ul_rate_threshold\": 2,\n \"socket_backlog_size\": 30,\n \"start_paused_enabled\": false,\n \"stop_tracker_timeout\": 1,\n \"temp_path\": \"/home/user/Downloads/temp\",\n \"temp_path_enabled\": false,\n \"torrent_changed_tmm_enabled\": true,\n \"up_limit\": 0,\n \"upload_choking_algorithm\": 1,\n \"upload_slots_behavior\": 0,\n \"upnp\": true,\n \"use_https\": false,\n \"utp_tcp_mixed_mode\": 0,\n \"web_ui_address\": \"*\",\n \"web_ui_ban_duration\": 3600,\n \"web_ui_clickjacking_protection_enabled\": true,\n \"web_ui_csrf_protection_enabled\": true,\n \"web_ui_custom_http_headers\": \"\",\n \"web_ui_domain_list\": \"*\",\n \"web_ui_host_header_validation_enabled\": true,\n \"web_ui_https_cert_path\": \"\",\n \"web_ui_https_key_path\": \"\",\n \"web_ui_max_auth_fail_count\": 5,\n \"web_ui_port\": 8080,\n \"web_ui_secure_cookie_enabled\": true,\n \"web_ui_session_timeout\": 3600,\n \"web_ui_upnp\": false,\n \"web_ui_use_custom_http_headers_enabled\": false,\n \"web_ui_username\": \"admin\"\n}\n```", + ), + parameters: None, + return_type: Some( + ReturnType { + is_list: false, + parameters: [ + ReturnTypeParameter { + name: "locale", + description: "Currently selected language (e.g. en_GB for English)", + return_type: String( + TypeInfo { + name: "locale", + is_optional: false, + is_list: false, + description: Some( + "Currently selected language (e.g. en_GB for English)", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "create_subfolder_enabled", + description: "True if a subfolder should be created when adding a torrent", + return_type: Bool( + TypeInfo { + name: "create_subfolder_enabled", + is_optional: false, + is_list: false, + description: Some( + "True if a subfolder should be created when adding a torrent", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "start_paused_enabled", + description: "True if torrents should be added in a Paused state", + return_type: Bool( + TypeInfo { + name: "start_paused_enabled", + is_optional: false, + is_list: false, + description: Some( + "True if torrents should be added in a Paused state", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "auto_delete_mode", + description: "TODO", + return_type: Number( + TypeInfo { + name: "auto_delete_mode", + is_optional: false, + is_list: false, + description: Some( + "TODO", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "preallocate_all", + description: "True if disk space should be pre-allocated for all files", + return_type: Bool( + TypeInfo { + name: "preallocate_all", + is_optional: false, + is_list: false, + description: Some( + "True if disk space should be pre-allocated for all files", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "incomplete_files_ext", + description: "True if \".!qB\" should be appended to incomplete files", + return_type: Bool( + TypeInfo { + name: "incomplete_files_ext", + is_optional: false, + is_list: false, + description: Some( + "True if \".!qB\" should be appended to incomplete files", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "auto_tmm_enabled", + description: "True if Automatic Torrent Management is enabled by default", + return_type: Bool( + TypeInfo { + name: "auto_tmm_enabled", + is_optional: false, + is_list: false, + description: Some( + "True if Automatic Torrent Management is enabled by default", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "torrent_changed_tmm_enabled", + description: "True if torrent should be relocated when its Category changes", + return_type: Bool( + TypeInfo { + name: "torrent_changed_tmm_enabled", + is_optional: false, + is_list: false, + description: Some( + "True if torrent should be relocated when its Category changes", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "save_path_changed_tmm_enabled", + description: "True if torrent should be relocated when the default save path changes", + return_type: Bool( + TypeInfo { + name: "save_path_changed_tmm_enabled", + is_optional: false, + is_list: false, + description: Some( + "True if torrent should be relocated when the default save path changes", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "category_changed_tmm_enabled", + description: "True if torrent should be relocated when its Category's save path changes", + return_type: Bool( + TypeInfo { + name: "category_changed_tmm_enabled", + is_optional: false, + is_list: false, + description: Some( + "True if torrent should be relocated when its Category's save path changes", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "save_path", + description: "Default save path for torrents, separated by slashes", + return_type: String( + TypeInfo { + name: "save_path", + is_optional: false, + is_list: false, + description: Some( + "Default save path for torrents, separated by slashes", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "temp_path_enabled", + description: "True if folder for incomplete torrents is enabled", + return_type: Bool( + TypeInfo { + name: "temp_path_enabled", + is_optional: false, + is_list: false, + description: Some( + "True if folder for incomplete torrents is enabled", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "temp_path", + description: "Path for incomplete torrents, separated by slashes", + return_type: String( + TypeInfo { + name: "temp_path", + is_optional: false, + is_list: false, + description: Some( + "Path for incomplete torrents, separated by slashes", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "scan_dirs", + description: "Property: directory to watch for torrent files, value: where torrents loaded from this directory should be downloaded to (see list of possible values below). Slashes are used as path separators; multiple key/value pairs can be specified", + return_type: Object( + TypeInfo { + name: "scan_dirs", + is_optional: false, + is_list: false, + description: Some( + "Property: directory to watch for torrent files, value: where torrents loaded from this directory should be downloaded to (see list of possible values below). Slashes are used as path separators; multiple key/value pairs can be specified", + ), + type_description: Some( + TypeDescription { + values: [ + TypeDescriptions { + value: "0", + description: "Download to the monitored folder", + }, + TypeDescriptions { + value: "1", + description: "Download to the default save path", + }, + TypeDescriptions { + value: "\"/path/to/download/to\"", + description: "Download to this path", + }, + ], + }, + ), + }, + ), + }, + ReturnTypeParameter { + name: "export_dir", + description: "Path to directory to copy .torrent files to. Slashes are used as path separators", + return_type: String( + TypeInfo { + name: "export_dir", + is_optional: false, + is_list: false, + description: Some( + "Path to directory to copy .torrent files to. Slashes are used as path separators", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "export_dir_fin", + description: "Path to directory to copy .torrent files of completed downloads to. Slashes are used as path separators", + return_type: String( + TypeInfo { + name: "export_dir_fin", + is_optional: false, + is_list: false, + description: Some( + "Path to directory to copy .torrent files of completed downloads to. Slashes are used as path separators", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "mail_notification_enabled", + description: "True if e-mail notification should be enabled", + return_type: Bool( + TypeInfo { + name: "mail_notification_enabled", + is_optional: false, + is_list: false, + description: Some( + "True if e-mail notification should be enabled", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "mail_notification_sender", + description: "e-mail where notifications should originate from", + return_type: String( + TypeInfo { + name: "mail_notification_sender", + is_optional: false, + is_list: false, + description: Some( + "e-mail where notifications should originate from", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "mail_notification_email", + description: "e-mail to send notifications to", + return_type: String( + TypeInfo { + name: "mail_notification_email", + is_optional: false, + is_list: false, + description: Some( + "e-mail to send notifications to", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "mail_notification_smtp", + description: "smtp server for e-mail notifications", + return_type: String( + TypeInfo { + name: "mail_notification_smtp", + is_optional: false, + is_list: false, + description: Some( + "smtp server for e-mail notifications", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "mail_notification_ssl_enabled", + description: "True if smtp server requires SSL connection", + return_type: Bool( + TypeInfo { + name: "mail_notification_ssl_enabled", + is_optional: false, + is_list: false, + description: Some( + "True if smtp server requires SSL connection", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "mail_notification_auth_enabled", + description: "True if smtp server requires authentication", + return_type: Bool( + TypeInfo { + name: "mail_notification_auth_enabled", + is_optional: false, + is_list: false, + description: Some( + "True if smtp server requires authentication", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "mail_notification_username", + description: "Username for smtp authentication", + return_type: String( + TypeInfo { + name: "mail_notification_username", + is_optional: false, + is_list: false, + description: Some( + "Username for smtp authentication", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "mail_notification_password", + description: "Password for smtp authentication", + return_type: String( + TypeInfo { + name: "mail_notification_password", + is_optional: false, + is_list: false, + description: Some( + "Password for smtp authentication", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "autorun_enabled", + description: "True if external program should be run after torrent has finished downloading", + return_type: Bool( + TypeInfo { + name: "autorun_enabled", + is_optional: false, + is_list: false, + description: Some( + "True if external program should be run after torrent has finished downloading", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "autorun_program", + description: "Program path/name/arguments to run if autorun_enabled is enabled; path is separated by slashes; you can use %f and %n arguments, which will be expanded by qBittorent as path_to_torrent_file and torrent_name (from the GUI; not the .torrent file name) respectively", + return_type: String( + TypeInfo { + name: "autorun_program", + is_optional: false, + is_list: false, + description: Some( + "Program path/name/arguments to run if autorun_enabled is enabled; path is separated by slashes; you can use %f and %n arguments, which will be expanded by qBittorent as path_to_torrent_file and torrent_name (from the GUI; not the .torrent file name) respectively", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "queueing_enabled", + description: "True if torrent queuing is enabled", + return_type: Bool( + TypeInfo { + name: "queueing_enabled", + is_optional: false, + is_list: false, + description: Some( + "True if torrent queuing is enabled", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "max_active_downloads", + description: "Maximum number of active simultaneous downloads", + return_type: Number( + TypeInfo { + name: "max_active_downloads", + is_optional: false, + is_list: false, + description: Some( + "Maximum number of active simultaneous downloads", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "max_active_torrents", + description: "Maximum number of active simultaneous downloads and uploads", + return_type: Number( + TypeInfo { + name: "max_active_torrents", + is_optional: false, + is_list: false, + description: Some( + "Maximum number of active simultaneous downloads and uploads", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "max_active_uploads", + description: "Maximum number of active simultaneous uploads", + return_type: Number( + TypeInfo { + name: "max_active_uploads", + is_optional: false, + is_list: false, + description: Some( + "Maximum number of active simultaneous uploads", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "dont_count_slow_torrents", + description: "If true torrents w/o any activity (stalled ones) will not be counted towards max_active_* limits; see [dont_count_slow_torrents](https://www.libtorrent.org/reference-Settings.html#dont_count_slow_torrents) for more information", + return_type: Bool( + TypeInfo { + name: "dont_count_slow_torrents", + is_optional: false, + is_list: false, + description: Some( + "If true torrents w/o any activity (stalled ones) will not be counted towards max_active_* limits; see [dont_count_slow_torrents](https://www.libtorrent.org/reference-Settings.html#dont_count_slow_torrents) for more information", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "slow_torrent_dl_rate_threshold", + description: "Download rate in KiB/s for a torrent to be considered \"slow\"", + return_type: Number( + TypeInfo { + name: "slow_torrent_dl_rate_threshold", + is_optional: false, + is_list: false, + description: Some( + "Download rate in KiB/s for a torrent to be considered \"slow\"", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "slow_torrent_ul_rate_threshold", + description: "Upload rate in KiB/s for a torrent to be considered \"slow\"", + return_type: Number( + TypeInfo { + name: "slow_torrent_ul_rate_threshold", + is_optional: false, + is_list: false, + description: Some( + "Upload rate in KiB/s for a torrent to be considered \"slow\"", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "slow_torrent_inactive_timer", + description: "Seconds a torrent should be inactive before considered \"slow\"", + return_type: Number( + TypeInfo { + name: "slow_torrent_inactive_timer", + is_optional: false, + is_list: false, + description: Some( + "Seconds a torrent should be inactive before considered \"slow\"", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "max_ratio_enabled", + description: "True if share ratio limit is enabled", + return_type: Bool( + TypeInfo { + name: "max_ratio_enabled", + is_optional: false, + is_list: false, + description: Some( + "True if share ratio limit is enabled", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "max_ratio", + description: "Get the global share ratio limit", + return_type: Float( + TypeInfo { + name: "max_ratio", + is_optional: false, + is_list: false, + description: Some( + "Get the global share ratio limit", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "max_ratio_act", + description: "Action performed when a torrent reaches the maximum share ratio. See list of possible values here below.", + return_type: Number( + TypeInfo { + name: "max_ratio_act", + is_optional: false, + is_list: false, + description: Some( + "Action performed when a torrent reaches the maximum share ratio. See list of possible values here below.", + ), + type_description: Some( + TypeDescription { + values: [ + TypeDescriptions { + value: "0", + description: "Pause torrent", + }, + TypeDescriptions { + value: "1", + description: "Remove torrent", + }, + ], + }, + ), + }, + ), + }, + ReturnTypeParameter { + name: "listen_port", + description: "Port for incoming connections", + return_type: Number( + TypeInfo { + name: "listen_port", + is_optional: false, + is_list: false, + description: Some( + "Port for incoming connections", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "upnp", + description: "True if UPnP/NAT-PMP is enabled", + return_type: Bool( + TypeInfo { + name: "upnp", + is_optional: false, + is_list: false, + description: Some( + "True if UPnP/NAT-PMP is enabled", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "random_port", + description: "True if the port is randomly selected", + return_type: Bool( + TypeInfo { + name: "random_port", + is_optional: false, + is_list: false, + description: Some( + "True if the port is randomly selected", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "dl_limit", + description: "Global download speed limit in KiB/s; -1 means no limit is applied", + return_type: Number( + TypeInfo { + name: "dl_limit", + is_optional: false, + is_list: false, + description: Some( + "Global download speed limit in KiB/s; -1 means no limit is applied", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "up_limit", + description: "Global upload speed limit in KiB/s; -1 means no limit is applied", + return_type: Number( + TypeInfo { + name: "up_limit", + is_optional: false, + is_list: false, + description: Some( + "Global upload speed limit in KiB/s; -1 means no limit is applied", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "max_connec", + description: "Maximum global number of simultaneous connections", + return_type: Number( + TypeInfo { + name: "max_connec", + is_optional: false, + is_list: false, + description: Some( + "Maximum global number of simultaneous connections", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "max_connec_per_torrent", + description: "Maximum number of simultaneous connections per torrent", + return_type: Number( + TypeInfo { + name: "max_connec_per_torrent", + is_optional: false, + is_list: false, + description: Some( + "Maximum number of simultaneous connections per torrent", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "max_uploads", + description: "Maximum number of upload slots", + return_type: Number( + TypeInfo { + name: "max_uploads", + is_optional: false, + is_list: false, + description: Some( + "Maximum number of upload slots", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "max_uploads_per_torrent", + description: "Maximum number of upload slots per torrent", + return_type: Number( + TypeInfo { + name: "max_uploads_per_torrent", + is_optional: false, + is_list: false, + description: Some( + "Maximum number of upload slots per torrent", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "stop_tracker_timeout", + description: "Timeout in seconds for a stopped announce request to trackers", + return_type: Number( + TypeInfo { + name: "stop_tracker_timeout", + is_optional: false, + is_list: false, + description: Some( + "Timeout in seconds for a stopped announce request to trackers", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "enable_piece_extent_affinity", + description: "True if the advanced libtorrent option piece_extent_affinity is enabled", + return_type: Bool( + TypeInfo { + name: "enable_piece_extent_affinity", + is_optional: false, + is_list: false, + description: Some( + "True if the advanced libtorrent option piece_extent_affinity is enabled", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "bittorrent_protocol", + description: "Bittorrent Protocol to use (see list of possible values below)", + return_type: Number( + TypeInfo { + name: "bittorrent_protocol", + is_optional: false, + is_list: false, + description: Some( + "Bittorrent Protocol to use (see list of possible values below)", + ), + type_description: Some( + TypeDescription { + values: [ + TypeDescriptions { + value: "0", + description: "TCP and μTP", + }, + TypeDescriptions { + value: "1", + description: "TCP", + }, + TypeDescriptions { + value: "2", + description: "μTP", + }, + ], + }, + ), + }, + ), + }, + ReturnTypeParameter { + name: "limit_utp_rate", + description: "True if [du]l_limit should be applied to uTP connections; this option is only available in qBittorent built against libtorrent version 0.16.X and higher", + return_type: Bool( + TypeInfo { + name: "limit_utp_rate", + is_optional: false, + is_list: false, + description: Some( + "True if [du]l_limit should be applied to uTP connections; this option is only available in qBittorent built against libtorrent version 0.16.X and higher", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "limit_tcp_overhead", + description: "True if [du]l_limit should be applied to estimated TCP overhead (service data: e.g. packet headers)", + return_type: Bool( + TypeInfo { + name: "limit_tcp_overhead", + is_optional: false, + is_list: false, + description: Some( + "True if [du]l_limit should be applied to estimated TCP overhead (service data: e.g. packet headers)", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "limit_lan_peers", + description: "True if [du]l_limit should be applied to peers on the LAN", + return_type: Bool( + TypeInfo { + name: "limit_lan_peers", + is_optional: false, + is_list: false, + description: Some( + "True if [du]l_limit should be applied to peers on the LAN", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "alt_dl_limit", + description: "Alternative global download speed limit in KiB/s", + return_type: Number( + TypeInfo { + name: "alt_dl_limit", + is_optional: false, + is_list: false, + description: Some( + "Alternative global download speed limit in KiB/s", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "alt_up_limit", + description: "Alternative global upload speed limit in KiB/s", + return_type: Number( + TypeInfo { + name: "alt_up_limit", + is_optional: false, + is_list: false, + description: Some( + "Alternative global upload speed limit in KiB/s", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "scheduler_enabled", + description: "True if alternative limits should be applied according to schedule", + return_type: Bool( + TypeInfo { + name: "scheduler_enabled", + is_optional: false, + is_list: false, + description: Some( + "True if alternative limits should be applied according to schedule", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "schedule_from_hour", + description: "Scheduler starting hour", + return_type: Number( + TypeInfo { + name: "schedule_from_hour", + is_optional: false, + is_list: false, + description: Some( + "Scheduler starting hour", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "schedule_from_min", + description: "Scheduler starting minute", + return_type: Number( + TypeInfo { + name: "schedule_from_min", + is_optional: false, + is_list: false, + description: Some( + "Scheduler starting minute", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "schedule_to_hour", + description: "Scheduler ending hour", + return_type: Number( + TypeInfo { + name: "schedule_to_hour", + is_optional: false, + is_list: false, + description: Some( + "Scheduler ending hour", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "schedule_to_min", + description: "Scheduler ending minute", + return_type: Number( + TypeInfo { + name: "schedule_to_min", + is_optional: false, + is_list: false, + description: Some( + "Scheduler ending minute", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "scheduler_days", + description: "Scheduler days. See possible values here below", + return_type: Number( + TypeInfo { + name: "scheduler_days", + is_optional: false, + is_list: false, + description: Some( + "Scheduler days. See possible values here below", + ), + type_description: Some( + TypeDescription { + values: [ + TypeDescriptions { + value: "0", + description: "Every day", + }, + TypeDescriptions { + value: "1", + description: "Every weekday", + }, + TypeDescriptions { + value: "2", + description: "Every weekend", + }, + TypeDescriptions { + value: "3", + description: "Every Monday", + }, + TypeDescriptions { + value: "4", + description: "Every Tuesday", + }, + TypeDescriptions { + value: "5", + description: "Every Wednesday", + }, + TypeDescriptions { + value: "6", + description: "Every Thursday", + }, + TypeDescriptions { + value: "7", + description: "Every Friday", + }, + TypeDescriptions { + value: "8", + description: "Every Saturday", + }, + TypeDescriptions { + value: "9", + description: "Every Sunday", + }, + ], + }, + ), + }, + ), + }, + ReturnTypeParameter { + name: "dht", + description: "True if DHT is enabled", + return_type: Bool( + TypeInfo { + name: "dht", + is_optional: false, + is_list: false, + description: Some( + "True if DHT is enabled", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "pex", + description: "True if PeX is enabled", + return_type: Bool( + TypeInfo { + name: "pex", + is_optional: false, + is_list: false, + description: Some( + "True if PeX is enabled", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "lsd", + description: "True if LSD is enabled", + return_type: Bool( + TypeInfo { + name: "lsd", + is_optional: false, + is_list: false, + description: Some( + "True if LSD is enabled", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "encryption", + description: "See list of possible values here below", + return_type: Number( + TypeInfo { + name: "encryption", + is_optional: false, + is_list: false, + description: Some( + "See list of possible values here below", + ), + type_description: Some( + TypeDescription { + values: [ + TypeDescriptions { + value: "0", + description: "Prefer encryption", + }, + TypeDescriptions { + value: "1", + description: "Force encryption on", + }, + TypeDescriptions { + value: "2", + description: "Force encryption off", + }, + ], + }, + ), + }, + ), + }, + ReturnTypeParameter { + name: "anonymous_mode", + description: "If true anonymous mode will be enabled; read more [here](Anonymous-Mode); this option is only available in qBittorent built against libtorrent version 0.16.X and higher", + return_type: Bool( + TypeInfo { + name: "anonymous_mode", + is_optional: false, + is_list: false, + description: Some( + "If true anonymous mode will be enabled; read more [here](Anonymous-Mode); this option is only available in qBittorent built against libtorrent version 0.16.X and higher", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "proxy_type", + description: "See list of possible values here below", + return_type: Number( + TypeInfo { + name: "proxy_type", + is_optional: false, + is_list: false, + description: Some( + "See list of possible values here below", + ), + type_description: Some( + TypeDescription { + values: [ + TypeDescriptions { + value: "-1", + description: "Proxy is disabled", + }, + TypeDescriptions { + value: "1", + description: "HTTP proxy without authentication", + }, + TypeDescriptions { + value: "2", + description: "SOCKS5 proxy without authentication", + }, + TypeDescriptions { + value: "3", + description: "HTTP proxy with authentication", + }, + TypeDescriptions { + value: "4", + description: "SOCKS5 proxy with authentication", + }, + TypeDescriptions { + value: "5", + description: "SOCKS4 proxy without authentication", + }, + ], + }, + ), + }, + ), + }, + ReturnTypeParameter { + name: "proxy_ip", + description: "Proxy IP address or domain name", + return_type: String( + TypeInfo { + name: "proxy_ip", + is_optional: false, + is_list: false, + description: Some( + "Proxy IP address or domain name", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "proxy_port", + description: "Proxy port", + return_type: Number( + TypeInfo { + name: "proxy_port", + is_optional: false, + is_list: false, + description: Some( + "Proxy port", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "proxy_peer_connections", + description: "True if peer and web seed connections should be proxified; this option will have any effect only in qBittorent built against libtorrent version 0.16.X and higher", + return_type: Bool( + TypeInfo { + name: "proxy_peer_connections", + is_optional: false, + is_list: false, + description: Some( + "True if peer and web seed connections should be proxified; this option will have any effect only in qBittorent built against libtorrent version 0.16.X and higher", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "proxy_auth_enabled", + description: "True proxy requires authentication; doesn't apply to SOCKS4 proxies", + return_type: Bool( + TypeInfo { + name: "proxy_auth_enabled", + is_optional: false, + is_list: false, + description: Some( + "True proxy requires authentication; doesn't apply to SOCKS4 proxies", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "proxy_username", + description: "Username for proxy authentication", + return_type: String( + TypeInfo { + name: "proxy_username", + is_optional: false, + is_list: false, + description: Some( + "Username for proxy authentication", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "proxy_password", + description: "Password for proxy authentication", + return_type: String( + TypeInfo { + name: "proxy_password", + is_optional: false, + is_list: false, + description: Some( + "Password for proxy authentication", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "proxy_torrents_only", + description: "True if proxy is only used for torrents", + return_type: Bool( + TypeInfo { + name: "proxy_torrents_only", + is_optional: false, + is_list: false, + description: Some( + "True if proxy is only used for torrents", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "ip_filter_enabled", + description: "True if external IP filter should be enabled", + return_type: Bool( + TypeInfo { + name: "ip_filter_enabled", + is_optional: false, + is_list: false, + description: Some( + "True if external IP filter should be enabled", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "ip_filter_path", + description: "Path to IP filter file (.dat, .p2p, .p2b files are supported); path is separated by slashes", + return_type: String( + TypeInfo { + name: "ip_filter_path", + is_optional: false, + is_list: false, + description: Some( + "Path to IP filter file (.dat, .p2p, .p2b files are supported); path is separated by slashes", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "ip_filter_trackers", + description: "True if IP filters are applied to trackers", + return_type: Bool( + TypeInfo { + name: "ip_filter_trackers", + is_optional: false, + is_list: false, + description: Some( + "True if IP filters are applied to trackers", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "web_ui_domain_list", + description: "Comma-separated list of domains to accept when performing Host header validation", + return_type: String( + TypeInfo { + name: "web_ui_domain_list", + is_optional: false, + is_list: false, + description: Some( + "Comma-separated list of domains to accept when performing Host header validation", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "web_ui_address", + description: "IP address to use for the WebUI", + return_type: String( + TypeInfo { + name: "web_ui_address", + is_optional: false, + is_list: false, + description: Some( + "IP address to use for the WebUI", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "web_ui_port", + description: "WebUI port", + return_type: Number( + TypeInfo { + name: "web_ui_port", + is_optional: false, + is_list: false, + description: Some( + "WebUI port", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "web_ui_upnp", + description: "True if UPnP is used for the WebUI port", + return_type: Bool( + TypeInfo { + name: "web_ui_upnp", + is_optional: false, + is_list: false, + description: Some( + "True if UPnP is used for the WebUI port", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "web_ui_username", + description: "WebUI username", + return_type: String( + TypeInfo { + name: "web_ui_username", + is_optional: false, + is_list: false, + description: Some( + "WebUI username", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "web_ui_password", + description: "For API ≥ v2.3.0: Plaintext WebUI password, not readable, write-only. For API < v2.3.0: MD5 hash of WebUI password, hash is generated from the following string: username:Web UI Access:plain_text_web_ui_password", + return_type: String( + TypeInfo { + name: "web_ui_password", + is_optional: false, + is_list: false, + description: Some( + "For API ≥ v2.3.0: Plaintext WebUI password, not readable, write-only. For API < v2.3.0: MD5 hash of WebUI password, hash is generated from the following string: username:Web UI Access:plain_text_web_ui_password", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "web_ui_csrf_protection_enabled", + description: "True if WebUI CSRF protection is enabled", + return_type: Bool( + TypeInfo { + name: "web_ui_csrf_protection_enabled", + is_optional: false, + is_list: false, + description: Some( + "True if WebUI CSRF protection is enabled", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "web_ui_clickjacking_protection_enabled", + description: "True if WebUI clickjacking protection is enabled", + return_type: Bool( + TypeInfo { + name: "web_ui_clickjacking_protection_enabled", + is_optional: false, + is_list: false, + description: Some( + "True if WebUI clickjacking protection is enabled", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "web_ui_secure_cookie_enabled", + description: "True if WebUI cookie Secure flag is enabled", + return_type: Bool( + TypeInfo { + name: "web_ui_secure_cookie_enabled", + is_optional: false, + is_list: false, + description: Some( + "True if WebUI cookie Secure flag is enabled", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "web_ui_max_auth_fail_count", + description: "Maximum number of authentication failures before WebUI access ban", + return_type: Number( + TypeInfo { + name: "web_ui_max_auth_fail_count", + is_optional: false, + is_list: false, + description: Some( + "Maximum number of authentication failures before WebUI access ban", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "web_ui_ban_duration", + description: "WebUI access ban duration in seconds", + return_type: Number( + TypeInfo { + name: "web_ui_ban_duration", + is_optional: false, + is_list: false, + description: Some( + "WebUI access ban duration in seconds", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "web_ui_session_timeout", + description: "Seconds until WebUI is automatically signed off", + return_type: Number( + TypeInfo { + name: "web_ui_session_timeout", + is_optional: false, + is_list: false, + description: Some( + "Seconds until WebUI is automatically signed off", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "web_ui_host_header_validation_enabled", + description: "True if WebUI host header validation is enabled", + return_type: Bool( + TypeInfo { + name: "web_ui_host_header_validation_enabled", + is_optional: false, + is_list: false, + description: Some( + "True if WebUI host header validation is enabled", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "bypass_local_auth", + description: "True if authentication challenge for loopback address (127.0.0.1) should be disabled", + return_type: Bool( + TypeInfo { + name: "bypass_local_auth", + is_optional: false, + is_list: false, + description: Some( + "True if authentication challenge for loopback address (127.0.0.1) should be disabled", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "bypass_auth_subnet_whitelist_enabled", + description: "True if webui authentication should be bypassed for clients whose ip resides within (at least) one of the subnets on the whitelist", + return_type: Bool( + TypeInfo { + name: "bypass_auth_subnet_whitelist_enabled", + is_optional: false, + is_list: false, + description: Some( + "True if webui authentication should be bypassed for clients whose ip resides within (at least) one of the subnets on the whitelist", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "bypass_auth_subnet_whitelist", + description: "(White)list of ipv4/ipv6 subnets for which webui authentication should be bypassed; list entries are separated by commas", + return_type: String( + TypeInfo { + name: "bypass_auth_subnet_whitelist", + is_optional: false, + is_list: false, + description: Some( + "(White)list of ipv4/ipv6 subnets for which webui authentication should be bypassed; list entries are separated by commas", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "alternative_webui_enabled", + description: "True if an alternative WebUI should be used", + return_type: Bool( + TypeInfo { + name: "alternative_webui_enabled", + is_optional: false, + is_list: false, + description: Some( + "True if an alternative WebUI should be used", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "alternative_webui_path", + description: "File path to the alternative WebUI", + return_type: String( + TypeInfo { + name: "alternative_webui_path", + is_optional: false, + is_list: false, + description: Some( + "File path to the alternative WebUI", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "use_https", + description: "True if WebUI HTTPS access is enabled", + return_type: Bool( + TypeInfo { + name: "use_https", + is_optional: false, + is_list: false, + description: Some( + "True if WebUI HTTPS access is enabled", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "ssl_key", + description: "For API < v2.0.1: SSL keyfile contents (this is a not a path)", + return_type: String( + TypeInfo { + name: "ssl_key", + is_optional: false, + is_list: false, + description: Some( + "For API < v2.0.1: SSL keyfile contents (this is a not a path)", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "ssl_cert", + description: "For API < v2.0.1: SSL certificate contents (this is a not a path)", + return_type: String( + TypeInfo { + name: "ssl_cert", + is_optional: false, + is_list: false, + description: Some( + "For API < v2.0.1: SSL certificate contents (this is a not a path)", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "web_ui_https_key_path", + description: "For API ≥ v2.0.1: Path to SSL keyfile", + return_type: String( + TypeInfo { + name: "web_ui_https_key_path", + is_optional: false, + is_list: false, + description: Some( + "For API ≥ v2.0.1: Path to SSL keyfile", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "web_ui_https_cert_path", + description: "For API ≥ v2.0.1: Path to SSL certificate", + return_type: String( + TypeInfo { + name: "web_ui_https_cert_path", + is_optional: false, + is_list: false, + description: Some( + "For API ≥ v2.0.1: Path to SSL certificate", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "dyndns_enabled", + description: "True if server DNS should be updated dynamically", + return_type: Bool( + TypeInfo { + name: "dyndns_enabled", + is_optional: false, + is_list: false, + description: Some( + "True if server DNS should be updated dynamically", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "dyndns_service", + description: "See list of possible values here below", + return_type: Number( + TypeInfo { + name: "dyndns_service", + is_optional: false, + is_list: false, + description: Some( + "See list of possible values here below", + ), + type_description: Some( + TypeDescription { + values: [ + TypeDescriptions { + value: "0", + description: "Use DyDNS", + }, + TypeDescriptions { + value: "1", + description: "Use NOIP", + }, + ], + }, + ), + }, + ), + }, + ReturnTypeParameter { + name: "dyndns_username", + description: "Username for DDNS service", + return_type: String( + TypeInfo { + name: "dyndns_username", + is_optional: false, + is_list: false, + description: Some( + "Username for DDNS service", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "dyndns_password", + description: "Password for DDNS service", + return_type: String( + TypeInfo { + name: "dyndns_password", + is_optional: false, + is_list: false, + description: Some( + "Password for DDNS service", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "dyndns_domain", + description: "Your DDNS domain name", + return_type: String( + TypeInfo { + name: "dyndns_domain", + is_optional: false, + is_list: false, + description: Some( + "Your DDNS domain name", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "rss_refresh_interval", + description: "RSS refresh interval", + return_type: Number( + TypeInfo { + name: "rss_refresh_interval", + is_optional: false, + is_list: false, + description: Some( + "RSS refresh interval", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "rss_max_articles_per_feed", + description: "Max stored articles per RSS feed", + return_type: Number( + TypeInfo { + name: "rss_max_articles_per_feed", + is_optional: false, + is_list: false, + description: Some( + "Max stored articles per RSS feed", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "rss_processing_enabled", + description: "Enable processing of RSS feeds", + return_type: Bool( + TypeInfo { + name: "rss_processing_enabled", + is_optional: false, + is_list: false, + description: Some( + "Enable processing of RSS feeds", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "rss_auto_downloading_enabled", + description: "Enable auto-downloading of torrents from the RSS feeds", + return_type: Bool( + TypeInfo { + name: "rss_auto_downloading_enabled", + is_optional: false, + is_list: false, + description: Some( + "Enable auto-downloading of torrents from the RSS feeds", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "rss_download_repack_proper_episodes", + description: "For API ≥ v2.5.1: Enable downloading of repack/proper Episodes", + return_type: Bool( + TypeInfo { + name: "rss_download_repack_proper_episodes", + is_optional: false, + is_list: false, + description: Some( + "For API ≥ v2.5.1: Enable downloading of repack/proper Episodes", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "rss_smart_episode_filters", + description: "For API ≥ v2.5.1: List of RSS Smart Episode Filters", + return_type: String( + TypeInfo { + name: "rss_smart_episode_filters", + is_optional: false, + is_list: false, + description: Some( + "For API ≥ v2.5.1: List of RSS Smart Episode Filters", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "add_trackers_enabled", + description: "Enable automatic adding of trackers to new torrents", + return_type: Bool( + TypeInfo { + name: "add_trackers_enabled", + is_optional: false, + is_list: false, + description: Some( + "Enable automatic adding of trackers to new torrents", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "add_trackers", + description: "List of trackers to add to new torrent", + return_type: String( + TypeInfo { + name: "add_trackers", + is_optional: false, + is_list: false, + description: Some( + "List of trackers to add to new torrent", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "web_ui_use_custom_http_headers_enabled", + description: "For API ≥ v2.5.1: Enable custom http headers", + return_type: Bool( + TypeInfo { + name: "web_ui_use_custom_http_headers_enabled", + is_optional: false, + is_list: false, + description: Some( + "For API ≥ v2.5.1: Enable custom http headers", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "web_ui_custom_http_headers", + description: "For API ≥ v2.5.1: List of custom http headers", + return_type: String( + TypeInfo { + name: "web_ui_custom_http_headers", + is_optional: false, + is_list: false, + description: Some( + "For API ≥ v2.5.1: List of custom http headers", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "max_seeding_time_enabled", + description: "True enables max seeding time", + return_type: Bool( + TypeInfo { + name: "max_seeding_time_enabled", + is_optional: false, + is_list: false, + description: Some( + "True enables max seeding time", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "max_seeding_time", + description: "Number of minutes to seed a torrent", + return_type: Number( + TypeInfo { + name: "max_seeding_time", + is_optional: false, + is_list: false, + description: Some( + "Number of minutes to seed a torrent", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "announce_ip", + description: "TODO", + return_type: String( + TypeInfo { + name: "announce_ip", + is_optional: false, + is_list: false, + description: Some( + "TODO", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "announce_to_all_tiers", + description: "True always announce to all tiers", + return_type: Bool( + TypeInfo { + name: "announce_to_all_tiers", + is_optional: false, + is_list: false, + description: Some( + "True always announce to all tiers", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "announce_to_all_trackers", + description: "True always announce to all trackers in a tier", + return_type: Bool( + TypeInfo { + name: "announce_to_all_trackers", + is_optional: false, + is_list: false, + description: Some( + "True always announce to all trackers in a tier", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "async_io_threads", + description: "Number of asynchronous I/O threads", + return_type: Number( + TypeInfo { + name: "async_io_threads", + is_optional: false, + is_list: false, + description: Some( + "Number of asynchronous I/O threads", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "banned_IPs", + description: "List of banned IPs", + return_type: String( + TypeInfo { + name: "banned_IPs", + is_optional: false, + is_list: false, + description: Some( + "List of banned IPs", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "checking_memory_use", + description: "Outstanding memory when checking torrents in MiB", + return_type: Number( + TypeInfo { + name: "checking_memory_use", + is_optional: false, + is_list: false, + description: Some( + "Outstanding memory when checking torrents in MiB", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "current_interface_address", + description: "IP Address to bind to. Empty String means All addresses", + return_type: String( + TypeInfo { + name: "current_interface_address", + is_optional: false, + is_list: false, + description: Some( + "IP Address to bind to. Empty String means All addresses", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "current_network_interface", + description: "Network Interface used", + return_type: String( + TypeInfo { + name: "current_network_interface", + is_optional: false, + is_list: false, + description: Some( + "Network Interface used", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "disk_cache", + description: "Disk cache used in MiB", + return_type: Number( + TypeInfo { + name: "disk_cache", + is_optional: false, + is_list: false, + description: Some( + "Disk cache used in MiB", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "disk_cache_ttl", + description: "Disk cache expiry interval in seconds", + return_type: Number( + TypeInfo { + name: "disk_cache_ttl", + is_optional: false, + is_list: false, + description: Some( + "Disk cache expiry interval in seconds", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "embedded_tracker_port", + description: "Port used for embedded tracker", + return_type: Number( + TypeInfo { + name: "embedded_tracker_port", + is_optional: false, + is_list: false, + description: Some( + "Port used for embedded tracker", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "enable_coalesce_read_write", + description: "True enables coalesce reads & writes", + return_type: Bool( + TypeInfo { + name: "enable_coalesce_read_write", + is_optional: false, + is_list: false, + description: Some( + "True enables coalesce reads & writes", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "enable_embedded_tracker", + description: "True enables embedded tracker", + return_type: Bool( + TypeInfo { + name: "enable_embedded_tracker", + is_optional: false, + is_list: false, + description: Some( + "True enables embedded tracker", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "enable_multi_connections_from_same_ip", + description: "True allows multiple connections from the same IP address", + return_type: Bool( + TypeInfo { + name: "enable_multi_connections_from_same_ip", + is_optional: false, + is_list: false, + description: Some( + "True allows multiple connections from the same IP address", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "enable_os_cache", + description: "True enables os cache", + return_type: Bool( + TypeInfo { + name: "enable_os_cache", + is_optional: false, + is_list: false, + description: Some( + "True enables os cache", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "enable_upload_suggestions", + description: "True enables sending of upload piece suggestions", + return_type: Bool( + TypeInfo { + name: "enable_upload_suggestions", + is_optional: false, + is_list: false, + description: Some( + "True enables sending of upload piece suggestions", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "file_pool_size", + description: "File pool size", + return_type: Number( + TypeInfo { + name: "file_pool_size", + is_optional: false, + is_list: false, + description: Some( + "File pool size", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "outgoing_ports_max", + description: "Maximal outgoing port (0: Disabled)", + return_type: Number( + TypeInfo { + name: "outgoing_ports_max", + is_optional: false, + is_list: false, + description: Some( + "Maximal outgoing port (0: Disabled)", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "outgoing_ports_min", + description: "Minimal outgoing port (0: Disabled)", + return_type: Number( + TypeInfo { + name: "outgoing_ports_min", + is_optional: false, + is_list: false, + description: Some( + "Minimal outgoing port (0: Disabled)", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "recheck_completed_torrents", + description: "True rechecks torrents on completion", + return_type: Bool( + TypeInfo { + name: "recheck_completed_torrents", + is_optional: false, + is_list: false, + description: Some( + "True rechecks torrents on completion", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "resolve_peer_countries", + description: "True resolves peer countries", + return_type: Bool( + TypeInfo { + name: "resolve_peer_countries", + is_optional: false, + is_list: false, + description: Some( + "True resolves peer countries", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "save_resume_data_interval", + description: "Save resume data interval in min", + return_type: Number( + TypeInfo { + name: "save_resume_data_interval", + is_optional: false, + is_list: false, + description: Some( + "Save resume data interval in min", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "send_buffer_low_watermark", + description: "Send buffer low watermark in KiB", + return_type: Number( + TypeInfo { + name: "send_buffer_low_watermark", + is_optional: false, + is_list: false, + description: Some( + "Send buffer low watermark in KiB", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "send_buffer_watermark", + description: "Send buffer watermark in KiB", + return_type: Number( + TypeInfo { + name: "send_buffer_watermark", + is_optional: false, + is_list: false, + description: Some( + "Send buffer watermark in KiB", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "send_buffer_watermark_factor", + description: "Send buffer watermark factor in percent", + return_type: Number( + TypeInfo { + name: "send_buffer_watermark_factor", + is_optional: false, + is_list: false, + description: Some( + "Send buffer watermark factor in percent", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "socket_backlog_size", + description: "Socket backlog size", + return_type: Number( + TypeInfo { + name: "socket_backlog_size", + is_optional: false, + is_list: false, + description: Some( + "Socket backlog size", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "upload_choking_algorithm", + description: "Upload choking algorithm used (see list of possible values below)", + return_type: Number( + TypeInfo { + name: "upload_choking_algorithm", + is_optional: false, + is_list: false, + description: Some( + "Upload choking algorithm used (see list of possible values below)", + ), + type_description: Some( + TypeDescription { + values: [ + TypeDescriptions { + value: "0", + description: "Round-robin", + }, + TypeDescriptions { + value: "1", + description: "Fastest upload", + }, + TypeDescriptions { + value: "2", + description: "Anti-leech", + }, + ], + }, + ), + }, + ), + }, + ReturnTypeParameter { + name: "upload_slots_behavior", + description: "Upload slots behavior used (see list of possible values below)", + return_type: Number( + TypeInfo { + name: "upload_slots_behavior", + is_optional: false, + is_list: false, + description: Some( + "Upload slots behavior used (see list of possible values below)", + ), + type_description: Some( + TypeDescription { + values: [ + TypeDescriptions { + value: "0", + description: "Fixed slots", + }, + TypeDescriptions { + value: "1", + description: "Upload rate based", + }, + ], + }, + ), + }, + ), + }, + ReturnTypeParameter { + name: "upnp_lease_duration", + description: "UPnP lease duration (0: Permanent lease)", + return_type: Number( + TypeInfo { + name: "upnp_lease_duration", + is_optional: false, + is_list: false, + description: Some( + "UPnP lease duration (0: Permanent lease)", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "utp_tcp_mixed_mode", + description: "μTP-TCP mixed mode algorithm (see list of possible values below)", + return_type: Number( + TypeInfo { + name: "utp_tcp_mixed_mode", + is_optional: false, + is_list: false, + description: Some( + "μTP-TCP mixed mode algorithm (see list of possible values below)", + ), + type_description: Some( + TypeDescription { + values: [ + TypeDescriptions { + value: "0", + description: "Prefer TCP", + }, + TypeDescriptions { + value: "1", + description: "Peer proportional", + }, + ], + }, + ), + }, + ), + }, + ], + }, + ), + url: "preferences", + }, + ApiMethod { + name: "setPreferences", + description: Some( + "1. There is no need to pass all possible preferences' `token:value` pairs if you only want to change one option\n 1. Paths in `scan_dirs` must exist, otherwise this option will have no effect\n 1. String values must be quoted; integer and boolean values must never be quoted\n\nFor a list of possible preference options see [Get application preferences](#get-application-preferences)", + ), + parameters: None, + return_type: None, + url: "setPreferences", + }, + ApiMethod { + name: "defaultSavePath", + description: None, + parameters: None, + return_type: None, + url: "defaultSavePath", + }, + ], + description: Some( + "All Application API methods are under \"app\", e.g.: `/api/v2/app/methodName`.", + ), + url: "app", + }, + ApiGroup { + name: "log", + methods: [ + ApiMethod { + name: "main", + description: Some( + "Each element of the array has the following properties:\n\nExample:\n\n```JSON\n[\n {\n \"id\":0,\n \"message\":\"qBittorrent v3.4.0 started\",\n \"timestamp\":1507969127860,\n \"type\":1\n },\n {\n \"id\":1,\n \"message\":\"qBittorrent is trying to listen on any interface port: 19036\",\n \"timestamp\":1507969127869,\n \"type\":2\n },\n {\n \"id\":2,\n \"message\":\"Peer ID: -qB3400-\",\n \"timestamp\":1507969127870,\n \"type\":1\n },\n {\n \"id\":3,\n \"message\":\"HTTP User-Agent is 'qBittorrent/3.4.0'\",\n \"timestamp\":1507969127870,\n \"type\":1\n },\n {\n \"id\":4,\n \"message\":\"DHT support [ON]\",\n \"timestamp\":1507969127871,\n \"type\":2\n },\n {\n \"id\":5,\n \"message\":\"Local Peer Discovery support [ON]\",\n \"timestamp\":1507969127871,\n \"type\":2\n },\n {\n \"id\":6,\n \"message\":\"PeX support [ON]\",\n \"timestamp\":1507969127871,\n \"type\":2\n },\n {\n \"id\":7,\n \"message\":\"Anonymous mode [OFF]\",\n \"timestamp\":1507969127871,\n \"type\":2\n },\n {\n \"id\":8,\n \"message\":\"Encryption support [ON]\",\n \"timestamp\":1507969127871,\n \"type\":2\n },\n {\n \"id\":9,\n \"message\":\"Embedded Tracker [OFF]\",\n \"timestamp\":1507969127871,\n \"type\":2\n },\n {\n \"id\":10,\n \"message\":\"UPnP / NAT-PMP support [ON]\",\n \"timestamp\":1507969127873,\n \"type\":2\n },\n {\n \"id\":11,\n \"message\":\"Web UI: Now listening on port 8080\",\n \"timestamp\":1507969127883,\n \"type\":1\n },\n {\n \"id\":12,\n \"message\":\"Options were saved successfully.\",\n \"timestamp\":1507969128055,\n \"type\":1\n },\n {\n \"id\":13,\n \"message\":\"qBittorrent is successfully listening on interface :: port: TCP/19036\",\n \"timestamp\":1507969128270,\n \"type\":2\n },\n {\n \"id\":14,\n \"message\":\"qBittorrent is successfully listening on interface 0.0.0.0 port: TCP/19036\",\n \"timestamp\":1507969128271,\n \"type\":2\n },\n {\n \"id\":15,\n \"message\":\"qBittorrent is successfully listening on interface 0.0.0.0 port: UDP/19036\",\n \"timestamp\":1507969128272,\n \"type\":2\n }\n]\n```", + ), + parameters: Some( + [ + Bool( + TypeInfo { + name: "normal", + is_optional: true, + is_list: false, + description: Some( + "Include normal messages (default: true)", + ), + type_description: None, + }, + ), + Bool( + TypeInfo { + name: "info", + is_optional: true, + is_list: false, + description: Some( + "Include info messages (default: true)", + ), + type_description: None, + }, + ), + Bool( + TypeInfo { + name: "warning", + is_optional: true, + is_list: false, + description: Some( + "Include warning messages (default: true)", + ), + type_description: None, + }, + ), + Bool( + TypeInfo { + name: "critical", + is_optional: true, + is_list: false, + description: Some( + "Include critical messages (default: true)", + ), + type_description: None, + }, + ), + Number( + TypeInfo { + name: "last_known_id", + is_optional: true, + is_list: false, + description: Some( + "Exclude messages with \"message id\" <= last_known_id (default: -1)", + ), + type_description: None, + }, + ), + ], + ), + return_type: Some( + ReturnType { + is_list: true, + parameters: [ + ReturnTypeParameter { + name: "id", + description: "ID of the message", + return_type: Number( + TypeInfo { + name: "id", + is_optional: false, + is_list: false, + description: Some( + "ID of the message", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "message", + description: "Text of the message", + return_type: String( + TypeInfo { + name: "message", + is_optional: false, + is_list: false, + description: Some( + "Text of the message", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "timestamp", + description: "Milliseconds since epoch", + return_type: Number( + TypeInfo { + name: "timestamp", + is_optional: false, + is_list: false, + description: Some( + "Milliseconds since epoch", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "type", + description: "Type of the message: Log::NORMAL: 1, Log::INFO: 2, Log::WARNING: 4, Log::CRITICAL: 8", + return_type: Number( + TypeInfo { + name: "type", + is_optional: false, + is_list: false, + description: Some( + "Type of the message: Log::NORMAL: 1, Log::INFO: 2, Log::WARNING: 4, Log::CRITICAL: 8", + ), + type_description: None, + }, + ), + }, + ], + }, + ), + url: "main", + }, + ApiMethod { + name: "peers", + description: None, + parameters: Some( + [ + Number( + TypeInfo { + name: "last_known_id", + is_optional: true, + is_list: false, + description: Some( + "Exclude messages with \"message id\" <= last_known_id (default: -1)", + ), + type_description: None, + }, + ), + ], + ), + return_type: None, + url: "peers", + }, + ], + description: Some( + "All Log API methods are under \"log\", e.g.: `/api/v2/log/methodName`.", + ), + url: "log", + }, + ApiGroup { + name: "sync", + methods: [ + ApiMethod { + name: "maindata", + description: Some( + "Example:\n\n```JSON\n{\n \"rid\":15,\n \"torrents\":\n {\n \"8c212779b4abde7c6bc608063a0d008b7e40ce32\":\n {\n \"state\":\"pausedUP\"\n }\n }\n}\n```", + ), + parameters: Some( + [ + Number( + TypeInfo { + name: "rid", + is_optional: false, + is_list: false, + description: Some( + "Response ID. If not provided, rid=0 will be assumed. If the given rid is different from the one of last server reply, full_update will be true (see the server reply details for more info)", + ), + type_description: None, + }, + ), + ], + ), + return_type: Some( + ReturnType { + is_list: false, + parameters: [ + ReturnTypeParameter { + name: "rid", + description: "Response ID", + return_type: Number( + TypeInfo { + name: "rid", + is_optional: false, + is_list: false, + description: Some( + "Response ID", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "full_update", + description: "Whether the response contains all the data or partial data", + return_type: Bool( + TypeInfo { + name: "full_update", + is_optional: false, + is_list: false, + description: Some( + "Whether the response contains all the data or partial data", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "torrents", + description: "Property: torrent hash, value: same as [torrent list](#get-torrent-list)", + return_type: Object( + TypeInfo { + name: "torrents", + is_optional: false, + is_list: false, + description: Some( + "Property: torrent hash, value: same as [torrent list](#get-torrent-list)", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "torrents_removed", + description: "List of hashes of torrents removed since last request", + return_type: StringArray( + TypeInfo { + name: "torrents_removed", + is_optional: false, + is_list: false, + description: Some( + "List of hashes of torrents removed since last request", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "categories", + description: "Info for categories added since last request", + return_type: Object( + TypeInfo { + name: "categories", + is_optional: false, + is_list: false, + description: Some( + "Info for categories added since last request", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "categories_removed", + description: "List of categories removed since last request", + return_type: StringArray( + TypeInfo { + name: "categories_removed", + is_optional: false, + is_list: false, + description: Some( + "List of categories removed since last request", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "tags", + description: "List of tags added since last request", + return_type: StringArray( + TypeInfo { + name: "tags", + is_optional: false, + is_list: false, + description: Some( + "List of tags added since last request", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "tags_removed", + description: "List of tags removed since last request", + return_type: StringArray( + TypeInfo { + name: "tags_removed", + is_optional: false, + is_list: false, + description: Some( + "List of tags removed since last request", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "server_state", + description: "Global transfer info", + return_type: Object( + TypeInfo { + name: "server_state", + is_optional: false, + is_list: false, + description: Some( + "Global transfer info", + ), + type_description: None, + }, + ), + }, + ], + }, + ), + url: "maindata", + }, + ApiMethod { + name: "torrentPeers", + description: None, + parameters: Some( + [ + String( + TypeInfo { + name: "hash", + is_optional: false, + is_list: false, + description: Some( + "Torrent hash", + ), + type_description: None, + }, + ), + Number( + TypeInfo { + name: "rid", + is_optional: false, + is_list: false, + description: Some( + "Response ID. If not provided, rid=0 will be assumed. If the given rid is different from the one of last server reply, full_update will be true (see the server reply details for more info)", + ), + type_description: None, + }, + ), + ], + ), + return_type: None, + url: "torrentPeers", + }, + ], + description: Some( + "Sync API implements requests for obtaining changes since the last request.\nAll Sync API methods are under \"sync\", e.g.: `/api/v2/sync/methodName`.", + ), + url: "sync", + }, + ApiGroup { + name: "transfer_info", + methods: [ + ApiMethod { + name: "info", + description: Some( + "In addition to the above in partial data requests (see [Get partial data](#get-partial-data) for more info):\n\nPossible values of `connection_status`:\n\nExample:\n\n```JSON\n{\n \"connection_status\":\"connected\",\n \"dht_nodes\":386,\n \"dl_info_data\":681521119,\n \"dl_info_speed\":0,\n \"dl_rate_limit\":0,\n \"up_info_data\":10747904,\n \"up_info_speed\":0,\n \"up_rate_limit\":1048576\n}\n```", + ), + parameters: None, + return_type: Some( + ReturnType { + is_list: false, + parameters: [ + ReturnTypeParameter { + name: "dl_info_speed", + description: "Global download rate (bytes/s)", + return_type: Number( + TypeInfo { + name: "dl_info_speed", + is_optional: false, + is_list: false, + description: Some( + "Global download rate (bytes/s)", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "dl_info_data", + description: "Data downloaded this session (bytes)", + return_type: Number( + TypeInfo { + name: "dl_info_data", + is_optional: false, + is_list: false, + description: Some( + "Data downloaded this session (bytes)", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "up_info_speed", + description: "Global upload rate (bytes/s)", + return_type: Number( + TypeInfo { + name: "up_info_speed", + is_optional: false, + is_list: false, + description: Some( + "Global upload rate (bytes/s)", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "up_info_data", + description: "Data uploaded this session (bytes)", + return_type: Number( + TypeInfo { + name: "up_info_data", + is_optional: false, + is_list: false, + description: Some( + "Data uploaded this session (bytes)", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "dl_rate_limit", + description: "Download rate limit (bytes/s)", + return_type: Number( + TypeInfo { + name: "dl_rate_limit", + is_optional: false, + is_list: false, + description: Some( + "Download rate limit (bytes/s)", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "up_rate_limit", + description: "Upload rate limit (bytes/s)", + return_type: Number( + TypeInfo { + name: "up_rate_limit", + is_optional: false, + is_list: false, + description: Some( + "Upload rate limit (bytes/s)", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "dht_nodes", + description: "DHT nodes connected to", + return_type: Number( + TypeInfo { + name: "dht_nodes", + is_optional: false, + is_list: false, + description: Some( + "DHT nodes connected to", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "connection_status", + description: "Connection status. See possible values here below", + return_type: String( + TypeInfo { + name: "connection_status", + is_optional: false, + is_list: false, + description: Some( + "Connection status. See possible values here below", + ), + type_description: Some( + TypeDescription { + values: [ + TypeDescriptions { + value: "connected", + description: "", + }, + TypeDescriptions { + value: "firewalled", + description: "", + }, + TypeDescriptions { + value: "disconnected", + description: "", + }, + ], + }, + ), + }, + ), + }, + ], + }, + ), + url: "info", + }, + ApiMethod { + name: "speedLimitsMode", + description: None, + parameters: None, + return_type: None, + url: "speedLimitsMode", + }, + ApiMethod { + name: "toggleSpeedLimitsMode", + description: None, + parameters: None, + return_type: None, + url: "toggleSpeedLimitsMode", + }, + ApiMethod { + name: "downloadLimit", + description: None, + parameters: None, + return_type: None, + url: "downloadLimit", + }, + ApiMethod { + name: "setDownloadLimit", + description: None, + parameters: Some( + [ + Number( + TypeInfo { + name: "limit", + is_optional: false, + is_list: false, + description: Some( + "The global download speed limit to set in bytes/second", + ), + type_description: None, + }, + ), + ], + ), + return_type: None, + url: "setDownloadLimit", + }, + ApiMethod { + name: "uploadLimit", + description: None, + parameters: None, + return_type: None, + url: "uploadLimit", + }, + ApiMethod { + name: "setUploadLimit", + description: None, + parameters: Some( + [ + Number( + TypeInfo { + name: "limit", + is_optional: false, + is_list: false, + description: Some( + "The global upload speed limit to set in bytes/second", + ), + type_description: None, + }, + ), + ], + ), + return_type: None, + url: "setUploadLimit", + }, + ApiMethod { + name: "banPeers", + description: None, + parameters: Some( + [ + String( + TypeInfo { + name: "peers", + is_optional: false, + is_list: false, + description: Some( + "The peer to ban, or multiple peers separated by a pipe \\", + ), + type_description: None, + }, + ), + ], + ), + return_type: None, + url: "banPeers", + }, + ], + description: Some( + "All Transfer info API methods are under \"transfer\", e.g.: `/api/v2/transfer/methodName`.", + ), + url: "transfer", + }, + ApiGroup { + name: "torrent_management", + methods: [ + ApiMethod { + name: "info", + description: Some( + "Possible values of `state`:\n\nExample:\n\n```JSON\n[\n {\n \"dlspeed\":9681262,\n \"eta\":87,\n \"f_l_piece_prio\":false,\n \"force_start\":false,\n \"hash\":\"8c212779b4abde7c6bc608063a0d008b7e40ce32\",\n \"category\":\"\",\n \"tags\": \"\",\n \"name\":\"debian-8.1.0-amd64-CD-1.iso\",\n \"num_complete\":-1,\n \"num_incomplete\":-1,\n \"num_leechs\":2,\n \"num_seeds\":54,\n \"priority\":1,\n \"progress\":0.16108787059783936,\n \"ratio\":0,\n \"seq_dl\":false,\n \"size\":657457152,\n \"state\":\"downloading\",\n \"super_seeding\":false,\n \"upspeed\":0\n },\n {\n another_torrent_info\n }\n]\n```", + ), + parameters: Some( + [ + String( + TypeInfo { + name: "filter", + is_optional: true, + is_list: false, + description: Some( + "Filter torrent list by state. Allowed state filters: all, downloading, seeding, completed, paused, active, inactive, resumed, stalled, stalled_uploading, stalled_downloading, errored", + ), + type_description: None, + }, + ), + String( + TypeInfo { + name: "category", + is_optional: true, + is_list: false, + description: Some( + "Get torrents with the given category (empty string means \"without category\"; no \"category\" parameter means \"any category\" <- broken until [#11748](https://github.com/qbittorrent/qBittorrent/issues/11748) is resolved). Remember to URL-encode the category name. For example, My category becomes My%20category", + ), + type_description: None, + }, + ), + String( + TypeInfo { + name: "tag", + is_optional: true, + is_list: false, + description: Some( + "Get torrents with the given tag (empty string means \"without tag\"; no \"tag\" parameter means \"any tag\". Remember to URL-encode the category name. For example, My tag becomes My%20tag", + ), + type_description: None, + }, + ), + String( + TypeInfo { + name: "sort", + is_optional: true, + is_list: false, + description: Some( + "Sort torrents by given key. They can be sorted using any field of the response's JSON array (which are documented below) as the sort key.", + ), + type_description: None, + }, + ), + Bool( + TypeInfo { + name: "reverse", + is_optional: true, + is_list: false, + description: Some( + "Enable reverse sorting. Defaults to false", + ), + type_description: None, + }, + ), + Number( + TypeInfo { + name: "limit", + is_optional: true, + is_list: false, + description: Some( + "Limit the number of torrents returned", + ), + type_description: None, + }, + ), + Number( + TypeInfo { + name: "offset", + is_optional: true, + is_list: false, + description: Some( + "Set offset (if less than 0, offset from end)", + ), + type_description: None, + }, + ), + String( + TypeInfo { + name: "hashes", + is_optional: true, + is_list: false, + description: Some( + "Filter by hashes. Can contain multiple hashes separated by \\", + ), + type_description: None, + }, + ), + ], + ), + return_type: Some( + ReturnType { + is_list: true, + parameters: [ + ReturnTypeParameter { + name: "added_on", + description: "Time (Unix Epoch) when the torrent was added to the client", + return_type: Number( + TypeInfo { + name: "added_on", + is_optional: false, + is_list: false, + description: Some( + "Time (Unix Epoch) when the torrent was added to the client", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "amount_left", + description: "Amount of data left to download (bytes)", + return_type: Number( + TypeInfo { + name: "amount_left", + is_optional: false, + is_list: false, + description: Some( + "Amount of data left to download (bytes)", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "auto_tmm", + description: "Whether this torrent is managed by Automatic Torrent Management", + return_type: Bool( + TypeInfo { + name: "auto_tmm", + is_optional: false, + is_list: false, + description: Some( + "Whether this torrent is managed by Automatic Torrent Management", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "availability", + description: "Percentage of file pieces currently available", + return_type: Float( + TypeInfo { + name: "availability", + is_optional: false, + is_list: false, + description: Some( + "Percentage of file pieces currently available", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "category", + description: "Category of the torrent", + return_type: String( + TypeInfo { + name: "category", + is_optional: false, + is_list: false, + description: Some( + "Category of the torrent", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "completed", + description: "Amount of transfer data completed (bytes)", + return_type: Number( + TypeInfo { + name: "completed", + is_optional: false, + is_list: false, + description: Some( + "Amount of transfer data completed (bytes)", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "completion_on", + description: "Time (Unix Epoch) when the torrent completed", + return_type: Number( + TypeInfo { + name: "completion_on", + is_optional: false, + is_list: false, + description: Some( + "Time (Unix Epoch) when the torrent completed", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "content_path", + description: "Absolute path of torrent content (root path for multifile torrents, absolute file path for singlefile torrents)", + return_type: String( + TypeInfo { + name: "content_path", + is_optional: false, + is_list: false, + description: Some( + "Absolute path of torrent content (root path for multifile torrents, absolute file path for singlefile torrents)", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "dl_limit", + description: "Torrent download speed limit (bytes/s). -1 if ulimited.", + return_type: Number( + TypeInfo { + name: "dl_limit", + is_optional: false, + is_list: false, + description: Some( + "Torrent download speed limit (bytes/s). -1 if ulimited.", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "dlspeed", + description: "Torrent download speed (bytes/s)", + return_type: Number( + TypeInfo { + name: "dlspeed", + is_optional: false, + is_list: false, + description: Some( + "Torrent download speed (bytes/s)", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "downloaded", + description: "Amount of data downloaded", + return_type: Number( + TypeInfo { + name: "downloaded", + is_optional: false, + is_list: false, + description: Some( + "Amount of data downloaded", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "downloaded_session", + description: "Amount of data downloaded this session", + return_type: Number( + TypeInfo { + name: "downloaded_session", + is_optional: false, + is_list: false, + description: Some( + "Amount of data downloaded this session", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "eta", + description: "Torrent ETA (seconds)", + return_type: Number( + TypeInfo { + name: "eta", + is_optional: false, + is_list: false, + description: Some( + "Torrent ETA (seconds)", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "f_l_piece_prio", + description: "True if first last piece are prioritized", + return_type: Bool( + TypeInfo { + name: "f_l_piece_prio", + is_optional: false, + is_list: false, + description: Some( + "True if first last piece are prioritized", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "force_start", + description: "True if force start is enabled for this torrent", + return_type: Bool( + TypeInfo { + name: "force_start", + is_optional: false, + is_list: false, + description: Some( + "True if force start is enabled for this torrent", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "hash", + description: "Torrent hash", + return_type: String( + TypeInfo { + name: "hash", + is_optional: false, + is_list: false, + description: Some( + "Torrent hash", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "last_activity", + description: "Last time (Unix Epoch) when a chunk was downloaded/uploaded", + return_type: Number( + TypeInfo { + name: "last_activity", + is_optional: false, + is_list: false, + description: Some( + "Last time (Unix Epoch) when a chunk was downloaded/uploaded", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "magnet_uri", + description: "Magnet URI corresponding to this torrent", + return_type: String( + TypeInfo { + name: "magnet_uri", + is_optional: false, + is_list: false, + description: Some( + "Magnet URI corresponding to this torrent", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "max_ratio", + description: "Maximum share ratio until torrent is stopped from seeding/uploading", + return_type: Float( + TypeInfo { + name: "max_ratio", + is_optional: false, + is_list: false, + description: Some( + "Maximum share ratio until torrent is stopped from seeding/uploading", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "max_seeding_time", + description: "Maximum seeding time (seconds) until torrent is stopped from seeding", + return_type: Number( + TypeInfo { + name: "max_seeding_time", + is_optional: false, + is_list: false, + description: Some( + "Maximum seeding time (seconds) until torrent is stopped from seeding", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "name", + description: "Torrent name", + return_type: String( + TypeInfo { + name: "name", + is_optional: false, + is_list: false, + description: Some( + "Torrent name", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "num_complete", + description: "Number of seeds in the swarm", + return_type: Number( + TypeInfo { + name: "num_complete", + is_optional: false, + is_list: false, + description: Some( + "Number of seeds in the swarm", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "num_incomplete", + description: "Number of leechers in the swarm", + return_type: Number( + TypeInfo { + name: "num_incomplete", + is_optional: false, + is_list: false, + description: Some( + "Number of leechers in the swarm", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "num_leechs", + description: "Number of leechers connected to", + return_type: Number( + TypeInfo { + name: "num_leechs", + is_optional: false, + is_list: false, + description: Some( + "Number of leechers connected to", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "num_seeds", + description: "Number of seeds connected to", + return_type: Number( + TypeInfo { + name: "num_seeds", + is_optional: false, + is_list: false, + description: Some( + "Number of seeds connected to", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "priority", + description: "Torrent priority. Returns -1 if queuing is disabled or torrent is in seed mode", + return_type: Number( + TypeInfo { + name: "priority", + is_optional: false, + is_list: false, + description: Some( + "Torrent priority. Returns -1 if queuing is disabled or torrent is in seed mode", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "progress", + description: "Torrent progress (percentage/100)", + return_type: Float( + TypeInfo { + name: "progress", + is_optional: false, + is_list: false, + description: Some( + "Torrent progress (percentage/100)", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "ratio", + description: "Torrent share ratio. Max ratio value: 9999.", + return_type: Float( + TypeInfo { + name: "ratio", + is_optional: false, + is_list: false, + description: Some( + "Torrent share ratio. Max ratio value: 9999.", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "ratio_limit", + description: "TODO (what is different from max_ratio?)", + return_type: Float( + TypeInfo { + name: "ratio_limit", + is_optional: false, + is_list: false, + description: Some( + "TODO (what is different from max_ratio?)", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "save_path", + description: "Path where this torrent's data is stored", + return_type: String( + TypeInfo { + name: "save_path", + is_optional: false, + is_list: false, + description: Some( + "Path where this torrent's data is stored", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "seeding_time", + description: "Torrent elapsed time while complete (seconds)", + return_type: Number( + TypeInfo { + name: "seeding_time", + is_optional: false, + is_list: false, + description: Some( + "Torrent elapsed time while complete (seconds)", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "seeding_time_limit", + description: "TODO (what is different from max_seeding_time?) seeding_time_limit is a per torrent setting, when Automatic Torrent Management is disabled, furthermore then max_seeding_time is set to seeding_time_limit for this torrent. If Automatic Torrent Management is enabled, the value is -2. And if max_seeding_time is unset it have a default value -1.", + return_type: Number( + TypeInfo { + name: "seeding_time_limit", + is_optional: false, + is_list: false, + description: Some( + "TODO (what is different from max_seeding_time?) seeding_time_limit is a per torrent setting, when Automatic Torrent Management is disabled, furthermore then max_seeding_time is set to seeding_time_limit for this torrent. If Automatic Torrent Management is enabled, the value is -2. And if max_seeding_time is unset it have a default value -1.", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "seen_complete", + description: "Time (Unix Epoch) when this torrent was last seen complete", + return_type: Number( + TypeInfo { + name: "seen_complete", + is_optional: false, + is_list: false, + description: Some( + "Time (Unix Epoch) when this torrent was last seen complete", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "seq_dl", + description: "True if sequential download is enabled", + return_type: Bool( + TypeInfo { + name: "seq_dl", + is_optional: false, + is_list: false, + description: Some( + "True if sequential download is enabled", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "size", + description: "Total size (bytes) of files selected for download", + return_type: Number( + TypeInfo { + name: "size", + is_optional: false, + is_list: false, + description: Some( + "Total size (bytes) of files selected for download", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "state", + description: "Torrent state. See table here below for the possible values", + return_type: String( + TypeInfo { + name: "state", + is_optional: false, + is_list: false, + description: Some( + "Torrent state. See table here below for the possible values", + ), + type_description: Some( + TypeDescription { + values: [ + TypeDescriptions { + value: "error", + description: "Some error occurred, applies to paused torrents", + }, + TypeDescriptions { + value: "missingFiles", + description: "Torrent data files is missing", + }, + TypeDescriptions { + value: "uploading", + description: "Torrent is being seeded and data is being transferred", + }, + TypeDescriptions { + value: "pausedUP", + description: "Torrent is paused and has finished downloading", + }, + TypeDescriptions { + value: "queuedUP", + description: "Queuing is enabled and torrent is queued for upload", + }, + TypeDescriptions { + value: "stalledUP", + description: "Torrent is being seeded, but no connection were made", + }, + TypeDescriptions { + value: "checkingUP", + description: "Torrent has finished downloading and is being checked", + }, + TypeDescriptions { + value: "forcedUP", + description: "Torrent is forced to uploading and ignore queue limit", + }, + TypeDescriptions { + value: "allocating", + description: "Torrent is allocating disk space for download", + }, + TypeDescriptions { + value: "downloading", + description: "Torrent is being downloaded and data is being transferred", + }, + TypeDescriptions { + value: "metaDL", + description: "Torrent has just started downloading and is fetching metadata", + }, + TypeDescriptions { + value: "pausedDL", + description: "Torrent is paused and has NOT finished downloading", + }, + TypeDescriptions { + value: "queuedDL", + description: "Queuing is enabled and torrent is queued for download", + }, + TypeDescriptions { + value: "stalledDL", + description: "Torrent is being downloaded, but no connection were made", + }, + TypeDescriptions { + value: "checkingDL", + description: "Same as checkingUP, but torrent has NOT finished downloading", + }, + TypeDescriptions { + value: "forcedDL", + description: "Torrent is forced to downloading to ignore queue limit", + }, + TypeDescriptions { + value: "checkingResumeData", + description: "Checking resume data on qBt startup", + }, + TypeDescriptions { + value: "moving", + description: "Torrent is moving to another location", + }, + TypeDescriptions { + value: "unknown", + description: "Unknown status", + }, + ], + }, + ), + }, + ), + }, + ReturnTypeParameter { + name: "super_seeding", + description: "True if super seeding is enabled", + return_type: Bool( + TypeInfo { + name: "super_seeding", + is_optional: false, + is_list: false, + description: Some( + "True if super seeding is enabled", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "tags", + description: "Comma-concatenated tag list of the torrent", + return_type: String( + TypeInfo { + name: "tags", + is_optional: false, + is_list: false, + description: Some( + "Comma-concatenated tag list of the torrent", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "time_active", + description: "Total active time (seconds)", + return_type: Number( + TypeInfo { + name: "time_active", + is_optional: false, + is_list: false, + description: Some( + "Total active time (seconds)", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "total_size", + description: "Total size (bytes) of all file in this torrent (including unselected ones)", + return_type: Number( + TypeInfo { + name: "total_size", + is_optional: false, + is_list: false, + description: Some( + "Total size (bytes) of all file in this torrent (including unselected ones)", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "tracker", + description: "The first tracker with working status. Returns empty string if no tracker is working.", + return_type: String( + TypeInfo { + name: "tracker", + is_optional: false, + is_list: false, + description: Some( + "The first tracker with working status. Returns empty string if no tracker is working.", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "up_limit", + description: "Torrent upload speed limit (bytes/s). -1 if ulimited.", + return_type: Number( + TypeInfo { + name: "up_limit", + is_optional: false, + is_list: false, + description: Some( + "Torrent upload speed limit (bytes/s). -1 if ulimited.", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "uploaded", + description: "Amount of data uploaded", + return_type: Number( + TypeInfo { + name: "uploaded", + is_optional: false, + is_list: false, + description: Some( + "Amount of data uploaded", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "uploaded_session", + description: "Amount of data uploaded this session", + return_type: Number( + TypeInfo { + name: "uploaded_session", + is_optional: false, + is_list: false, + description: Some( + "Amount of data uploaded this session", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "upspeed", + description: "Torrent upload speed (bytes/s)", + return_type: Number( + TypeInfo { + name: "upspeed", + is_optional: false, + is_list: false, + description: Some( + "Torrent upload speed (bytes/s)", + ), + type_description: None, + }, + ), + }, + ], + }, + ), + url: "info", + }, + ApiMethod { + name: "properties", + description: Some( + "- empty, if the torrent hash is invalid\n- otherwise, a JSON object with the following fields\n\nNB: `-1` is returned if the type of the property is integer but its value is not known.\n\nExample:\n\n```JSON\n{\n \"addition_date\":1438429165,\n \"comment\":\"\\\"Debian CD from cdimage.debian.org\\\"\",\n \"completion_date\":1438429234,\n \"created_by\":\"\",\n \"creation_date\":1433605214,\n \"dl_limit\":-1,\n \"dl_speed\":0,\n \"dl_speed_avg\":9736015,\n \"eta\":8640000,\n \"last_seen\":1438430354,\n \"nb_connections\":3,\n \"nb_connections_limit\":250,\n \"peers\":1,\n \"peers_total\":89,\n \"piece_size\":524288,\n \"pieces_have\":1254,\n \"pieces_num\":1254,\n \"reannounce\":672,\n \"save_path\":\"/Downloads/debian-8.1.0-amd64-CD-1.iso\",\n \"seeding_time\":1128,\n \"seeds\":1,\n \"seeds_total\":254,\n \"share_ratio\":0.00072121022562178299,\n \"time_elapsed\":1197,\n \"total_downloaded\":681521119,\n \"total_downloaded_session\":681521119,\n \"total_size\":657457152,\n \"total_uploaded\":491520,\n \"total_uploaded_session\":491520,\n \"total_wasted\":23481724,\n \"up_limit\":-1,\n \"up_speed\":0,\n \"up_speed_avg\":410\n}\n```", + ), + parameters: Some( + [ + String( + TypeInfo { + name: "hash", + is_optional: false, + is_list: false, + description: Some( + "The hash of the torrent you want to get the generic properties of", + ), + type_description: None, + }, + ), + ], + ), + return_type: None, + url: "properties", + }, + ApiMethod { + name: "trackers", + description: Some( + "Possible values of `status`:\n\nExample:\n\n```JSON\n[\n {\n \"msg\":\"\",\n \"num_peers\":100,\n \"status\":2,\n \"url\":\"http://bttracker.debian.org:6969/announce\"\n },\n {\n another_tracker_info\n }\n]\n```", + ), + parameters: Some( + [ + String( + TypeInfo { + name: "hash", + is_optional: false, + is_list: false, + description: Some( + "The hash of the torrent you want to get the trackers of", + ), + type_description: None, + }, + ), + ], + ), + return_type: Some( + ReturnType { + is_list: true, + parameters: [ + ReturnTypeParameter { + name: "url", + description: "Tracker url", + return_type: String( + TypeInfo { + name: "url", + is_optional: false, + is_list: false, + description: Some( + "Tracker url", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "status", + description: "Tracker status. See the table below for possible values", + return_type: Number( + TypeInfo { + name: "status", + is_optional: false, + is_list: false, + description: Some( + "Tracker status. See the table below for possible values", + ), + type_description: Some( + TypeDescription { + values: [ + TypeDescriptions { + value: "0", + description: "Tracker is disabled (used for DHT, PeX, and LSD)", + }, + TypeDescriptions { + value: "1", + description: "Tracker has not been contacted yet", + }, + TypeDescriptions { + value: "2", + description: "Tracker has been contacted and is working", + }, + TypeDescriptions { + value: "3", + description: "Tracker is updating", + }, + TypeDescriptions { + value: "4", + description: "Tracker has been contacted, but it is not working (or doesn't send proper replies)", + }, + ], + }, + ), + }, + ), + }, + ReturnTypeParameter { + name: "tier", + description: "Tracker priority tier. Lower tier trackers are tried before higher tiers. Tier numbers are valid when >= 0, < 0 is used as placeholder when tier does not exist for special entries (such as DHT).", + return_type: Number( + TypeInfo { + name: "tier", + is_optional: false, + is_list: false, + description: Some( + "Tracker priority tier. Lower tier trackers are tried before higher tiers. Tier numbers are valid when >= 0, < 0 is used as placeholder when tier does not exist for special entries (such as DHT).", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "num_peers", + description: "Number of peers for current torrent, as reported by the tracker", + return_type: Number( + TypeInfo { + name: "num_peers", + is_optional: false, + is_list: false, + description: Some( + "Number of peers for current torrent, as reported by the tracker", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "num_seeds", + description: "Number of seeds for current torrent, asreported by the tracker", + return_type: Number( + TypeInfo { + name: "num_seeds", + is_optional: false, + is_list: false, + description: Some( + "Number of seeds for current torrent, asreported by the tracker", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "num_leeches", + description: "Number of leeches for current torrent, as reported by the tracker", + return_type: Number( + TypeInfo { + name: "num_leeches", + is_optional: false, + is_list: false, + description: Some( + "Number of leeches for current torrent, as reported by the tracker", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "num_downloaded", + description: "Number of completed downlods for current torrent, as reported by the tracker", + return_type: Number( + TypeInfo { + name: "num_downloaded", + is_optional: false, + is_list: false, + description: Some( + "Number of completed downlods for current torrent, as reported by the tracker", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "msg", + description: "Tracker message (there is no way of knowing what this message is - it's up to tracker admins)", + return_type: String( + TypeInfo { + name: "msg", + is_optional: false, + is_list: false, + description: Some( + "Tracker message (there is no way of knowing what this message is - it's up to tracker admins)", + ), + type_description: None, + }, + ), + }, + ], + }, + ), + url: "trackers", + }, + ApiMethod { + name: "webseeds", + description: Some( + "Example:\n\n```JSON\n[\n {\n \"url\":\"http://some_url/\"\n },\n {\n \"url\":\"http://some_other_url/\"\n }\n]\n```", + ), + parameters: Some( + [ + String( + TypeInfo { + name: "hash", + is_optional: false, + is_list: false, + description: Some( + "The hash of the torrent you want to get the webseeds of", + ), + type_description: None, + }, + ), + ], + ), + return_type: Some( + ReturnType { + is_list: true, + parameters: [ + ReturnTypeParameter { + name: "url", + description: "URL of the web seed", + return_type: String( + TypeInfo { + name: "url", + is_optional: false, + is_list: false, + description: Some( + "URL of the web seed", + ), + type_description: None, + }, + ), + }, + ], + }, + ), + url: "webseeds", + }, + ApiMethod { + name: "files", + description: Some( + "- empty, if the torrent hash is invalid\n- otherwise, a JSON array, where each element contains info about one file, with the following fields\n\nPossible values of `priority`:\n\nExample:\n\n```JSON\n\n[\n {\n \"index\":0,\n \"is_seed\":false,\n \"name\":\"debian-8.1.0-amd64-CD-1.iso\",\n \"piece_range\":[0,1253],\n \"priority\":1,\n \"progress\":0,\n \"size\":657457152,\n \"availability\":0.5,\n }\n]\n```", + ), + parameters: Some( + [ + String( + TypeInfo { + name: "hash", + is_optional: false, + is_list: false, + description: Some( + "The hash of the torrent you want to get the contents of", + ), + type_description: None, + }, + ), + String( + TypeInfo { + name: "indexes", + is_optional: true, + is_list: false, + description: Some( + "The indexes of the files you want to retrieve. indexes can contain multiple values separated by \\", + ), + type_description: None, + }, + ), + ], + ), + return_type: None, + url: "files", + }, + ApiMethod { + name: "pieceStates", + description: Some( + "- empty, if the torrent hash is invalid\n- otherwise, an array of states (integers) of all pieces (in order) of a specific torrent.\n\nValue meanings are defined as below:\n\nExample:\n\n```JSON\n[0,0,2,1,0,0,2,1]\n```", + ), + parameters: Some( + [ + String( + TypeInfo { + name: "hash", + is_optional: false, + is_list: false, + description: Some( + "The hash of the torrent you want to get the pieces' states of", + ), + type_description: None, + }, + ), + ], + ), + return_type: None, + url: "pieceStates", + }, + ApiMethod { + name: "pieceHashes", + description: Some( + "- empty, if the torrent hash is invalid\n- otherwise, an array of hashes (strings) of all pieces (in order) of a specific torrent.\n\nExample:\n\n```JSON\n[\"54eddd830a5b58480a6143d616a97e3a6c23c439\",\"f8a99d225aa4241db100f88407fc3bdaead583ab\",\"928fb615b9bd4dd8f9e9022552c8f8f37ef76f58\"]\n```", + ), + parameters: Some( + [ + String( + TypeInfo { + name: "hash", + is_optional: false, + is_list: false, + description: Some( + "The hash of the torrent you want to get the pieces' hashes of", + ), + type_description: None, + }, + ), + ], + ), + return_type: None, + url: "pieceHashes", + }, + ApiMethod { + name: "pause", + description: None, + parameters: Some( + [ + String( + TypeInfo { + name: "hashes", + is_optional: false, + is_list: false, + description: Some( + "The hashes of the torrents you want to pause. hashes can contain multiple hashes separated by \\", + ), + type_description: None, + }, + ), + ], + ), + return_type: None, + url: "pause", + }, + ApiMethod { + name: "resume", + description: None, + parameters: Some( + [ + String( + TypeInfo { + name: "hashes", + is_optional: false, + is_list: false, + description: Some( + "The hashes of the torrents you want to resume. hashes can contain multiple hashes separated by \\", + ), + type_description: None, + }, + ), + ], + ), + return_type: None, + url: "resume", + }, + ApiMethod { + name: "delete", + description: None, + parameters: Some( + [ + String( + TypeInfo { + name: "hashes", + is_optional: false, + is_list: false, + description: Some( + "The hashes of the torrents you want to delete. hashes can contain multiple hashes separated by \\", + ), + type_description: None, + }, + ), + ], + ), + return_type: None, + url: "delete", + }, + ApiMethod { + name: "recheck", + description: None, + parameters: Some( + [ + String( + TypeInfo { + name: "hashes", + is_optional: false, + is_list: false, + description: Some( + "The hashes of the torrents you want to recheck. hashes can contain multiple hashes separated by \\", + ), + type_description: None, + }, + ), + ], + ), + return_type: None, + url: "recheck", + }, + ApiMethod { + name: "reannounce", + description: None, + parameters: Some( + [ + String( + TypeInfo { + name: "hashes", + is_optional: false, + is_list: false, + description: Some( + "The hashes of the torrents you want to reannounce. hashes can contain multiple hashes separated by \\", + ), + type_description: None, + }, + ), + ], + ), + return_type: None, + url: "reannounce", + }, + ApiMethod { + name: "add", + description: None, + parameters: Some( + [ + String( + TypeInfo { + name: "urls", + is_optional: false, + is_list: false, + description: Some( + "URLs separated with newlines", + ), + type_description: None, + }, + ), + String( + TypeInfo { + name: "savepath", + is_optional: true, + is_list: false, + description: Some( + "Download folder", + ), + type_description: None, + }, + ), + String( + TypeInfo { + name: "cookie", + is_optional: true, + is_list: false, + description: Some( + "Cookie sent to download the .torrent file", + ), + type_description: None, + }, + ), + String( + TypeInfo { + name: "category", + is_optional: true, + is_list: false, + description: Some( + "Category for the torrent", + ), + type_description: None, + }, + ), + String( + TypeInfo { + name: "tags", + is_optional: true, + is_list: false, + description: Some( + "Tags for the torrent, split by ','", + ), + type_description: None, + }, + ), + String( + TypeInfo { + name: "skip_checking", + is_optional: true, + is_list: false, + description: Some( + "Skip hash checking. Possible values are true, false (default)", + ), + type_description: None, + }, + ), + String( + TypeInfo { + name: "paused", + is_optional: true, + is_list: false, + description: Some( + "Add torrents in the paused state. Possible values are true, false (default)", + ), + type_description: None, + }, + ), + String( + TypeInfo { + name: "root_folder", + is_optional: true, + is_list: false, + description: Some( + "Create the root folder. Possible values are true, false, unset (default)", + ), + type_description: None, + }, + ), + String( + TypeInfo { + name: "rename", + is_optional: true, + is_list: false, + description: Some( + "Rename torrent", + ), + type_description: None, + }, + ), + Number( + TypeInfo { + name: "upLimit", + is_optional: true, + is_list: false, + description: Some( + "Set torrent upload speed limit. Unit in bytes/second", + ), + type_description: None, + }, + ), + Number( + TypeInfo { + name: "dlLimit", + is_optional: true, + is_list: false, + description: Some( + "Set torrent download speed limit. Unit in bytes/second", + ), + type_description: None, + }, + ), + Float( + TypeInfo { + name: "ratioLimit", + is_optional: true, + is_list: false, + description: Some( + "Set torrent share ratio limit", + ), + type_description: None, + }, + ), + Number( + TypeInfo { + name: "seedingTimeLimit", + is_optional: true, + is_list: false, + description: Some( + "Set torrent seeding time limit. Unit in seconds", + ), + type_description: None, + }, + ), + Bool( + TypeInfo { + name: "autoTMM", + is_optional: true, + is_list: false, + description: Some( + "Whether Automatic Torrent Management should be used", + ), + type_description: None, + }, + ), + String( + TypeInfo { + name: "sequentialDownload", + is_optional: true, + is_list: false, + description: Some( + "Enable sequential download. Possible values are true, false (default)", + ), + type_description: None, + }, + ), + String( + TypeInfo { + name: "firstLastPiecePrio", + is_optional: true, + is_list: false, + description: Some( + "Prioritize download first last piece. Possible values are true, false (default)", + ), + type_description: None, + }, + ), + ], + ), + return_type: None, + url: "add", + }, + ApiMethod { + name: "addTrackers", + description: None, + parameters: None, + return_type: None, + url: "addTrackers", + }, + ApiMethod { + name: "editTracker", + description: None, + parameters: Some( + [ + String( + TypeInfo { + name: "hash", + is_optional: false, + is_list: false, + description: Some( + "The hash of the torrent", + ), + type_description: None, + }, + ), + String( + TypeInfo { + name: "origUrl", + is_optional: false, + is_list: false, + description: Some( + "The tracker URL you want to edit", + ), + type_description: None, + }, + ), + String( + TypeInfo { + name: "newUrl", + is_optional: false, + is_list: false, + description: Some( + "The new URL to replace the origUrl", + ), + type_description: None, + }, + ), + ], + ), + return_type: None, + url: "editTracker", + }, + ApiMethod { + name: "removeTrackers", + description: None, + parameters: Some( + [ + String( + TypeInfo { + name: "hash", + is_optional: false, + is_list: false, + description: Some( + "The hash of the torrent", + ), + type_description: None, + }, + ), + String( + TypeInfo { + name: "urls", + is_optional: false, + is_list: false, + description: Some( + "URLs to remove, separated by \\", + ), + type_description: None, + }, + ), + ], + ), + return_type: None, + url: "removeTrackers", + }, + ApiMethod { + name: "addPeers", + description: None, + parameters: Some( + [ + String( + TypeInfo { + name: "hashes", + is_optional: false, + is_list: false, + description: Some( + "The hash of the torrent, or multiple hashes separated by a pipe \\", + ), + type_description: None, + }, + ), + String( + TypeInfo { + name: "peers", + is_optional: false, + is_list: false, + description: Some( + "The peer to add, or multiple peers separated by a pipe \\", + ), + type_description: None, + }, + ), + ], + ), + return_type: None, + url: "addPeers", + }, + ApiMethod { + name: "increasePrio", + description: None, + parameters: Some( + [ + String( + TypeInfo { + name: "hashes", + is_optional: false, + is_list: false, + description: Some( + "The hashes of the torrents you want to increase the priority of. hashes can contain multiple hashes separated by \\", + ), + type_description: None, + }, + ), + ], + ), + return_type: None, + url: "increasePrio", + }, + ApiMethod { + name: "decreasePrio", + description: None, + parameters: Some( + [ + String( + TypeInfo { + name: "hashes", + is_optional: false, + is_list: false, + description: Some( + "The hashes of the torrents you want to decrease the priority of. hashes can contain multiple hashes separated by \\", + ), + type_description: None, + }, + ), + ], + ), + return_type: None, + url: "decreasePrio", + }, + ApiMethod { + name: "topPrio", + description: None, + parameters: Some( + [ + String( + TypeInfo { + name: "hashes", + is_optional: false, + is_list: false, + description: Some( + "The hashes of the torrents you want to set to the maximum priority. hashes can contain multiple hashes separated by \\", + ), + type_description: None, + }, + ), + ], + ), + return_type: None, + url: "topPrio", + }, + ApiMethod { + name: "bottomPrio", + description: None, + parameters: Some( + [ + String( + TypeInfo { + name: "hashes", + is_optional: false, + is_list: false, + description: Some( + "The hashes of the torrents you want to set to the minimum priority. hashes can contain multiple hashes separated by \\", + ), + type_description: None, + }, + ), + ], + ), + return_type: None, + url: "bottomPrio", + }, + ApiMethod { + name: "filePrio", + description: None, + parameters: Some( + [ + String( + TypeInfo { + name: "hash", + is_optional: false, + is_list: false, + description: Some( + "The hash of the torrent", + ), + type_description: None, + }, + ), + String( + TypeInfo { + name: "id", + is_optional: false, + is_list: false, + description: Some( + "File ids, separated by \\", + ), + type_description: None, + }, + ), + Number( + TypeInfo { + name: "priority", + is_optional: false, + is_list: false, + description: Some( + "File priority to set (consult [torrent contents API](#get-torrent-contents) for possible values)", + ), + type_description: None, + }, + ), + ], + ), + return_type: None, + url: "filePrio", + }, + ApiMethod { + name: "downloadLimit", + description: None, + parameters: None, + return_type: None, + url: "downloadLimit", + }, + ApiMethod { + name: "setShareLimits", + description: None, + parameters: None, + return_type: None, + url: "setShareLimits", + }, + ApiMethod { + name: "uploadLimit", + description: None, + parameters: None, + return_type: None, + url: "uploadLimit", + }, + ApiMethod { + name: "setUploadLimit", + description: None, + parameters: None, + return_type: None, + url: "setUploadLimit", + }, + ApiMethod { + name: "setLocation", + description: None, + parameters: None, + return_type: None, + url: "setLocation", + }, + ApiMethod { + name: "rename", + description: None, + parameters: None, + return_type: None, + url: "rename", + }, + ApiMethod { + name: "setCategory", + description: None, + parameters: None, + return_type: None, + url: "setCategory", + }, + ApiMethod { + name: "categories", + description: None, + parameters: None, + return_type: None, + url: "categories", + }, + ApiMethod { + name: "createCategory", + description: None, + parameters: None, + return_type: None, + url: "createCategory", + }, + ApiMethod { + name: "editCategory", + description: None, + parameters: None, + return_type: None, + url: "editCategory", + }, + ApiMethod { + name: "removeCategories", + description: None, + parameters: None, + return_type: None, + url: "removeCategories", + }, + ApiMethod { + name: "addTags", + description: None, + parameters: None, + return_type: None, + url: "addTags", + }, + ApiMethod { + name: "removeTags", + description: None, + parameters: None, + return_type: None, + url: "removeTags", + }, + ApiMethod { + name: "tags", + description: None, + parameters: None, + return_type: None, + url: "tags", + }, + ApiMethod { + name: "createTags", + description: None, + parameters: None, + return_type: None, + url: "createTags", + }, + ApiMethod { + name: "deleteTags", + description: None, + parameters: None, + return_type: None, + url: "deleteTags", + }, + ApiMethod { + name: "setAutoManagement", + description: None, + parameters: None, + return_type: None, + url: "setAutoManagement", + }, + ApiMethod { + name: "toggleSequentialDownload", + description: None, + parameters: Some( + [ + String( + TypeInfo { + name: "hashes", + is_optional: false, + is_list: false, + description: Some( + "The hashes of the torrents you want to toggle sequential download for. hashes can contain multiple hashes separated by \\", + ), + type_description: None, + }, + ), + ], + ), + return_type: None, + url: "toggleSequentialDownload", + }, + ApiMethod { + name: "toggleFirstLastPiecePrio", + description: None, + parameters: Some( + [ + String( + TypeInfo { + name: "hashes", + is_optional: false, + is_list: false, + description: Some( + "The hashes of the torrents you want to toggle the first/last piece priority for. hashes can contain multiple hashes separated by \\", + ), + type_description: None, + }, + ), + ], + ), + return_type: None, + url: "toggleFirstLastPiecePrio", + }, + ApiMethod { + name: "setForceStart", + description: None, + parameters: None, + return_type: None, + url: "setForceStart", + }, + ApiMethod { + name: "setSuperSeeding", + description: None, + parameters: None, + return_type: None, + url: "setSuperSeeding", + }, + ApiMethod { + name: "renameFile", + description: None, + parameters: Some( + [ + String( + TypeInfo { + name: "hash", + is_optional: false, + is_list: false, + description: Some( + "The hash of the torrent", + ), + type_description: None, + }, + ), + String( + TypeInfo { + name: "oldPath", + is_optional: false, + is_list: false, + description: Some( + "The old path of the torrent", + ), + type_description: None, + }, + ), + String( + TypeInfo { + name: "newPath", + is_optional: false, + is_list: false, + description: Some( + "The new path to use for the file", + ), + type_description: None, + }, + ), + ], + ), + return_type: None, + url: "renameFile", + }, + ApiMethod { + name: "renameFolder", + description: None, + parameters: Some( + [ + String( + TypeInfo { + name: "hash", + is_optional: false, + is_list: false, + description: Some( + "The hash of the torrent", + ), + type_description: None, + }, + ), + String( + TypeInfo { + name: "oldPath", + is_optional: false, + is_list: false, + description: Some( + "The old path of the torrent", + ), + type_description: None, + }, + ), + String( + TypeInfo { + name: "newPath", + is_optional: false, + is_list: false, + description: Some( + "The new path to use for the file", + ), + type_description: None, + }, + ), + ], + ), + return_type: None, + url: "renameFolder", + }, + ], + description: Some( + "All Torrent management API methods are under \"torrents\", e.g.: `/api/v2/torrents/methodName`.", + ), + url: "torrents", + }, + ApiGroup { + name: "rss", + methods: [ + ApiMethod { + name: "addFolder", + description: None, + parameters: Some( + [ + String( + TypeInfo { + name: "path", + is_optional: false, + is_list: false, + description: Some( + "Full path of added folder (e.g. \"The Pirate Bay\\Top100\")", + ), + type_description: None, + }, + ), + ], + ), + return_type: None, + url: "addFolder", + }, + ApiMethod { + name: "addFeed", + description: None, + parameters: Some( + [ + String( + TypeInfo { + name: "url", + is_optional: false, + is_list: false, + description: Some( + "URL of RSS feed (e.g. \"[http://thepiratebay.org/rss//top100/200](http://thepiratebay.org/rss//top100/200)\")", + ), + type_description: None, + }, + ), + String( + TypeInfo { + name: "path", + is_optional: true, + is_list: false, + description: Some( + "Full path of added folder (e.g. \"The Pirate Bay\\Top100\\Video\")", + ), + type_description: None, + }, + ), + ], + ), + return_type: None, + url: "addFeed", + }, + ApiMethod { + name: "removeItem", + description: None, + parameters: Some( + [ + String( + TypeInfo { + name: "path", + is_optional: false, + is_list: false, + description: Some( + "Full path of removed item (e.g. \"The Pirate Bay\\Top100\")", + ), + type_description: None, + }, + ), + ], + ), + return_type: None, + url: "removeItem", + }, + ApiMethod { + name: "moveItem", + description: None, + parameters: Some( + [ + String( + TypeInfo { + name: "itemPath", + is_optional: false, + is_list: false, + description: Some( + "Current full path of item (e.g. \"The Pirate Bay\\Top100\")", + ), + type_description: None, + }, + ), + String( + TypeInfo { + name: "destPath", + is_optional: false, + is_list: false, + description: Some( + "New full path of item (e.g. \"The Pirate Bay\")", + ), + type_description: None, + }, + ), + ], + ), + return_type: None, + url: "moveItem", + }, + ApiMethod { + name: "items", + description: None, + parameters: Some( + [ + Bool( + TypeInfo { + name: "withData", + is_optional: true, + is_list: false, + description: Some( + "True if you need current feed articles", + ), + type_description: None, + }, + ), + ], + ), + return_type: None, + url: "items", + }, + ApiMethod { + name: "markAsRead", + description: None, + parameters: Some( + [ + String( + TypeInfo { + name: "itemPath", + is_optional: false, + is_list: false, + description: Some( + "Current full path of item (e.g. \"The Pirate Bay\\Top100\")", + ), + type_description: None, + }, + ), + String( + TypeInfo { + name: "articleId", + is_optional: true, + is_list: false, + description: Some( + "ID of article", + ), + type_description: None, + }, + ), + ], + ), + return_type: None, + url: "markAsRead", + }, + ApiMethod { + name: "refreshItem", + description: None, + parameters: Some( + [ + String( + TypeInfo { + name: "itemPath", + is_optional: false, + is_list: false, + description: Some( + "Current full path of item (e.g. \"The Pirate Bay\\Top100\")", + ), + type_description: None, + }, + ), + ], + ), + return_type: None, + url: "refreshItem", + }, + ApiMethod { + name: "setRule", + description: None, + parameters: Some( + [ + String( + TypeInfo { + name: "ruleName", + is_optional: false, + is_list: false, + description: Some( + "Rule name (e.g. \"Punisher\")", + ), + type_description: None, + }, + ), + String( + TypeInfo { + name: "ruleDef", + is_optional: false, + is_list: false, + description: Some( + "JSON encoded rule definition", + ), + type_description: None, + }, + ), + ], + ), + return_type: None, + url: "setRule", + }, + ApiMethod { + name: "renameRule", + description: None, + parameters: Some( + [ + String( + TypeInfo { + name: "ruleName", + is_optional: false, + is_list: false, + description: Some( + "Rule name (e.g. \"Punisher\")", + ), + type_description: None, + }, + ), + String( + TypeInfo { + name: "newRuleName", + is_optional: false, + is_list: false, + description: Some( + "New rule name (e.g. \"The Punisher\")", + ), + type_description: None, + }, + ), + ], + ), + return_type: None, + url: "renameRule", + }, + ApiMethod { + name: "removeRule", + description: None, + parameters: Some( + [ + String( + TypeInfo { + name: "ruleName", + is_optional: false, + is_list: false, + description: Some( + "Rule name (e.g. \"Punisher\")", + ), + type_description: None, + }, + ), + ], + ), + return_type: None, + url: "removeRule", + }, + ApiMethod { + name: "rules", + description: None, + parameters: None, + return_type: None, + url: "rules", + }, + ApiMethod { + name: "matchingArticles", + description: None, + parameters: None, + return_type: None, + url: "matchingArticles", + }, + ], + description: Some( + "All RSS API methods are under \"rss\", e.g.: `/api/v2/rss/methodName`.", + ), + url: "rss", + }, + ApiGroup { + name: "search", + methods: [ + ApiMethod { + name: "start", + description: Some( + "Example:\n\n```JSON\n{\n \"id\": 12345\n}\n```", + ), + parameters: Some( + [ + String( + TypeInfo { + name: "pattern", + is_optional: false, + is_list: false, + description: Some( + "Pattern to search for (e.g. \"Ubuntu 18.04\")", + ), + type_description: None, + }, + ), + String( + TypeInfo { + name: "plugins", + is_optional: false, + is_list: false, + description: Some( + "Plugins to use for searching (e.g. \"legittorrents\"). Supports multiple plugins separated by \\", + ), + type_description: None, + }, + ), + String( + TypeInfo { + name: "category", + is_optional: false, + is_list: false, + description: Some( + "Categories to limit your search to (e.g. \"legittorrents\"). Available categories depend on the specified plugins. Also supports all", + ), + type_description: None, + }, + ), + ], + ), + return_type: Some( + ReturnType { + is_list: false, + parameters: [ + ReturnTypeParameter { + name: "id", + description: "ID of the search job", + return_type: Number( + TypeInfo { + name: "id", + is_optional: false, + is_list: false, + description: Some( + "ID of the search job", + ), + type_description: None, + }, + ), + }, + ], + }, + ), + url: "start", + }, + ApiMethod { + name: "stop", + description: None, + parameters: Some( + [ + Number( + TypeInfo { + name: "id", + is_optional: false, + is_list: false, + description: Some( + "ID of the search job", + ), + type_description: None, + }, + ), + ], + ), + return_type: None, + url: "stop", + }, + ApiMethod { + name: "status", + description: Some( + "Example:\n\n```JSON\n[\n {\n \"id\": 12345,\n \"status\": \"Running\",\n \"total\": 170\n }\n]\n```", + ), + parameters: Some( + [ + Number( + TypeInfo { + name: "id", + is_optional: true, + is_list: false, + description: Some( + "ID of the search job. If not specified, all search jobs are returned", + ), + type_description: None, + }, + ), + ], + ), + return_type: Some( + ReturnType { + is_list: true, + parameters: [ + ReturnTypeParameter { + name: "id", + description: "ID of the search job", + return_type: Number( + TypeInfo { + name: "id", + is_optional: false, + is_list: false, + description: Some( + "ID of the search job", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "status", + description: "Current status of the search job (either Running or Stopped)", + return_type: String( + TypeInfo { + name: "status", + is_optional: false, + is_list: false, + description: Some( + "Current status of the search job (either Running or Stopped)", + ), + type_description: None, + }, + ), + }, + ReturnTypeParameter { + name: "total", + description: "Total number of results. If the status is Running this number may contineu to increase", + return_type: Number( + TypeInfo { + name: "total", + is_optional: false, + is_list: false, + description: Some( + "Total number of results. If the status is Running this number may contineu to increase", + ), + type_description: None, + }, + ), + }, + ], + }, + ), + url: "status", + }, + ApiMethod { + name: "results", + description: None, + parameters: Some( + [ + Number( + TypeInfo { + name: "id", + is_optional: false, + is_list: false, + description: Some( + "ID of the search job", + ), + type_description: None, + }, + ), + Number( + TypeInfo { + name: "limit", + is_optional: true, + is_list: false, + description: Some( + "max number of results to return. 0 or negative means no limit", + ), + type_description: None, + }, + ), + Number( + TypeInfo { + name: "offset", + is_optional: true, + is_list: false, + description: Some( + "result to start at. A negative number means count backwards (e.g. -2 returns the 2 most recent results)", + ), + type_description: None, + }, + ), + ], + ), + return_type: Some( + ReturnType { + is_list: false, + parameters: [], + }, + ), + url: "results", + }, + ], + description: Some( + "All Search API methods are under \"search\", e.g.: `/api/v2/search/methodName`.", + ), + url: "search", + }, +] \ No newline at end of file diff --git a/parser/src/api-4_1.md b/parser/src/api-4_1.md new file mode 100644 index 0000000..cf94502 --- /dev/null +++ b/parser/src/api-4_1.md @@ -0,0 +1,3345 @@ +This WebUI API documentation applies to qBittorrent v4.1+. For other WebUI API versions, visit [WebUI API](https://github.com/qbittorrent/qBittorrent/wiki#WebUI-API). + +# Table of Contents # + +1. [Changes](#changes) + 1. [API v2.0](#api-v20) + 1. [API v2.0.1](#api-v201) + 1. [API v2.0.2](#api-v202) + 1. [API v2.1.0](#api-v210) + 1. [API v2.1.1](#api-v211) + 1. [API v2.2.0](#api-v220) + 1. [API v2.2.1](#api-v221) + 1. [API v2.3.0](#api-v230) + 1. [API v2.4.0](#api-v240) + 1. [API v2.4.1](#api-v241) + 1. [API v2.5.0](#api-v250) + 1. [API v2.5.1](#api-v251) + 1. [API v2.6.0](#api-v260) + 1. [API v2.6.1](#api-v261) + 1. [API v2.6.2](#api-v262) + 1. [API v2.7.0](#api-v270) + 1. [API v2.8.0](#api-v280) + 1. [API v2.8.1](#api-v281) + 1. [API v2.8.2](#api-v282) + 1. [API v2.8.3](#api-v283) +1. [General information](#general-information) +1. [Authentication](#authentication) + 1. [Login](#login) + 1. [Logout](#logout) +1. [Application](#application) + 1. [Get application version](#get-application-version) + 1. [Get API version](#get-api-version) + 1. [Get build info](#get-build-info) + 1. [Shutdown application](#shutdown-application) + 1. [Get application preferences](#get-application-preferences) + 1. [Set application preferences](#set-application-preferences) + 1. [Get default save path](#get-default-save-path) +1. [Log](#log) + 1. [Get log](#get-log) + 1. [Get peer log](#get-peer-log) +1. [Sync](#sync) + 1. [Get main data](#get-main-data) + 1. [Get torrent peers data](#get-torrent-peers-data) +1. [Transfer info](#transfer-info) + 1. [Get global transfer info](#get-global-transfer-info) + 1. [Get alternative speed limits state](#get-alternative-speed-limits-state) + 1. [Toggle alternative speed limits](#toggle-alternative-speed-limits) + 1. [Get global download limit](#get-global-download-limit) + 1. [Set global download limit](#set-global-download-limit) + 1. [Get global upload limit](#get-global-upload-limit) + 1. [Set global upload limit](#set-global-upload-limit) + 1. [Ban peers](#ban-peers) +1. [Torrent management](#torrent-management) + 1. [Get torrent list](#get-torrent-list) + 1. [Get torrent generic properties](#get-torrent-generic-properties) + 1. [Get torrent trackers](#get-torrent-trackers) + 1. [Get torrent web seeds](#get-torrent-web-seeds) + 1. [Get torrent contents](#get-torrent-contents) + 1. [Get torrent pieces' states](#get-torrent-pieces-states) + 1. [Get torrent pieces' hashes](#get-torrent-pieces-hashes) + 1. [Pause torrents](#pause-torrents) + 1. [Resume torrents](#resume-torrents) + 1. [Delete torrents](#delete-torrents) + 1. [Recheck torrents](#recheck-torrents) + 1. [Reannounce torrents](#reannounce-torrents) + 1. [Edit trackers](#edit-trackers) + 1. [Remove trackers](#remove-trackers) + 1. [Add peers](#add-peers) + 1. [Add new torrent](#add-new-torrent) + 1. [Add trackers to torrent](#add-trackers-to-torrent) + 1. [Increase torrent priority](#increase-torrent-priority) + 1. [Decrease torrent priority](#decrease-torrent-priority) + 1. [Maximal torrent priority](#maximal-torrent-priority) + 1. [Minimal torrent priority](#minimal-torrent-priority) + 1. [Set file priority](#set-file-priority) + 1. [Get torrent download limit](#get-torrent-download-limit) + 1. [Set torrent download limit](#set-torrent-download-limit) + 1. [Set torrent share limit](#set-torrent-share-limit) + 1. [Get torrent upload limit](#get-torrent-upload-limit) + 1. [Set torrent upload limit](#set-torrent-upload-limit) + 1. [Set torrent location](#set-torrent-location) + 1. [Set torrent name](#set-torrent-name) + 1. [Set torrent category](#set-torrent-category) + 1. [Get all categories](#get-all-categories) + 1. [Add new category](#add-new-category) + 1. [Edit category](#edit-category) + 1. [Remove categories](#remove-categories) + 1. [Add torrent tags](#add-torrent-tags) + 1. [Remove torrent tags](#remove-torrent-tags) + 1. [Get all tags](#get-all-tags) + 1. [Create tags](#create-tags) + 1. [Delete tags](#delete-tags) + 1. [Set automatic torrent management](#set-automatic-torrent-management) + 1. [Toggle sequential download](#toggle-sequential-download) + 1. [Set first/last piece priority](#set-firstlast-piece-priority) + 1. [Set force start](#set-force-start) + 1. [Set super seeding](#set-super-seeding) + 1. [Rename file](#rename-file) + 1. [Rename folder](#rename-folder) +1. [RSS (experimental)](#rss-experimental) + 1. [Add folder](#add-folder) + 1. [Add feed](#add-feed) + 1. [Remove item](#remove-item) + 1. [Move item](#move-item) + 1. [Get all items](#get-all-items) + 1. [Mark as read](#mark-as-read) + 1. [Refresh item](#refresh-item) + 1. [Set auto-downloading rule](#set-auto-downloading-rule) + 1. [Rename auto-downloading rule](#rename-auto-downloading-rule) + 1. [Remove auto-downloading rule](#remove-auto-downloading-rule) + 1. [Get all auto-downloading rules](#get-all-auto-downloading-rules) + 1. [Get all articles matching a rule](#get-all-articles-matching-a-rule) +1. [Search](#search) + 1. [Start search](#start-search) + 1. [Stop search](#stop-search) + 1. [Get search status](#get-search-status) + 1. [Get search results](#get-search-results) + 1. [Delete search](#delete-search) + 1. [Get search plugins](#get-search-plugins) + 1. [Install search plugin](#install-search-plugin) + 1. [Uninstall search plugin](#uninstall-search-plugin) + 1. [Enable search plugin](#enable-search-plugin) + 1. [Update search plugins](#update-search-plugins) +1. [WebAPI versioning](#webapi-versioning) + +*** + +# Changes # + +## API v2.0 ## + +- New version naming scheme: X.Y.Z (where X - major version, Y - minor version, Z - release version) +- New API paths. All API methods are under `api/vX/` (where X is API major version) +- API methods are under new scopes + +## API v2.0.1 ## + +- Add `hashes` field to `/torrents/info` ([#8782](https://github.com/qbittorrent/qBittorrent/pull/8782)) +- Add `/torrents/setShareLimits/` method ([#8598](https://github.com/qbittorrent/qBittorrent/pull/8598)) + +## API v2.0.2 ## + +- Add `/torrents/reannounce` method ([#9229](https://github.com/qbittorrent/qBittorrent/pull/9229)) + +## API v2.1.0 ## + +- Change `/sync/maindata` `categories` field from `array` to `object` ([#9228](https://github.com/qbittorrent/qBittorrent/pull/9228)) +- Add `savePath` field to `/torrents/createCategory` ([#9228](https://github.com/qbittorrent/qBittorrent/pull/9228)). This method now requires the category to already exist and will not create new categories. +- Add `/torrents/editCategory` method ([#9228](https://github.com/qbittorrent/qBittorrent/pull/9228)) + +## API v2.1.1 ## + +- Add `/torrents/categories` method ([#9586](https://github.com/qbittorrent/qBittorrent/pull/9586)) +- Add `/search/` methods ([#8584](https://github.com/qbittorrent/qBittorrent/pull/8584)) +- Add `free_space_on_disk` field to `/sync/maindata` ([#8217](https://github.com/qbittorrent/qBittorrent/pull/8217)) + +## API v2.2.0 ## + +- Add `/torrents/editTracker` and `/torrents/removeTracker` methods ([#9375](https://github.com/qbittorrent/qBittorrent/pull/9375)) +- Add `tier`, `num_seeds`, `num_leeches`, and `num_downloaded` fields to `/torrents/trackers` ([#9375](https://github.com/qbittorrent/qBittorrent/pull/9375)) +- Change `status` field from translated string to an integer for `/torrents/trackers` ([#9375](https://github.com/qbittorrent/qBittorrent/pull/9375)) +- Change `/torrents/filePrio` `id` field to accept multiple ids ([#9541](https://github.com/qbittorrent/qBittorrent/pull/9541)) +- Throw additional errors for failed requests to `/torrents/filePrio` ([#9541](https://github.com/qbittorrent/qBittorrent/pull/9541)) +- Add `autoTMM` field to `/torrents/add` ([#9752](https://github.com/qbittorrent/qBittorrent/pull/9752)) +- Add various fields to `/app/getPreferences` and `/app/setPreferences` (`create_subfolder_enabled`, `start_paused_enabled`, `auto_delete_mode`, `preallocate_all`, `incomplete_files_ext`, `auto_tmm_enabled`, `torrent_changed_tmm_enabled`, `save_path_changed_tmm_enabled`, `category_changed_tmm_enabled`, `mail_notification_sender`, `limit_lan_peers`, `slow_torrent_dl_rate_threshold`, `slow_torrent_ul_rate_threshold`, `slow_torrent_inactive_timer`, `alternative_webui_enabled`, `alternative_webui_path`) ([#9752](https://github.com/qbittorrent/qBittorrent/pull/9752)) + +## API v2.2.1 ## + +- Add `rss/refreshItem` ([#11067](https://github.com/qbittorrent/qBittorrent/pull/11067)) + +## API v2.3.0 ## + +- Remove `web_ui_password` field from `/app/preferences`, this field is still writable in `/app/setPreferences` method ([#9942](https://github.com/qbittorrent/qBittorrent/pull/9942)) +- Add `/app/buildInfo` method ([#10096](https://github.com/qbittorrent/qBittorrent/pull/10096)) +- Always use `/` as path separator in `/torrents/files` response ([#10153](https://github.com/qbittorrent/qBittorrent/pull/10153/)) +- Add `/torrents/addPeers` and `/transfer/banPeers` methods ([#10158](https://github.com/qbittorrent/qBittorrent/pull/10158)) +- Add `/torrents/addTags`, `/torrents/removeTags`, `/torrents/tags`, `/torrents/createTags`, `/torrents/deleteTags` methods ([#10527](https://github.com/qbittorrent/qBittorrent/pull/10527)) + +## API v2.4.0 ## + +- Add `/torrents/renameFile` method ([#11029](https://github.com/qbittorrent/qBittorrent/pull/11029)) + +## API v2.4.1 ## + +- Add `stalled`, `stalled_uploading` and `stalled_downloading` as possible values for the `filter` parameter in `/torrents/info` ([#11825](https://github.com/qbittorrent/qBittorrent/pull/11825)) +- Add various fields to `/app/preferences` and `/app/setPreferences` (`piece_extent_affinity`, `web_ui_secure_cookie_enabled`, `web_ui_max_auth_fail_count`, `web_ui_ban_duration`, `stop_tracker_timeout`) ([#11781](https://github.com/qbittorrent/qBittorrent/pull/11781), [#11726](https://github.com/qbittorrent/qBittorrent/pull/11726), [#12004](https://github.com/qbittorrent/qBittorrent/pull/12004), [#11834](https://github.com/qbittorrent/qBittorrent/pull/11834)) + +## API v2.5.0 ## +- Removes `enable_super_seeding` as fields from `/app/preferences` and `/app/setPreferences` ([#12423](https://github.com/qbittorrent/qBittorrent/pull/12423)) + +## API v2.5.1 ## +- Add `web_ui_use_custom_http_headers_enabled`, `web_ui_custom_http_headers`, `rss_download_repack_proper_episodes` and `rss_smart_episode_filters` as fields to `/app/preferences` and `/app/setPreferences` ([#12579](https://github.com/qbittorrent/qBittorrent/pull/12579), [#12549](https://github.com/qbittorrent/qBittorrent/pull/12549)) +- Add `/rss/markAsRead` and `/rss/matchingArticles` methods ([#12549](https://github.com/qbittorrent/qBittorrent/pull/12549)) + +## API v2.6.0 ## +- Removed `/search/categories` method and modified `/search/plugins` method's response ([#12705](https://github.com/qbittorrent/qBittorrent/pull/12705)) + +## API v2.6.1 ## +- Exposed `contentPath` via the `content_path` field in the response to `/torrents/info` ([#13625](https://github.com/qbittorrent/qBittorrent/pull/13625)) + +## API v2.6.2 ## +- Added `tags` optional field to `/torrents/add` ([#13882](https://github.com/qbittorrent/qBittorrent/pull/13882)) + +## API v2.8.0 ## +- Added `/torrents/renameFolder` method and modified `/torrents/renameFile` method's parameters ([#13995](https://github.com/qbittorrent/qBittorrent/pull/13995)) + +Note that this change was released in qBittorrent v4.3.3, but the WebAPI version was incorrectly set to v2.7.0 (see [#14275](https://github.com/qbittorrent/qBittorrent/pull/14275#issuecomment-766310862) for details) + +## API v2.8.1 ## +- Added `ratioLimit` and `seedingTimeLimit` optional fields to `/torrents/add` ([#14519](https://github.com/qbittorrent/qBittorrent/pull/14519)) +- Added `seeding_time` field to `/torrents/info` ([#14554](https://github.com/qbittorrent/qBittorrent/pull/14554)) + +## API v2.8.2 ## +- Added `indexes` optional parameter to `/torrents/files` ([#14795](https://github.com/qbittorrent/qBittorrent/pull/14795)) +- Added `index` field to `/torrents/files` response ([#14795](https://github.com/qbittorrent/qBittorrent/pull/14795)) + +## API v2.8.3 ## +- Added `tag` optional parameter to `/torrents/info` ([#15152](https://github.com/qbittorrent/qBittorrent/pull/15152)) + +# General Information # + +- All API methods are under `/api/v2/APIName/methodName`, where `APIName` is a certain subgroup of API methods whose functionality is related. +- Either `GET` or `POST` can be used as the request type for all API methods. +- All API methods require [authentication](#authentication) (except the `/api/v2/auth/login` method itself, obviously). + +# Authentication # + +All Authentication API methods are under "auth", e.g.: `/api/v2/auth/methodName`. + +qBittorrent uses cookie-based authentication. + +## Login ## + +Name: `login` + +**Parameters:** + +Parameter | Type | Description +----------------------------------|---------|------------ +`username` | string | Username used to access the WebUI +`password` | string | Password used to access the WebUI + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +403 | User's IP is banned for too many failed login attempts +200 | All other scenarios + +Upon success, the response will contain a cookie with your SID. You must supply the cookie whenever you want to perform an operation that requires authentication. + +Example showing how to login and execute a command that requires authentication using `curl`: + +```sh +$ curl -i --header 'Referer: http://localhost:8080' --data 'username=admin&password=adminadmin' http://localhost:8080/api/v2/auth/login +HTTP/1.1 200 OK +Content-Encoding: +Content-Length: 3 +Content-Type: text/plain; charset=UTF-8 +Set-Cookie: SID=hBc7TxF76ERhvIw0jQQ4LZ7Z1jQUV0tQ; path=/ +$ curl http://localhost:8080/api/v2/torrents/info --cookie "SID=hBc7TxF76ERhvIw0jQQ4LZ7Z1jQUV0tQ" +``` + +Note: Set `Referer` or `Origin` header to the exact same domain and port as used in the HTTP query `Host` header. + +## Logout ## + +Name: `logout` + +**Parameters:** + +None + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios + +# Application # + +All Application API methods are under "app", e.g.: `/api/v2/app/methodName`. + +## Get application version ## + +Name: `version` + +**Parameters:** + +None + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios + +The response is a string with the application version, e.g. `v4.1.3` + +## Get API version ## + +Name: `webapiVersion` + +**Parameters:** + +None + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios + +The response is a string with the WebAPI version, e.g. `2.0` + +## Get build info ## + +Name: `buildInfo` + +**Parameters:** + +None + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios- see JSON below + +The response is a JSON object containing the following fields + +Property | Type | Description +-------------|---------|------------ +`qt` | string | QT version +`libtorrent` | string | libtorrent version +`boost` | string | Boost version +`openssl` | string | OpenSSL version +`bitness` | int | Application bitness (e.g. 64-bit) + +## Shutdown application ## + +Name: `shutdown` + +**Parameters:** + +None + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios + +## Get application preferences ## + +Name: `preferences` + +**Parameters:** + +None + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios- see JSON below + +The response is a JSON object with several fields (key-value) pairs representing the application's settings. The contents may vary depending on which settings are present in qBittorrent.ini. + +Possible fields: + +Property | Type | Description +-----------------------------------------|---------|------------ +`locale` | string | Currently selected language (e.g. en_GB for English) +`create_subfolder_enabled` | bool | True if a subfolder should be created when adding a torrent +`start_paused_enabled` | bool | True if torrents should be added in a Paused state +`auto_delete_mode` | integer | TODO +`preallocate_all` | bool | True if disk space should be pre-allocated for all files +`incomplete_files_ext` | bool | True if ".!qB" should be appended to incomplete files +`auto_tmm_enabled` | bool | True if Automatic Torrent Management is enabled by default +`torrent_changed_tmm_enabled` | bool | True if torrent should be relocated when its Category changes +`save_path_changed_tmm_enabled` | bool | True if torrent should be relocated when the default save path changes +`category_changed_tmm_enabled` | bool | True if torrent should be relocated when its Category's save path changes +`save_path` | string | Default save path for torrents, separated by slashes +`temp_path_enabled` | bool | True if folder for incomplete torrents is enabled +`temp_path` | string | Path for incomplete torrents, separated by slashes +`scan_dirs` | object | Property: directory to watch for torrent files, value: where torrents loaded from this directory should be downloaded to (see list of possible values below). Slashes are used as path separators; multiple key/value pairs can be specified +`export_dir` | string | Path to directory to copy .torrent files to. Slashes are used as path separators +`export_dir_fin` | string | Path to directory to copy .torrent files of completed downloads to. Slashes are used as path separators +`mail_notification_enabled` | bool | True if e-mail notification should be enabled +`mail_notification_sender` | string | e-mail where notifications should originate from +`mail_notification_email` | string | e-mail to send notifications to +`mail_notification_smtp` | string | smtp server for e-mail notifications +`mail_notification_ssl_enabled` | bool | True if smtp server requires SSL connection +`mail_notification_auth_enabled` | bool | True if smtp server requires authentication +`mail_notification_username` | string | Username for smtp authentication +`mail_notification_password` | string | Password for smtp authentication +`autorun_enabled` | bool | True if external program should be run after torrent has finished downloading +`autorun_program` | string | Program path/name/arguments to run if `autorun_enabled` is enabled; path is separated by slashes; you can use `%f` and `%n` arguments, which will be expanded by qBittorent as path_to_torrent_file and torrent_name (from the GUI; not the .torrent file name) respectively +`queueing_enabled` | bool | True if torrent queuing is enabled +`max_active_downloads` | integer | Maximum number of active simultaneous downloads +`max_active_torrents` | integer | Maximum number of active simultaneous downloads and uploads +`max_active_uploads` | integer | Maximum number of active simultaneous uploads +`dont_count_slow_torrents` | bool | If true torrents w/o any activity (stalled ones) will not be counted towards `max_active_*` limits; see [dont_count_slow_torrents](https://www.libtorrent.org/reference-Settings.html#dont_count_slow_torrents) for more information +`slow_torrent_dl_rate_threshold` | integer | Download rate in KiB/s for a torrent to be considered "slow" +`slow_torrent_ul_rate_threshold` | integer | Upload rate in KiB/s for a torrent to be considered "slow" +`slow_torrent_inactive_timer` | integer | Seconds a torrent should be inactive before considered "slow" +`max_ratio_enabled` | bool | True if share ratio limit is enabled +`max_ratio` | float | Get the global share ratio limit +`max_ratio_act` | integer | Action performed when a torrent reaches the maximum share ratio. See list of possible values here below. +`listen_port` | integer | Port for incoming connections +`upnp` | bool | True if UPnP/NAT-PMP is enabled +`random_port` | bool | True if the port is randomly selected +`dl_limit` | integer | Global download speed limit in KiB/s; `-1` means no limit is applied +`up_limit` | integer | Global upload speed limit in KiB/s; `-1` means no limit is applied +`max_connec` | integer | Maximum global number of simultaneous connections +`max_connec_per_torrent` | integer | Maximum number of simultaneous connections per torrent +`max_uploads` | integer | Maximum number of upload slots +`max_uploads_per_torrent` | integer | Maximum number of upload slots per torrent +`stop_tracker_timeout` | integer | Timeout in seconds for a `stopped` announce request to trackers +`enable_piece_extent_affinity` | bool | True if the advanced libtorrent option `piece_extent_affinity` is enabled +`bittorrent_protocol` | integer | Bittorrent Protocol to use (see list of possible values below) +`limit_utp_rate` | bool | True if `[du]l_limit` should be applied to uTP connections; this option is only available in qBittorent built against libtorrent version 0.16.X and higher +`limit_tcp_overhead` | bool | True if `[du]l_limit` should be applied to estimated TCP overhead (service data: e.g. packet headers) +`limit_lan_peers` | bool | True if `[du]l_limit` should be applied to peers on the LAN +`alt_dl_limit` | integer | Alternative global download speed limit in KiB/s +`alt_up_limit` | integer | Alternative global upload speed limit in KiB/s +`scheduler_enabled` | bool | True if alternative limits should be applied according to schedule +`schedule_from_hour` | integer | Scheduler starting hour +`schedule_from_min` | integer | Scheduler starting minute +`schedule_to_hour` | integer | Scheduler ending hour +`schedule_to_min` | integer | Scheduler ending minute +`scheduler_days` | integer | Scheduler days. See possible values here below +`dht` | bool | True if DHT is enabled +`pex` | bool | True if PeX is enabled +`lsd` | bool | True if LSD is enabled +`encryption` | integer | See list of possible values here below +`anonymous_mode` | bool | If true anonymous mode will be enabled; read more [here](Anonymous-Mode); this option is only available in qBittorent built against libtorrent version 0.16.X and higher +`proxy_type` | integer | See list of possible values here below +`proxy_ip` | string | Proxy IP address or domain name +`proxy_port` | integer | Proxy port +`proxy_peer_connections` | bool | True if peer and web seed connections should be proxified; this option will have any effect only in qBittorent built against libtorrent version 0.16.X and higher +`proxy_auth_enabled` | bool | True proxy requires authentication; doesn't apply to SOCKS4 proxies +`proxy_username` | string | Username for proxy authentication +`proxy_password` | string | Password for proxy authentication +`proxy_torrents_only` | bool | True if proxy is only used for torrents +`ip_filter_enabled` | bool | True if external IP filter should be enabled +`ip_filter_path` | string | Path to IP filter file (.dat, .p2p, .p2b files are supported); path is separated by slashes +`ip_filter_trackers` | bool | True if IP filters are applied to trackers +`web_ui_domain_list` | string | Comma-separated list of domains to accept when performing Host header validation +`web_ui_address` | string | IP address to use for the WebUI +`web_ui_port` | integer | WebUI port +`web_ui_upnp` | bool | True if UPnP is used for the WebUI port +`web_ui_username` | string | WebUI username +`web_ui_password` | string | For API ≥ v2.3.0: Plaintext WebUI password, not readable, write-only. For API < v2.3.0: MD5 hash of WebUI password, hash is generated from the following string: `username:Web UI Access:plain_text_web_ui_password` +`web_ui_csrf_protection_enabled` | bool | True if WebUI CSRF protection is enabled +`web_ui_clickjacking_protection_enabled` | bool | True if WebUI clickjacking protection is enabled +`web_ui_secure_cookie_enabled` | bool | True if WebUI cookie `Secure` flag is enabled +`web_ui_max_auth_fail_count` | integer | Maximum number of authentication failures before WebUI access ban +`web_ui_ban_duration` | integer | WebUI access ban duration in seconds +`web_ui_session_timeout` | integer | Seconds until WebUI is automatically signed off +`web_ui_host_header_validation_enabled` | bool | True if WebUI host header validation is enabled +`bypass_local_auth` | bool | True if authentication challenge for loopback address (127.0.0.1) should be disabled +`bypass_auth_subnet_whitelist_enabled` | bool | True if webui authentication should be bypassed for clients whose ip resides within (at least) one of the subnets on the whitelist +`bypass_auth_subnet_whitelist` | string | (White)list of ipv4/ipv6 subnets for which webui authentication should be bypassed; list entries are separated by commas +`alternative_webui_enabled` | bool | True if an alternative WebUI should be used +`alternative_webui_path` | string | File path to the alternative WebUI +`use_https` | bool | True if WebUI HTTPS access is enabled +`ssl_key` | string | For API < v2.0.1: SSL keyfile contents (this is a not a path) +`ssl_cert` | string | For API < v2.0.1: SSL certificate contents (this is a not a path) +`web_ui_https_key_path` | string | For API ≥ v2.0.1: Path to SSL keyfile +`web_ui_https_cert_path` | string | For API ≥ v2.0.1: Path to SSL certificate +`dyndns_enabled` | bool | True if server DNS should be updated dynamically +`dyndns_service` | integer | See list of possible values here below +`dyndns_username` | string | Username for DDNS service +`dyndns_password` | string | Password for DDNS service +`dyndns_domain` | string | Your DDNS domain name +`rss_refresh_interval` | integer | RSS refresh interval +`rss_max_articles_per_feed` | integer | Max stored articles per RSS feed +`rss_processing_enabled` | bool | Enable processing of RSS feeds +`rss_auto_downloading_enabled` | bool | Enable auto-downloading of torrents from the RSS feeds +`rss_download_repack_proper_episodes` | bool | For API ≥ v2.5.1: Enable downloading of repack/proper Episodes +`rss_smart_episode_filters` | string | For API ≥ v2.5.1: List of RSS Smart Episode Filters +`add_trackers_enabled` | bool | Enable automatic adding of trackers to new torrents +`add_trackers` | string | List of trackers to add to new torrent +`web_ui_use_custom_http_headers_enabled` | bool | For API ≥ v2.5.1: Enable custom http headers +`web_ui_custom_http_headers` | string | For API ≥ v2.5.1: List of custom http headers +`max_seeding_time_enabled` | bool | True enables max seeding time +`max_seeding_time` | integer | Number of minutes to seed a torrent +`announce_ip` | string | TODO +`announce_to_all_tiers` | bool | True always announce to all tiers +`announce_to_all_trackers` | bool | True always announce to all trackers in a tier +`async_io_threads` | integer | Number of asynchronous I/O threads +`banned_IPs` | string | List of banned IPs +`checking_memory_use` | integer | Outstanding memory when checking torrents in MiB +`current_interface_address` | string | IP Address to bind to. Empty String means All addresses +`current_network_interface` | string | Network Interface used +`disk_cache` | integer | Disk cache used in MiB +`disk_cache_ttl` | integer | Disk cache expiry interval in seconds +`embedded_tracker_port` | integer | Port used for embedded tracker +`enable_coalesce_read_write` | bool | True enables coalesce reads & writes +`enable_embedded_tracker` | bool | True enables embedded tracker +`enable_multi_connections_from_same_ip` | bool | True allows multiple connections from the same IP address +`enable_os_cache` | bool | True enables os cache +`enable_upload_suggestions` | bool | True enables sending of upload piece suggestions +`file_pool_size` | integer | File pool size +`outgoing_ports_max` | integer | Maximal outgoing port (0: Disabled) +`outgoing_ports_min` | integer | Minimal outgoing port (0: Disabled) +`recheck_completed_torrents` | bool | True rechecks torrents on completion +`resolve_peer_countries` | bool | True resolves peer countries +`save_resume_data_interval` | integer | Save resume data interval in min +`send_buffer_low_watermark` | integer | Send buffer low watermark in KiB +`send_buffer_watermark` | integer | Send buffer watermark in KiB +`send_buffer_watermark_factor` | integer | Send buffer watermark factor in percent +`socket_backlog_size` | integer | Socket backlog size +`upload_choking_algorithm` | integer | Upload choking algorithm used (see list of possible values below) +`upload_slots_behavior` | integer | Upload slots behavior used (see list of possible values below) +`upnp_lease_duration` | integer | UPnP lease duration (0: Permanent lease) +`utp_tcp_mixed_mode` | integer | μTP-TCP mixed mode algorithm (see list of possible values below) + +Possible values of `scan_dirs`: + +Value | Description +----------------------------|------------ +`0` | Download to the monitored folder +`1` | Download to the default save path +`"/path/to/download/to"` | Download to this path + +Possible values of `scheduler_days`: + +Value | Description +-------|------------ +`0` | Every day +`1` | Every weekday +`2` | Every weekend +`3` | Every Monday +`4` | Every Tuesday +`5` | Every Wednesday +`6` | Every Thursday +`7` | Every Friday +`8` | Every Saturday +`9` | Every Sunday + +Possible values of `encryption`: + +Value | Description +-------|------------ +`0` | Prefer encryption +`1` | Force encryption on +`2` | Force encryption off + +NB: the first options allows you to use both encrypted and unencrypted connections (this is the default); other options are mutually exclusive: e.g. by forcing encryption on you won't be able to use unencrypted connections and vice versa. + +Possible values of `proxy_type`: + +Value | Description +------|------------ +`-1` | Proxy is disabled +`1` | HTTP proxy without authentication +`2` | SOCKS5 proxy without authentication +`3` | HTTP proxy with authentication +`4` | SOCKS5 proxy with authentication +`5` | SOCKS4 proxy without authentication + +Possible values of `dyndns_service`: + +Value | Description +-------|------------ +`0` | Use DyDNS +`1` | Use NOIP + +Possible values of `max_ratio_act`: + +Value | Description +------|------------ +`0` | Pause torrent +`1` | Remove torrent + +Possible values of `bittorrent_protocol`: + +Value | Description +-------|------------ +`0` | TCP and μTP +`1` | TCP +`2` | μTP + +Possible values of `upload_choking_algorithm`: + +Value | Description +-------|------------ +`0` | Round-robin +`1` | Fastest upload +`2` | Anti-leech + +Possible values of `upload_slots_behavior`: + +Value | Description +-------|------------ +`0` | Fixed slots +`1` | Upload rate based + +Possible values of `utp_tcp_mixed_mode`: + +Value | Description +-------|------------ +`0` | Prefer TCP +`1` | Peer proportional + +Example: + +```JSON +{ + "add_trackers": "", + "add_trackers_enabled": false, + "alt_dl_limit": 10240, + "alt_up_limit": 10240, + "alternative_webui_enabled": false, + "alternative_webui_path": "/home/user/Documents/qbit-webui", + "announce_ip": "", + "announce_to_all_tiers": true, + "announce_to_all_trackers": false, + "anonymous_mode": false, + "async_io_threads": 4, + "auto_delete_mode": 0, + "auto_tmm_enabled": false, + "autorun_enabled": false, + "autorun_program": "", + "banned_IPs": "", + "bittorrent_protocol": 0, + "bypass_auth_subnet_whitelist": "", + "bypass_auth_subnet_whitelist_enabled": false, + "bypass_local_auth": false, + "category_changed_tmm_enabled": false, + "checking_memory_use": 32, + "create_subfolder_enabled": true, + "current_interface_address": "", + "current_network_interface": "", + "dht": true, + "disk_cache": -1, + "disk_cache_ttl": 60, + "dl_limit": 0, + "dont_count_slow_torrents": false, + "dyndns_domain": "changeme.dyndns.org", + "dyndns_enabled": false, + "dyndns_password": "", + "dyndns_service": 0, + "dyndns_username": "", + "embedded_tracker_port": 9000, + "enable_coalesce_read_write": false, + "enable_embedded_tracker": false, + "enable_multi_connections_from_same_ip": false, + "enable_os_cache": true, + "enable_piece_extent_affinity": false, + "enable_upload_suggestions": false, + "encryption": 0, + "export_dir": "/home/user/Downloads/all", + "export_dir_fin": "/home/user/Downloads/completed", + "file_pool_size": 40, + "incomplete_files_ext": false, + "ip_filter_enabled": false, + "ip_filter_path": "", + "ip_filter_trackers": false, + "limit_lan_peers": true, + "limit_tcp_overhead": false, + "limit_utp_rate": true, + "listen_port": 58925, + "locale": "en", + "lsd": true, + "mail_notification_auth_enabled": false, + "mail_notification_email": "", + "mail_notification_enabled": false, + "mail_notification_password": "", + "mail_notification_sender": "qBittorrent_notification@example.com", + "mail_notification_smtp": "smtp.changeme.com", + "mail_notification_ssl_enabled": false, + "mail_notification_username": "", + "max_active_downloads": 3, + "max_active_torrents": 5, + "max_active_uploads": 3, + "max_connec": 500, + "max_connec_per_torrent": 100, + "max_ratio": -1, + "max_ratio_act": 0, + "max_ratio_enabled": false, + "max_seeding_time": -1, + "max_seeding_time_enabled": false, + "max_uploads": -1, + "max_uploads_per_torrent": -1, + "outgoing_ports_max": 0, + "outgoing_ports_min": 0, + "pex": true, + "preallocate_all": false, + "proxy_auth_enabled": false, + "proxy_ip": "0.0.0.0", + "proxy_password": "", + "proxy_peer_connections": false, + "proxy_port": 8080, + "proxy_torrents_only": false, + "proxy_type": 0, + "proxy_username": "", + "queueing_enabled": false, + "random_port": false, + "recheck_completed_torrents": false, + "resolve_peer_countries": true, + "rss_auto_downloading_enabled":true, + "rss_download_repack_proper_episodes":true, + "rss_max_articles_per_feed":50, + "rss_processing_enabled":true, + "rss_refresh_interval":30, + "rss_smart_episode_filters":"s(\\d+)e(\\d+)\n(\\d+)x(\\d+)\n(\\d{4}[.\\-]\\d{1,2}[.\\-]\\d{1,2})", + "save_path": "/home/user/Downloads/", + "save_path_changed_tmm_enabled": false, + "save_resume_data_interval": 60, + "scan_dirs": + { + "/home/user/Downloads/incoming/games": 0, + "/home/user/Downloads/incoming/movies": 1, + }, + "schedule_from_hour": 8, + "schedule_from_min": 0, + "schedule_to_hour": 20, + "schedule_to_min": 0, + "scheduler_days": 0, + "scheduler_enabled": false, + "send_buffer_low_watermark": 10, + "send_buffer_watermark": 500, + "send_buffer_watermark_factor": 50, + "slow_torrent_dl_rate_threshold": 2, + "slow_torrent_inactive_timer": 60, + "slow_torrent_ul_rate_threshold": 2, + "socket_backlog_size": 30, + "start_paused_enabled": false, + "stop_tracker_timeout": 1, + "temp_path": "/home/user/Downloads/temp", + "temp_path_enabled": false, + "torrent_changed_tmm_enabled": true, + "up_limit": 0, + "upload_choking_algorithm": 1, + "upload_slots_behavior": 0, + "upnp": true, + "use_https": false, + "utp_tcp_mixed_mode": 0, + "web_ui_address": "*", + "web_ui_ban_duration": 3600, + "web_ui_clickjacking_protection_enabled": true, + "web_ui_csrf_protection_enabled": true, + "web_ui_custom_http_headers": "", + "web_ui_domain_list": "*", + "web_ui_host_header_validation_enabled": true, + "web_ui_https_cert_path": "", + "web_ui_https_key_path": "", + "web_ui_max_auth_fail_count": 5, + "web_ui_port": 8080, + "web_ui_secure_cookie_enabled": true, + "web_ui_session_timeout": 3600, + "web_ui_upnp": false, + "web_ui_use_custom_http_headers_enabled": false, + "web_ui_username": "admin" +} +``` + +## Set application preferences ## + +Name: `setPreferences` + +**Parameters:** + +A json object with key-value pairs of the settings you want to change and their new values. + +Example: + +```JSON +json={"save_path":"C:/Users/Dayman/Downloads","queueing_enabled":false,"scan_dirs":{"C:/Games": 0,"D:/Downloads": 1}} +``` + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios + +**Notes**: + + 1. There is no need to pass all possible preferences' `token:value` pairs if you only want to change one option + 1. Paths in `scan_dirs` must exist, otherwise this option will have no effect + 1. String values must be quoted; integer and boolean values must never be quoted + +For a list of possible preference options see [Get application preferences](#get-application-preferences) + +## Get default save path ## + +Name: `defaultSavePath` + +**Parameters:** + +None + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios + +The response is a string with the default save path, e.g. `C:/Users/Dayman/Downloads`. + +# Log # + +All Log API methods are under "log", e.g.: `/api/v2/log/methodName`. + +## Get log ## + +Name: `main` + +**Parameters:** + +Parameter | Type | Description +----------------|---------|------------ +`normal` | bool | Include normal messages (default: `true`) +`info` | bool | Include info messages (default: `true`) +`warning` | bool | Include warning messages (default: `true`) +`critical` | bool | Include critical messages (default: `true`) +`last_known_id` | integer | Exclude messages with "message id" <= `last_known_id` (default: `-1`) + +Example: + +```http +/api/v2/log/main?normal=true&info=true&warning=true&critical=true&last_known_id=-1 +``` + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios- see JSON below + +The response is a JSON array in which each element is an entry of the log. + +Each element of the array has the following properties: + +Property | Type | Description +------------|---------|------------ +`id` | integer | ID of the message +`message` | string | Text of the message +`timestamp` | integer | Milliseconds since epoch +`type` | integer | Type of the message: Log::NORMAL: `1`, Log::INFO: `2`, Log::WARNING: `4`, Log::CRITICAL: `8` + +Example: + +```JSON +[ + { + "id":0, + "message":"qBittorrent v3.4.0 started", + "timestamp":1507969127860, + "type":1 + }, + { + "id":1, + "message":"qBittorrent is trying to listen on any interface port: 19036", + "timestamp":1507969127869, + "type":2 + }, + { + "id":2, + "message":"Peer ID: -qB3400-", + "timestamp":1507969127870, + "type":1 + }, + { + "id":3, + "message":"HTTP User-Agent is 'qBittorrent/3.4.0'", + "timestamp":1507969127870, + "type":1 + }, + { + "id":4, + "message":"DHT support [ON]", + "timestamp":1507969127871, + "type":2 + }, + { + "id":5, + "message":"Local Peer Discovery support [ON]", + "timestamp":1507969127871, + "type":2 + }, + { + "id":6, + "message":"PeX support [ON]", + "timestamp":1507969127871, + "type":2 + }, + { + "id":7, + "message":"Anonymous mode [OFF]", + "timestamp":1507969127871, + "type":2 + }, + { + "id":8, + "message":"Encryption support [ON]", + "timestamp":1507969127871, + "type":2 + }, + { + "id":9, + "message":"Embedded Tracker [OFF]", + "timestamp":1507969127871, + "type":2 + }, + { + "id":10, + "message":"UPnP / NAT-PMP support [ON]", + "timestamp":1507969127873, + "type":2 + }, + { + "id":11, + "message":"Web UI: Now listening on port 8080", + "timestamp":1507969127883, + "type":1 + }, + { + "id":12, + "message":"Options were saved successfully.", + "timestamp":1507969128055, + "type":1 + }, + { + "id":13, + "message":"qBittorrent is successfully listening on interface :: port: TCP/19036", + "timestamp":1507969128270, + "type":2 + }, + { + "id":14, + "message":"qBittorrent is successfully listening on interface 0.0.0.0 port: TCP/19036", + "timestamp":1507969128271, + "type":2 + }, + { + "id":15, + "message":"qBittorrent is successfully listening on interface 0.0.0.0 port: UDP/19036", + "timestamp":1507969128272, + "type":2 + } +] +``` + +## Get peer log ## + +Name: `peers` + +**Parameters:** + +Parameter | Type | Description +----------------|---------|------------ +`last_known_id` | integer | Exclude messages with "message id" <= `last_known_id` (default: `-1`) + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios- see JSON below + +The response a JSON array. Each element of the array of objects (each object is the information relative to a peer) containing the following fields + +Property | Type | Description +------------|---------|------------ +`id` | integer | ID of the peer +`ip` | string | IP of the peer +`timestamp` | integer | Milliseconds since epoch +`blocked` | boolean | Whether or not the peer was blocked +`reason` | string | Reason of the block + +# Sync # + +Sync API implements requests for obtaining changes since the last request. +All Sync API methods are under "sync", e.g.: `/api/v2/sync/methodName`. + +## Get main data ## + +Name: `maindata` + +**Parameters:** + +Parameter | Type | Description +----------|---------|------------ +`rid` | integer | Response ID. If not provided, `rid=0` will be assumed. If the given `rid` is different from the one of last server reply, `full_update` will be `true` (see the server reply details for more info) + +Example: + +```http +/api/v2/sync/maindata?rid=14 +``` + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios- see JSON below + +The response is a JSON object with the following possible fields + +Property | Type | Description +------------------------------|---------|------------ +`rid` | integer | Response ID +`full_update` | bool | Whether the response contains all the data or partial data +`torrents` | object | Property: torrent hash, value: same as [torrent list](#get-torrent-list) +`torrents_removed` | array | List of hashes of torrents removed since last request +`categories` | object | Info for categories added since last request +`categories_removed` | array | List of categories removed since last request +`tags` | array | List of tags added since last request +`tags_removed` | array | List of tags removed since last request +`server_state` | object | Global transfer info + +Example: + +```JSON +{ + "rid":15, + "torrents": + { + "8c212779b4abde7c6bc608063a0d008b7e40ce32": + { + "state":"pausedUP" + } + } +} +``` + +## Get torrent peers data ## + +Name: `torrentPeers` + +**Parameters:** + +Parameter | Type | Description +----------|---------|------------ +`hash` | string | Torrent hash +`rid` | integer | Response ID. If not provided, `rid=0` will be assumed. If the given `rid` is different from the one of last server reply, `full_update` will be `true` (see the server reply details for more info) + +Example: + +```http +/api/v2/sync/torrentPeers?hash=8c212779b4abde7c6bc608063a0d008b7e40ce32?rid=14 +``` + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +404 | Torrent hash was not found +200 | All other scenarios- see JSON below + +The response is TODO + +# Transfer info # + +All Transfer info API methods are under "transfer", e.g.: `/api/v2/transfer/methodName`. + +## Get global transfer info ## + +This method returns info you usually see in qBt status bar. + +Name: `info` + +**Parameters:** + +None + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios- see JSON below + +The response is a JSON object with the following fields + +Property | Type | Description +--------------------|---------|------------ +`dl_info_speed` | integer | Global download rate (bytes/s) +`dl_info_data` | integer | Data downloaded this session (bytes) +`up_info_speed` | integer | Global upload rate (bytes/s) +`up_info_data` | integer | Data uploaded this session (bytes) +`dl_rate_limit` | integer | Download rate limit (bytes/s) +`up_rate_limit` | integer | Upload rate limit (bytes/s) +`dht_nodes` | integer | DHT nodes connected to +`connection_status` | string | Connection status. See possible values here below + +In addition to the above in partial data requests (see [Get partial data](#get-partial-data) for more info): + +Property | Type | Description +-----------------------|---------|------------ +`queueing` | bool | True if torrent queueing is enabled +`use_alt_speed_limits` | bool | True if alternative speed limits are enabled +`refresh_interval` | integer | Transfer list refresh interval (milliseconds) + +Possible values of `connection_status`: + +Value | +--------------------| +`connected` | +`firewalled` | +`disconnected` | + +Example: + +```JSON +{ + "connection_status":"connected", + "dht_nodes":386, + "dl_info_data":681521119, + "dl_info_speed":0, + "dl_rate_limit":0, + "up_info_data":10747904, + "up_info_speed":0, + "up_rate_limit":1048576 +} +``` + +## Get alternative speed limits state ## + +Name: `speedLimitsMode` + +**Parameters:** + +None + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios + +The response is `1` if alternative speed limits are enabled, `0` otherwise. + +## Toggle alternative speed limits ## + +Name: `toggleSpeedLimitsMode` + +**Parameters:** + +None + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios + +## Get global download limit ## + +Name: `downloadLimit` + +**Parameters:** + +None + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios + +The response is the value of current global download speed limit in bytes/second; this value will be zero if no limit is applied. + +## Set global download limit ## + +Name: `setDownloadLimit` + +**Parameters:** + +Parameter | Type | Description +----------------------------------|---------|------------ +`limit` | integer | The global download speed limit to set in bytes/second + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios + +## Get global upload limit ## + +Name: `uploadLimit` + +**Parameters:** + +None + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios + +The response is the value of current global upload speed limit in bytes/second; this value will be zero if no limit is applied. + +## Set global upload limit ## + +Name: `setUploadLimit` + +**Parameters:** + +Parameter | Type | Description +----------------------------------|---------|------------ +`limit` | integer | The global upload speed limit to set in bytes/second + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios + +## Ban peers ## + +Name: `banPeers` + +**Parameters:** + +Parameter | Type | Description +----------------------------------|---------|------------ +`peers` | string | The peer to ban, or multiple peers separated by a pipe `\|`. Each peer is a colon-separated `host:port` + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios + +# Torrent management # + +All Torrent management API methods are under "torrents", e.g.: `/api/v2/torrents/methodName`. + +## Get torrent list ## + +Name: `info` + +**Parameters:** + +Parameter | Type | Description +----------------------|---------|------------ +`filter` _optional_ | string | Filter torrent list by state. Allowed state filters: `all`, `downloading`, `seeding`, `completed`, `paused`, `active`, `inactive`, `resumed`, `stalled`, `stalled_uploading`, `stalled_downloading`, `errored` +`category` _optional_ | string | Get torrents with the given category (empty string means "without category"; no "category" parameter means "any category" <- broken until [#11748](https://github.com/qbittorrent/qBittorrent/issues/11748) is resolved). Remember to URL-encode the category name. For example, `My category` becomes `My%20category` +`tag` _optional_ since 2.8.3 | string | Get torrents with the given tag (empty string means "without tag"; no "tag" parameter means "any tag". Remember to URL-encode the category name. For example, `My tag` becomes `My%20tag` +`sort` _optional_ | string | Sort torrents by given key. They can be sorted using any field of the response's JSON array (which are documented below) as the sort key. +`reverse` _optional_ | bool | Enable reverse sorting. Defaults to `false` +`limit` _optional_ | integer | Limit the number of torrents returned +`offset` _optional_ | integer | Set offset (if less than 0, offset from end) +`hashes` _optional_ | string | Filter by hashes. Can contain multiple hashes separated by `\|` + +Example: + +```http +/api/v2/torrents/info?filter=downloading&category=sample%20category&sort=ratio +``` + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios- see JSON below + +The response is a JSON array with the following fields + +Property | Type | Description +---------------------|---------|------------ +`added_on` | integer | Time (Unix Epoch) when the torrent was added to the client +`amount_left` | integer | Amount of data left to download (bytes) +`auto_tmm` | bool | Whether this torrent is managed by Automatic Torrent Management +`availability` | float | Percentage of file pieces currently available +`category` | string | Category of the torrent +`completed` | integer | Amount of transfer data completed (bytes) +`completion_on` | integer | Time (Unix Epoch) when the torrent completed +`content_path` | string | Absolute path of torrent content (root path for multifile torrents, absolute file path for singlefile torrents) +`dl_limit` | integer | Torrent download speed limit (bytes/s). `-1` if ulimited. +`dlspeed` | integer | Torrent download speed (bytes/s) +`downloaded` | integer | Amount of data downloaded +`downloaded_session` | integer | Amount of data downloaded this session +`eta` | integer | Torrent ETA (seconds) +`f_l_piece_prio` | bool | True if first last piece are prioritized +`force_start` | bool | True if force start is enabled for this torrent +`hash` | string | Torrent hash +`last_activity` | integer | Last time (Unix Epoch) when a chunk was downloaded/uploaded +`magnet_uri` | string | Magnet URI corresponding to this torrent +`max_ratio` | float | Maximum share ratio until torrent is stopped from seeding/uploading +`max_seeding_time` | integer | Maximum seeding time (seconds) until torrent is stopped from seeding +`name` | string | Torrent name +`num_complete` | integer | Number of seeds in the swarm +`num_incomplete` | integer | Number of leechers in the swarm +`num_leechs` | integer | Number of leechers connected to +`num_seeds` | integer | Number of seeds connected to +`priority` | integer | Torrent priority. Returns -1 if queuing is disabled or torrent is in seed mode +`progress` | float | Torrent progress (percentage/100) +`ratio` | float | Torrent share ratio. Max ratio value: 9999. +`ratio_limit` | float | TODO (what is different from `max_ratio`?) +`save_path` | string | Path where this torrent's data is stored +`seeding_time` | integer | Torrent elapsed time while complete (seconds) +`seeding_time_limit` | integer | TODO (what is different from `max_seeding_time`?) seeding_time_limit is a per torrent setting, when Automatic Torrent Management is disabled, furthermore then max_seeding_time is set to seeding_time_limit for this torrent. If Automatic Torrent Management is enabled, the value is -2. And if max_seeding_time is unset it have a default value -1. +`seen_complete` | integer | Time (Unix Epoch) when this torrent was last seen complete +`seq_dl` | bool | True if sequential download is enabled +`size` | integer | Total size (bytes) of files selected for download +`state` | string | Torrent state. See table here below for the possible values +`super_seeding` | bool | True if super seeding is enabled +`tags` | string | Comma-concatenated tag list of the torrent +`time_active` | integer | Total active time (seconds) +`total_size` | integer | Total size (bytes) of all file in this torrent (including unselected ones) +`tracker` | string | The first tracker with working status. Returns empty string if no tracker is working. +`up_limit` | integer | Torrent upload speed limit (bytes/s). `-1` if ulimited. +`uploaded` | integer | Amount of data uploaded +`uploaded_session` | integer | Amount of data uploaded this session +`upspeed` | integer | Torrent upload speed (bytes/s) + +Possible values of `state`: + +Value | Description +--------------|------------ +`error` | Some error occurred, applies to paused torrents +`missingFiles`| Torrent data files is missing +`uploading` | Torrent is being seeded and data is being transferred +`pausedUP` | Torrent is paused and has finished downloading +`queuedUP` | Queuing is enabled and torrent is queued for upload +`stalledUP` | Torrent is being seeded, but no connection were made +`checkingUP` | Torrent has finished downloading and is being checked +`forcedUP` | Torrent is forced to uploading and ignore queue limit +`allocating` | Torrent is allocating disk space for download +`downloading` | Torrent is being downloaded and data is being transferred +`metaDL` | Torrent has just started downloading and is fetching metadata +`pausedDL` | Torrent is paused and has NOT finished downloading +`queuedDL` | Queuing is enabled and torrent is queued for download +`stalledDL` | Torrent is being downloaded, but no connection were made +`checkingDL` | Same as checkingUP, but torrent has NOT finished downloading +`forcedDL` | Torrent is forced to downloading to ignore queue limit +`checkingResumeData`| Checking resume data on qBt startup +`moving` | Torrent is moving to another location +`unknown` | Unknown status + +Example: + +```JSON +[ + { + "dlspeed":9681262, + "eta":87, + "f_l_piece_prio":false, + "force_start":false, + "hash":"8c212779b4abde7c6bc608063a0d008b7e40ce32", + "category":"", + "tags": "", + "name":"debian-8.1.0-amd64-CD-1.iso", + "num_complete":-1, + "num_incomplete":-1, + "num_leechs":2, + "num_seeds":54, + "priority":1, + "progress":0.16108787059783936, + "ratio":0, + "seq_dl":false, + "size":657457152, + "state":"downloading", + "super_seeding":false, + "upspeed":0 + }, + { + another_torrent_info + } +] +``` + +## Get torrent generic properties ## + +Requires knowing the torrent hash. You can get it from [torrent list](#get-torrent-list). + +Name: `properties` + +**Parameters:** + +Parameter | Type | Description +----------|--------|------------ +`hash` | string | The hash of the torrent you want to get the generic properties of + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +404 | Torrent hash was not found +200 | All other scenarios- see JSON below + +The response is: + +- empty, if the torrent hash is invalid +- otherwise, a JSON object with the following fields + +Property | Type | Description +--------------------------|---------|------------ +`save_path` | string | Torrent save path +`creation_date` | integer | Torrent creation date (Unix timestamp) +`piece_size` | integer | Torrent piece size (bytes) +`comment` | string | Torrent comment +`total_wasted` | integer | Total data wasted for torrent (bytes) +`total_uploaded` | integer | Total data uploaded for torrent (bytes) +`total_uploaded_session` | integer | Total data uploaded this session (bytes) +`total_downloaded` | integer | Total data downloaded for torrent (bytes) +`total_downloaded_session`| integer | Total data downloaded this session (bytes) +`up_limit` | integer | Torrent upload limit (bytes/s) +`dl_limit` | integer | Torrent download limit (bytes/s) +`time_elapsed` | integer | Torrent elapsed time (seconds) +`seeding_time` | integer | Torrent elapsed time while complete (seconds) +`nb_connections` | integer | Torrent connection count +`nb_connections_limit` | integer | Torrent connection count limit +`share_ratio` | float | Torrent share ratio +`addition_date` | integer | When this torrent was added (unix timestamp) +`completion_date` | integer | Torrent completion date (unix timestamp) +`created_by` | string | Torrent creator +`dl_speed_avg` | integer | Torrent average download speed (bytes/second) +`dl_speed` | integer | Torrent download speed (bytes/second) +`eta` | integer | Torrent ETA (seconds) +`last_seen` | integer | Last seen complete date (unix timestamp) +`peers` | integer | Number of peers connected to +`peers_total` | integer | Number of peers in the swarm +`pieces_have` | integer | Number of pieces owned +`pieces_num` | integer | Number of pieces of the torrent +`reannounce` | integer | Number of seconds until the next announce +`seeds` | integer | Number of seeds connected to +`seeds_total` | integer | Number of seeds in the swarm +`total_size` | integer | Torrent total size (bytes) +`up_speed_avg` | integer | Torrent average upload speed (bytes/second) +`up_speed` | integer | Torrent upload speed (bytes/second) + +NB: `-1` is returned if the type of the property is integer but its value is not known. + +Example: + +```JSON +{ + "addition_date":1438429165, + "comment":"\"Debian CD from cdimage.debian.org\"", + "completion_date":1438429234, + "created_by":"", + "creation_date":1433605214, + "dl_limit":-1, + "dl_speed":0, + "dl_speed_avg":9736015, + "eta":8640000, + "last_seen":1438430354, + "nb_connections":3, + "nb_connections_limit":250, + "peers":1, + "peers_total":89, + "piece_size":524288, + "pieces_have":1254, + "pieces_num":1254, + "reannounce":672, + "save_path":"/Downloads/debian-8.1.0-amd64-CD-1.iso", + "seeding_time":1128, + "seeds":1, + "seeds_total":254, + "share_ratio":0.00072121022562178299, + "time_elapsed":1197, + "total_downloaded":681521119, + "total_downloaded_session":681521119, + "total_size":657457152, + "total_uploaded":491520, + "total_uploaded_session":491520, + "total_wasted":23481724, + "up_limit":-1, + "up_speed":0, + "up_speed_avg":410 +} +``` + +## Get torrent trackers ## + +Requires knowing the torrent hash. You can get it from [torrent list](#get-torrent-list). + +Name: `trackers` + +**Parameters:** + +Parameter | Type | Description +----------|--------|------------ +`hash` | string | The hash of the torrent you want to get the trackers of + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +404 | Torrent hash was not found +200 | All other scenarios- see JSON below + +The response is a JSON array, where each element contains info about one tracker, with the following fields + +Property | Type | Description +-----------------|----------|------------- +`url` | string | Tracker url +`status` | integer | Tracker status. See the table below for possible values +`tier` | integer | Tracker priority tier. Lower tier trackers are tried before higher tiers. Tier numbers are valid when `>= 0`, `< 0` is used as placeholder when `tier` does not exist for special entries (such as DHT). +`num_peers` | integer | Number of peers for current torrent, as reported by the tracker +`num_seeds` | integer | Number of seeds for current torrent, asreported by the tracker +`num_leeches` | integer | Number of leeches for current torrent, as reported by the tracker +`num_downloaded` | integer | Number of completed downlods for current torrent, as reported by the tracker +`msg` | string | Tracker message (there is no way of knowing what this message is - it's up to tracker admins) + +Possible values of `status`: + +Value | Description +-------|------------ +0 | Tracker is disabled (used for DHT, PeX, and LSD) +1 | Tracker has not been contacted yet +2 | Tracker has been contacted and is working +3 | Tracker is updating +4 | Tracker has been contacted, but it is not working (or doesn't send proper replies) + +Example: + +```JSON +[ + { + "msg":"", + "num_peers":100, + "status":2, + "url":"http://bttracker.debian.org:6969/announce" + }, + { + another_tracker_info + } +] +``` + +## Get torrent web seeds ## + +Requires knowing the torrent hash. You can get it from [torrent list](#get-torrent-list). + +Name: `webseeds` + +**Parameters:** + +Parameter | Type | Description +----------|--------|------------ +`hash` | string | The hash of the torrent you want to get the webseeds of + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +404 | Torrent hash was not found +200 | All other scenarios- see JSON below + +The response is a JSON array, where each element is information about one webseed, with the following fields + +Property | Type | Description +--------------|----------|------------ +`url` | string | URL of the web seed + +Example: + +```JSON +[ + { + "url":"http://some_url/" + }, + { + "url":"http://some_other_url/" + } +] +``` + +## Get torrent contents ## + +Requires knowing the torrent hash. You can get it from [torrent list](#get-torrent-list). + +Name: `files` + +**Parameters:** + +Parameter | Type | Description +----------|--------|------------ +`hash` | string | The hash of the torrent you want to get the contents of +`indexes` _optional_ since 2.8.2 | string | The indexes of the files you want to retrieve. `indexes` can contain multiple values separated by `\|`. + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +404 | Torrent hash was not found +200 | All other scenarios- see JSON below + +The response is: + +- empty, if the torrent hash is invalid +- otherwise, a JSON array, where each element contains info about one file, with the following fields + +Property | Type | Description +---------------|---------------|------------- +`index` since 2.8.2 | integer | File index +`name` | string | File name (including relative path) +`size` | integer | File size (bytes) +`progress` | float | File progress (percentage/100) +`priority` | integer | File priority. See possible values here below +`is_seed` | bool | True if file is seeding/complete +`piece_range` | integer array | The first number is the starting piece index and the second number is the ending piece index (inclusive) +`availability` | float | Percentage of file pieces currently available (percentage/100) + +Possible values of `priority`: + +Value | Description +-----------|------------ +`0` | Do not download +`1` | Normal priority +`6` | High priority +`7` | Maximal priority + +Example: + +```JSON + +[ + { + "index":0, + "is_seed":false, + "name":"debian-8.1.0-amd64-CD-1.iso", + "piece_range":[0,1253], + "priority":1, + "progress":0, + "size":657457152, + "availability":0.5, + } +] +``` + +## Get torrent pieces' states ## + +Requires knowing the torrent hash. You can get it from [torrent list](#get-torrent-list). + +Name: `pieceStates` + +**Parameters:** + +Parameter | Type | Description +----------|--------|------------ +`hash` | string | The hash of the torrent you want to get the pieces' states of + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +404 | Torrent hash was not found +200 | All other scenarios- see JSON below + +The response is: + +- empty, if the torrent hash is invalid +- otherwise, an array of states (integers) of all pieces (in order) of a specific torrent. + +Value meanings are defined as below: + +Value | Description +-----------|------------ +`0` | Not downloaded yet +`1` | Now downloading +`2` | Already downloaded + +Example: + +```JSON +[0,0,2,1,0,0,2,1] +``` + +## Get torrent pieces' hashes ## + +Requires knowing the torrent hash. You can get it from [torrent list](#get-torrent-list). + +Name: `pieceHashes` + +**Parameters:** + +Parameter | Type | Description +----------|--------|------------ +`hash` | string | The hash of the torrent you want to get the pieces' hashes of + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +404 | Torrent hash was not found +200 | All other scenarios- see JSON below + +The response is: + +- empty, if the torrent hash is invalid +- otherwise, an array of hashes (strings) of all pieces (in order) of a specific torrent. + +Example: + +```JSON +["54eddd830a5b58480a6143d616a97e3a6c23c439","f8a99d225aa4241db100f88407fc3bdaead583ab","928fb615b9bd4dd8f9e9022552c8f8f37ef76f58"] +``` + +## Pause torrents ## + +Requires knowing the torrent hashes. You can get it from [torrent list](#get-torrent-list). + +Name: `pause` + +**Parameters:** + +Parameter | Type | Description +------------|----------|------------ +`hashes` | string | The hashes of the torrents you want to pause. `hashes` can contain multiple hashes separated by `\|`, to pause multiple torrents, or set to `all`, to pause all torrents. + +Example: + +```http +/api/v2/torrents/pause?hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32|54eddd830a5b58480a6143d616a97e3a6c23c439 +``` + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios + +## Resume torrents ## + +Requires knowing the torrent hashes. You can get it from [torrent list](#get-torrent-list). + +Name: `resume` + +**Parameters:** + +Parameter | Type | Description +------------|----------|------------ +`hashes` | string | The hashes of the torrents you want to resume. `hashes` can contain multiple hashes separated by `\|`, to resume multiple torrents, or set to `all`, to resume all torrents. + +Example: + +```http +/api/v2/torrents/resume?hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32|54eddd830a5b58480a6143d616a97e3a6c23c439 +``` + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios + +## Delete torrents ## + +Requires knowing the torrent hashes. You can get it from [torrent list](#get-torrent-list). + +Name: `delete` + +**Parameters:** + +Parameter | Type | Description +------------|----------|------------ +`hashes` | string | The hashes of the torrents you want to delete. `hashes` can contain multiple hashes separated by `\|`, to delete multiple torrents, or set to `all`, to delete all torrents. +`deleteFiles` | If set to `true`, the downloaded data will also be deleted, otherwise has no effect. + +Example: + +```http +/api/v2/torrents/delete?hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32&deleteFiles=false +``` + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios + +## Recheck torrents ## + +Requires knowing the torrent hashes. You can get it from [torrent list](#get-torrent-list). + +Name: `recheck` + +**Parameters:** + +Parameter | Type | Description +------------|----------|------------ +`hashes` | string | The hashes of the torrents you want to recheck. `hashes` can contain multiple hashes separated by `\|`, to recheck multiple torrents, or set to `all`, to recheck all torrents. + +Example: + +```http +/api/v2/torrents/recheck?hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32|54eddd830a5b58480a6143d616a97e3a6c23c439 +``` + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios + +## Reannounce torrents ## + +Requires knowing the torrent hashes. You can get it from [torrent list](#get-torrent-list). + +Name: `reannounce` + +**Parameters:** + +Parameter | Type | Description +------------|----------|------------ +`hashes` | string | The hashes of the torrents you want to reannounce. `hashes` can contain multiple hashes separated by `\|`, to reannounce multiple torrents, or set to `all`, to reannounce all torrents. + +Example: + +```http +/api/v2/torrents/reannounce?hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32|54eddd830a5b58480a6143d616a97e3a6c23c439 +``` + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios + +## Add new torrent ## + +This method can add torrents from server local file or from URLs. `http://`, `https://`, `magnet:` and `bc://bt/` links are supported. + +Name: `add` + +Add torrent from URLs example: + +```http +POST /api/v2/torrents/add HTTP/1.1 +User-Agent: Fiddler +Host: 127.0.0.1 +Cookie: SID=your_sid +Content-Type: multipart/form-data; boundary=---------------------------6688794727912 +Content-Length: length + +-----------------------------6688794727912 +Content-Disposition: form-data; name="urls" + +https://torcache.net/torrent/3B1A1469C180F447B77021074DBBCCAEF62611E7.torrent +https://torcache.net/torrent/3B1A1469C180F447B77021074DBBCCAEF62611E8.torrent +-----------------------------6688794727912 +Content-Disposition: form-data; name="savepath" + +C:/Users/qBit/Downloads +-----------------------------6688794727912 +Content-Disposition: form-data; name="cookie" + +ui=28979218048197 +-----------------------------6688794727912 +Content-Disposition: form-data; name="category" + +movies +-----------------------------6688794727912 +Content-Disposition: form-data; name="skip_checking" + +true +-----------------------------6688794727912 +Content-Disposition: form-data; name="paused" + +true +-----------------------------6688794727912 +Content-Disposition: form-data; name="root_folder" + +true +-----------------------------6688794727912-- +``` + +Add torrents from files example: + +```http +POST /api/v2/torrents/add HTTP/1.1 +Content-Type: multipart/form-data; boundary=-------------------------acebdf13572468 +User-Agent: Fiddler +Host: 127.0.0.1 +Cookie: SID=your_sid +Content-Length: length + +---------------------------acebdf13572468 +Content-Disposition: form-data; name="torrents"; filename="8f18036b7a205c9347cb84a253975e12f7adddf2.torrent" +Content-Type: application/x-bittorrent + +file_binary_data_goes_here +---------------------------acebdf13572468 +Content-Disposition: form-data; name="torrents"; filename="UFS.torrent" +Content-Type: application/x-bittorrent + +file_binary_data_goes_here +---------------------------acebdf13572468-- + +``` + +The above example will add two torrent files. `file_binary_data_goes_here` represents raw data of torrent file (basically a byte array). + +**Parameters:** + +Property | Type | Description +--------------------------------|---------|------------ +`urls` | string | URLs separated with newlines +`torrents` | raw | Raw data of torrent file. `torrents` can be presented multiple times. +`savepath` _optional_ | string | Download folder +`cookie` _optional_ | string | Cookie sent to download the .torrent file +`category` _optional_ | string | Category for the torrent +`tags` _optional_ | string | Tags for the torrent, split by ',' +`skip_checking` _optional_ | string | Skip hash checking. Possible values are `true`, `false` (default) +`paused` _optional_ | string | Add torrents in the paused state. Possible values are `true`, `false` (default) +`root_folder` _optional_ | string | Create the root folder. Possible values are `true`, `false`, unset (default) +`rename` _optional_ | string | Rename torrent +`upLimit` _optional_ | integer | Set torrent upload speed limit. Unit in bytes/second +`dlLimit` _optional_ | integer | Set torrent download speed limit. Unit in bytes/second +`ratioLimit` _optional_ since 2.8.1 | float | Set torrent share ratio limit +`seedingTimeLimit` _optional_ since 2.8.1 | integer | Set torrent seeding time limit. Unit in seconds +`autoTMM` _optional_ | bool | Whether Automatic Torrent Management should be used +`sequentialDownload` _optional_ | string | Enable sequential download. Possible values are `true`, `false` (default) +`firstLastPiecePrio` _optional_ | string | Prioritize download first last piece. Possible values are `true`, `false` (default) + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +415 | Torrent file is not valid +200 | All other scenarios + +## Add trackers to torrent ## + +Requires knowing the torrent hash. You can get it from [torrent list](#get-torrent-list). + +Name: `addTrackers` + +```http +POST /api/v2/torrents/addTrackers HTTP/1.1 +User-Agent: Fiddler +Host: 127.0.0.1 +Cookie: SID=your_sid +Content-Type: application/x-www-form-urlencoded +Content-Length: length + +hash=8c212779b4abde7c6bc608063a0d008b7e40ce32&urls=http://192.168.0.1/announce%0Audp://192.168.0.1:3333/dummyAnnounce +``` + +This adds two trackers to torrent with hash `8c212779b4abde7c6bc608063a0d008b7e40ce32`. Note `%0A` (aka LF newline) between trackers. Ampersand in tracker urls **MUST** be escaped. + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +404 | Torrent hash was not found +200 | All other scenarios + +## Edit trackers ## + +Name: `editTracker` + +**Parameters:** + +Parameter | Type | Description +----------------------------------|---------|------------ +`hash` | string | The hash of the torrent +`origUrl` | string | The tracker URL you want to edit +`newUrl` | string | The new URL to replace the `origUrl` + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +400 | `newUrl` is not a valid URL +404 | Torrent hash was not found +409 | `newUrl` already exists for the torrent +409 | `origUrl` was not found +200 | All other scenarios + +## Remove trackers ## + +Name: `removeTrackers` + +**Parameters:** + +Parameter | Type | Description +----------------------------------|---------|------------ +`hash` | string | The hash of the torrent +`urls` | string | URLs to remove, separated by `\|` + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +404 | Torrent hash was not found +409 | All `urls` were not found +200 | All other scenarios + +## Add peers ## + +Name: `addPeers` + +**Parameters:** + +Parameter | Type | Description +----------------------------------|---------|------------ +`hashes` | string | The hash of the torrent, or multiple hashes separated by a pipe `\|` +`peers` | string | The peer to add, or multiple peers separated by a pipe `\|`. Each peer is a colon-separated `host:port` + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +400 | None of the supplied peers are valid +200 | All other scenarios + +## Increase torrent priority ## + +Requires knowing the torrent hash. You can get it from [torrent list](#get-torrent-list). + +Name: `increasePrio` + +**Parameters:** + +Parameter | Type | Description +------------|----------|------------ +`hashes` | string | The hashes of the torrents you want to increase the priority of. `hashes` can contain multiple hashes separated by `\|`, to increase the priority of multiple torrents, or set to `all`, to increase the priority of all torrents. + +Example: + +```http +/api/v2/torrents/increasePrio?hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32|54eddd830a5b58480a6143d616a97e3a6c23c439 +``` + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +409 | Torrent queueing is not enabled +200 | All other scenarios + +## Decrease torrent priority ## + +Requires knowing the torrent hash. You can get it from [torrent list](#get-torrent-list). + +Name: `decreasePrio` + +**Parameters:** + +Parameter | Type | Description +------------|----------|------------ +`hashes` | string | The hashes of the torrents you want to decrease the priority of. `hashes` can contain multiple hashes separated by `\|`, to decrease the priority of multiple torrents, or set to `all`, to decrease the priority of all torrents. + +Example: + +```http +/api/v2/torrents/decreasePrio?hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32|54eddd830a5b58480a6143d616a97e3a6c23c439 +``` + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +409 | Torrent queueing is not enabled +200 | All other scenarios + +## Maximal torrent priority ## + +Requires knowing the torrent hash. You can get it from [torrent list](#get-torrent-list). + +Name: `topPrio` + +**Parameters:** + +Parameter | Type | Description +------------|----------|------------ +`hashes` | string | The hashes of the torrents you want to set to the maximum priority. `hashes` can contain multiple hashes separated by `\|`, to set multiple torrents to the maximum priority, or set to `all`, to set all torrents to the maximum priority. + +Example: + +```http +/api/v2/torrents/topPrio?hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32|54eddd830a5b58480a6143d616a97e3a6c23c439 +``` + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +409 | Torrent queueing is not enabled +200 | All other scenarios + +## Minimal torrent priority ## + +Requires knowing the torrent hash. You can get it from [torrent list](#get-torrent-list). + +Name: `bottomPrio` + +**Parameters:** + +Parameter | Type | Description +------------|----------|------------ +`hashes` | string | The hashes of the torrents you want to set to the minimum priority. `hashes` can contain multiple hashes separated by `\|`, to set multiple torrents to the minimum priority, or set to `all`, to set all torrents to the minimum priority. + +Example: + +```http +/api/v2/torrents/bottomPrio?hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32|54eddd830a5b58480a6143d616a97e3a6c23c439 +``` + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +409 | Torrent queueing is not enabled +200 | All other scenarios + +## Set file priority ## + +Name: `filePrio` + +**Parameters:** + +Parameter | Type | Description +----------------------------------|---------|------------ +`hash` | string | The hash of the torrent +`id` | string | File ids, separated by `\|` +`priority` | number | File priority to set (consult [torrent contents API](#get-torrent-contents) for possible values) + +`id` values correspond to file position inside the array returned by [torrent contents API](#get-torrent-contents), e.g. `id=0` for first file, `id=1` for second file, etc. + +Since 2.8.2 it is reccomended to use `index` field returned by [torrent contents API](#get-torrent-contents) (since the files can be filtered and the `index` value may differ from the position inside the response array). + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +400 | Priority is invalid +400 | At least one file `id` is not a valid integer +404 | Torrent hash was not found +409 | Torrent metadata hasn't downloaded yet +409 | At least one file `id` was not found +200 | All other scenarios + +## Get torrent download limit ## + +Requires knowing the torrent hash. You can get it from [torrent list](#get-torrent-list). + +Name: `downloadLimit` + +```http +POST /api/v2/torrents/downloadLimit HTTP/1.1 +User-Agent: Fiddler +Host: 127.0.0.1 +Cookie: SID=your_sid +Content-Type: application/x-www-form-urlencoded +Content-Length: length + +hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32|284b83c9c7935002391129fd97f43db5d7cc2ba0 +``` + +`hashes` can contain multiple hashes separated by `|` or set to `all` + +Server reply (example): + +```http +HTTP/1.1 200 OK +content-type: application/json +content-length: length + +{"8c212779b4abde7c6bc608063a0d008b7e40ce32":338944,"284b83c9c7935002391129fd97f43db5d7cc2ba0":123} +``` + +`8c212779b4abde7c6bc608063a0d008b7e40ce32` is the hash of the torrent and `338944` its download speed limit in bytes per second; this value will be zero if no limit is applied. + +## Set torrent download limit ## + +Requires knowing the torrent hash. You can get it from [torrent list](#get-torrent-list). + +```http +POST /api/v2/torrents/setDownloadLimit HTTP/1.1 +User-Agent: Fiddler +Host: 127.0.0.1 +Cookie: SID=your_sid +Content-Type: application/x-www-form-urlencoded +Content-Length: length + +hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32|284b83c9c7935002391129fd97f43db5d7cc2ba0&limit=131072 +``` + +`hashes` can contain multiple hashes separated by `|` or set to `all` +`limit` is the download speed limit in bytes per second you want to set. + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios + +## Set torrent share limit ## + +Requires knowing the torrent hash. You can get it from [torrent list](#get-torrent-list). + +Name: `setShareLimits` + +```http +POST /api/v2/torrents/setShareLimits HTTP/1.1 +User-Agent: Fiddler +Host: 127.0.0.1 +Cookie: SID=your_sid +Content-Type: application/x-www-form-urlencoded +Content-Length: length + +hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32|284b83c9c7935002391129fd97f43db5d7cc2ba0&ratioLimit=1.0&seedingTimeLimit=60 +``` + +`hashes` can contain multiple hashes separated by `|` or set to `all` +`ratioLimit` is the max ratio the torrent should be seeded until. `-2` means the global limit should be used, `-1` means no limit. +`seedingTimeLimit` is the max amount of time the torrent should be seeded. `-2` means the global limit should be used, `-1` means no limit. + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios + +## Get torrent upload limit ## + +Requires knowing the torrent hash. You can get it from [torrent list](#get-torrent-list). + +Name: `uploadLimit` + +```http +POST /api/v2/torrents/uploadLimit HTTP/1.1 +User-Agent: Fiddler +Host: 127.0.0.1 +Cookie: SID=your_sid +Content-Type: application/x-www-form-urlencoded +Content-Length: length + +hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32|284b83c9c7935002391129fd97f43db5d7cc2ba0 +``` + +`hashes` can contain multiple hashes separated by `|` or set to `all` + +Server reply (example): + +```http +HTTP/1.1 200 OK +content-type: application/json +content-length: length + +{"8c212779b4abde7c6bc608063a0d008b7e40ce32":338944,"284b83c9c7935002391129fd97f43db5d7cc2ba0":123} +``` + +`8c212779b4abde7c6bc608063a0d008b7e40ce32` is the hash of the torrent in the request and `338944` its upload speed limit in bytes per second; this value will be zero if no limit is applied. + +## Set torrent upload limit ## + +Requires knowing the torrent hash. You can get it from [torrent list](#get-torrent-list). + +Name: `setUploadLimit` + +```http +POST /api/v2/torrents/setUploadLimit HTTP/1.1 +User-Agent: Fiddler +Host: 127.0.0.1 +Cookie: SID=your_sid +Content-Type: application/x-www-form-urlencoded +Content-Length: length + +hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32|284b83c9c7935002391129fd97f43db5d7cc2ba0&limit=131072 +``` + +`hashes` can contain multiple hashes separated by `|` or set to `all` +`limit` is the upload speed limit in bytes per second you want to set. + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios + +## Set torrent location ## + +Requires knowing the torrent hash. You can get it from [torrent list](#get-torrent-list). + +Name: `setLocation` + +```http +POST /api/v2/torrents/setLocation HTTP/1.1 +User-Agent: Fiddler +Host: 127.0.0.1 +Cookie: SID=your_sid +Content-Type: application/x-www-form-urlencoded +Content-Length: length + +hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32|284b83c9c7935002391129fd97f43db5d7cc2ba0&location=/mnt/nfs/media +``` + +`hashes` can contain multiple hashes separated by `|` or set to `all` +`location` is the location to download the torrent to. If the location doesn't exist, the torrent's location is unchanged. + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +400 | Save path is empty +403 | User does not have write access to directory +409 | Unable to create save path directory +200 | All other scenarios + +## Set torrent name ## + +Requires knowing the torrent hash. You can get it from [torrent list](#get-torrent-list). + +Name: `rename` + +```http +POST /api/v2/torrents/rename HTTP/1.1 +User-Agent: Fiddler +Host: 127.0.0.1 +Cookie: SID=your_sid +Content-Type: application/x-www-form-urlencoded +Content-Length: length + +hash=8c212779b4abde7c6bc608063a0d008b7e40ce32&name=This%20is%20a%20test +``` + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +404 | Torrent hash is invalid +409 | Torrent name is empty +200 | All other scenarios + +## Set torrent category ## + +Requires knowing the torrent hash. You can get it from [torrent list](#get-torrent-list). + +Name: `setCategory` + +```http +POST /api/v2/torrents/setCategory HTTP/1.1 +User-Agent: Fiddler +Host: 127.0.0.1 +Cookie: SID=your_sid +Content-Type: application/x-www-form-urlencoded +Content-Length: length + +hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32|284b83c9c7935002391129fd97f43db5d7cc2ba0&category=CategoryName +``` + +`hashes` can contain multiple hashes separated by `|` or set to `all` + +`category` is the torrent category you want to set. + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +409 | Category name does not exist +200 | All other scenarios + +## Get all categories ## + +Name: `categories` + +Parameters: + +None + +Returns all categories in JSON format, e.g.: + +```JSON +{ + "Video": { + "name": "Video", + "savePath": "/home/user/torrents/video/" + }, + "eBooks": { + "name": "eBooks", + "savePath": "/home/user/torrents/eBooks/" + } +} +``` +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios + +## Add new category ## + +Name: `createCategory` + +```http +POST /api/v2/torrents/createCategory HTTP/1.1 +User-Agent: Fiddler +Host: 127.0.0.1 +Cookie: SID=your_sid +Content-Type: application/x-www-form-urlencoded +Content-Length: length + +category=CategoryName&savePath=/path/to/dir +``` + +`category` is the category you want to create. + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +400 | Category name is empty +409 | Category name is invalid +200 | All other scenarios + +## Edit category ## + +Name: `editCategory` + +```http +POST /api/v2/torrents/editCategory HTTP/1.1 +User-Agent: Fiddler +Host: 127.0.0.1 +Cookie: SID=your_sid +Content-Type: application/x-www-form-urlencoded +Content-Length: length + +category=CategoryName&savePath=/path/to/save/torrents/to +``` + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +400 | Category name is empty +409 | Category editing failed +200 | All other scenarios + +## Remove categories ## + +Name: `removeCategories` + +```http +POST /api/v2/torrents/removeCategories HTTP/1.1 +User-Agent: Fiddler +Host: 127.0.0.1 +Cookie: SID=your_sid +Content-Type: application/x-www-form-urlencoded +Content-Length: length + +categories=Category1%0ACategory2 +``` + +`categories` can contain multiple cateogies separated by `\n` (%0A urlencoded) + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios + +## Add torrent tags ## + +Requires knowing the torrent hash. You can get it from [torrent list](#get-torrent-list). + +Name: `addTags` + +```http +POST /api/v2/torrents/addTags HTTP/1.1 +User-Agent: Fiddler +Host: 127.0.0.1 +Cookie: SID=your_sid +Content-Type: application/x-www-form-urlencoded +Content-Length: length + +hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32|284b83c9c7935002391129fd97f43db5d7cc2ba0&tags=TagName1,TagName2 +``` + +`hashes` can contain multiple hashes separated by `|` or set to `all` + +`tags` is the list of tags you want to add to passed torrents. + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios + +## Remove torrent tags ## + +Requires knowing the torrent hash. You can get it from [torrent list](#get-torrent-list). + +Name: `removeTags` + +```http +POST /api/v2/torrents/removeTags HTTP/1.1 +User-Agent: Fiddler +Host: 127.0.0.1 +Cookie: SID=your_sid +Content-Type: application/x-www-form-urlencoded +Content-Length: length + +hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32|284b83c9c7935002391129fd97f43db5d7cc2ba0&tags=TagName1,TagName2 +``` + +`hashes` can contain multiple hashes separated by `|` or set to `all` + +`tags` is the list of tags you want to remove from passed torrents. +Empty list removes all tags from relevant torrents. + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios + +## Get all tags ## + +Name: `tags` + +Parameters: + +None + +Returns all tags in JSON format, e.g.: + +```JSON +[ + "Tag 1", + "Tag 2" +] +``` +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios + +## Create tags ## + +Name: `createTags` + +```http +POST /api/v2/torrents/createTags HTTP/1.1 +User-Agent: Fiddler +Host: 127.0.0.1 +Cookie: SID=your_sid +Content-Type: application/x-www-form-urlencoded +Content-Length: length + +tags=TagName1,TagName2 +``` +`tags` is a list of tags you want to create. +Can contain multiple tags separated by `,`. + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios + +## Delete tags ## + +Name: `deleteTags` + +```http +POST /api/v2/torrents/deleteTags HTTP/1.1 +User-Agent: Fiddler +Host: 127.0.0.1 +Cookie: SID=your_sid +Content-Type: application/x-www-form-urlencoded +Content-Length: length + +tags=TagName1,TagName2 +``` + +`tags` is a list of tags you want to delete. +Can contain multiple tags separated by `,`. + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios + +## Set automatic torrent management ## + +Requires knowing the torrent hash. You can get it from [torrent list](#get-torrent-list). + +Name: `setAutoManagement` + +```http +POST /api/v2/torrents/setAutoManagement HTTP/1.1 +User-Agent: Fiddler +Host: 127.0.0.1 +Cookie: SID=your_sid +Content-Type: application/x-www-form-urlencoded +Content-Length: length + +hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32|284b83c9c7935002391129fd97f43db5d7cc2ba0&enable=true +``` + +`hashes` can contain multiple hashes separated by `|` or set to `all` +`enable` is a boolean, affects the torrents listed in `hashes`, default is `false` + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios + +## Toggle sequential download ## + +Requires knowing the torrent hash. You can get it from [torrent list](#get-torrent-list). + +Name: `toggleSequentialDownload` + +**Parameters:** + +Parameter | Type | Description +------------|----------|------------ +`hashes` | string | The hashes of the torrents you want to toggle sequential download for. `hashes` can contain multiple hashes separated by `\|`, to toggle sequential download for multiple torrents, or set to `all`, to toggle sequential download for all torrents. + +Example: + +```http +/api/v2/torrents/toggleSequentialDownload?hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32|54eddd830a5b58480a6143d616a97e3a6c23c439 +``` + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios + +## Set first/last piece priority ## + +Requires knowing the torrent hash. You can get it from [torrent list](#get-torrent-list). + +Name: `toggleFirstLastPiecePrio` + +**Parameters:** + +Parameter | Type | Description +------------|----------|------------ +`hashes` | string | The hashes of the torrents you want to toggle the first/last piece priority for. `hashes` can contain multiple hashes separated by `\|`, to toggle the first/last piece priority for multiple torrents, or set to `all`, to toggle the first/last piece priority for all torrents. + +Example: + +```http +/api/v2/torrents/toggleFirstLastPiecePrio?hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32|54eddd830a5b58480a6143d616a97e3a6c23c439 +``` + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios + +## Set force start ## + +Requires knowing the torrent hash. You can get it from [torrent list](#get-torrent-list). + +Name: `setForceStart` + +```http +POST /api/v2/torrents/setForceStart HTTP/1.1 +User-Agent: Fiddler +Host: 127.0.0.1 +Cookie: SID=your_sid +Content-Type: application/x-www-form-urlencoded +Content-Length: length + +hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32?value=true +``` + +`hashes` can contain multiple hashes separated by `|` or set to `all` +`value` is a boolean, affects the torrents listed in `hashes`, default is `false` + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios + +## Set super seeding ## + +Requires knowing the torrent hash. You can get it from [torrent list](#get-torrent-list). + +Name: `setSuperSeeding` + +```http +POST /api/v2/torrents/setSuperSeeding HTTP/1.1 +User-Agent: Fiddler +Host: 127.0.0.1 +Cookie: SID=your_sid +Content-Type: application/x-www-form-urlencoded +Content-Length: length + +hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32?value=true +``` + +`hashes` can contain multiple hashes separated by `|` or set to `all` +`value` is a boolean, affects the torrents listed in `hashes`, default is `false` + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios + +## Rename file ## + +Name: `renameFile` + +**Parameters:** + +Parameter | Type | Description +----------------------------------|----------|------------ +`hash` | string | The hash of the torrent +`oldPath` | string | The old path of the torrent +`newPath` | string | The new path to use for the file + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +400 | Missing `newPath` parameter +409 | Invalid `newPath` or `oldPath`, or `newPath` already in use +200 | All other scenarios + +## Rename folder ## + +Name: `renameFolder` + +**Parameters:** + +Parameter | Type | Description +----------------------------------|----------|------------ +`hash` | string | The hash of the torrent +`oldPath` | string | The old path of the torrent +`newPath` | string | The new path to use for the file + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +400 | Missing `newPath` parameter +409 | Invalid `newPath` or `oldPath`, or `newPath` already in use +200 | All other scenarios + +# RSS (experimental) # + +All RSS API methods are under "rss", e.g.: `/api/v2/rss/methodName`. + +## Add folder ## + +Name: `addFolder` + +Parameters: + +Parameter | Type | Description +----------------------------------|---------|------------ +`path` | string | Full path of added folder (e.g. "The Pirate Bay\Top100") + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +409 | Failure to add folder +200 | All other scenarios + +## Add feed ## + +Name: `addFeed` + +Parameters: + +Parameter | Type | Description +----------------------------------|---------|------------ +`url` | string | URL of RSS feed (e.g. "[http://thepiratebay.org/rss//top100/200](http://thepiratebay.org/rss//top100/200)") +`path` _optional_ | string | Full path of added folder (e.g. "The Pirate Bay\Top100\Video") + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +409 | Failure to add feed +200 | All other scenarios + +## Remove item ## + +Removes folder or feed. + +Name: `removeItem` + +Parameters: + +Parameter | Type | Description +----------------------------------|---------|------------ +`path` | string | Full path of removed item (e.g. "The Pirate Bay\Top100") + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +409 | Failure to remove item +200 | All other scenarios + +## Move item ## + +Moves/renames folder or feed. + +Name: `moveItem` + +Parameters: + +Parameter | Type | Description +----------------------------------|---------|------------ +`itemPath` | string | Current full path of item (e.g. "The Pirate Bay\Top100") +`destPath` | string | New full path of item (e.g. "The Pirate Bay") + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +409 | Failure to move item +200 | All other scenarios + +## Get all items ## + +Name: `items` + +Parameters: + +Parameter | Type | Description +----------------------------------|---------|------------ +`withData` _optional_ | bool | True if you need current feed articles + +Returns all RSS items in JSON format, e.g.: + +```JSON +{ + "HD-Torrents.org": "https://hd-torrents.org/rss.php", + "PowerfulJRE": "https://www.youtube.com/feeds/videos.xml?channel_id=UCzQUP1qoWDoEbmsQxvdjxgQ", + "The Pirate Bay": { + "Audio": "https://thepiratebay.org/rss//top100/100", + "Video": "https://thepiratebay.org/rss//top100/200" + } +} +``` + +## Mark as read ## + +If `articleId` is provided only the article is marked as read otherwise the whole feed is going to be marked as read. + +Name: `markAsRead` + +Parameters: + +Parameter | Type | Description +----------------------------------|---------|------------ +`itemPath` | string | Current full path of item (e.g. "The Pirate Bay\Top100") +`articleId` _optional_ | string | ID of article + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios + +## Refresh item ## + +Refreshes folder or feed. + +Name: `refreshItem` + +Parameters: + +Parameter | Type | Description +----------------------------------|---------|------------ +`itemPath` | string | Current full path of item (e.g. "The Pirate Bay\Top100") + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios + +## Set auto-downloading rule ## + +Name: `setRule` + +Parameters: + +Parameter | Type | Description +----------------------------------|---------|------------ +`ruleName` | string | Rule name (e.g. "Punisher") +`ruleDef` | string | JSON encoded rule definition + +Rule definition is JSON encoded dictionary with the following fields: + +Field | Type | Description +----------------------------------|---------|------------ +`enabled` | bool | Whether the rule is enabled +`mustContain` | string | The substring that the torrent name must contain +`mustNotContain` | string | The substring that the torrent name must not contain +`useRegex` | bool | Enable regex mode in "mustContain" and "mustNotContain" +`episodeFilter` | string | Episode filter definition +`smartFilter` | bool | Enable smart episode filter +`previouslyMatchedEpisodes` | list | The list of episode IDs already matched by smart filter +`affectedFeeds` | list | The feed URLs the rule applied to +`ignoreDays` | number | Ignore sunsequent rule matches +`lastMatch` | string | The rule last match time +`addPaused` | bool | Add matched torrent in paused mode +`assignedCategory` | string | Assign category to the torrent +`savePath` | string | Save torrent to the given directory + +E.g.: + +```JSON +{ + "enabled": false, + "mustContain": "The *Punisher*", + "mustNotContain": "", + "useRegex": false, + "episodeFilter": "1x01-;", + "smartFilter": false, + "previouslyMatchedEpisodes": [ + ], + "affectedFeeds": [ + "http://showrss.info/user/134567.rss?magnets=true" + ], + "ignoreDays": 0, + "lastMatch": "20 Nov 2017 09:05:11", + "addPaused": true, + "assignedCategory": "", + "savePath": "C:/Users/JohnDoe/Downloads/Punisher" +} +``` + +## Rename auto-downloading rule ## + +Name: `renameRule` + +Parameters: + +Parameter | Type | Description +----------------------------------|---------|------------ +`ruleName` | string | Rule name (e.g. "Punisher") +`newRuleName` | string | New rule name (e.g. "The Punisher") + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios + +## Remove auto-downloading rule ## + +Name: `removeRule` + +Parameters: + +Parameter | Type | Description +----------------------------------|---------|------------ +`ruleName` | string | Rule name (e.g. "Punisher") + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios + + +## Get all auto-downloading rules ## + +Name: `rules` + +Returns all auto-downloading rules in JSON format, e.g.: + +```JSON +{ + "The Punisher": { + "enabled": false, + "mustContain": "The *Punisher*", + "mustNotContain": "", + "useRegex": false, + "episodeFilter": "1x01-;", + "smartFilter": false, + "previouslyMatchedEpisodes": [ + ], + "affectedFeeds": [ + "http://showrss.info/user/134567.rss?magnets=true" + ], + "ignoreDays": 0, + "lastMatch": "20 Nov 2017 09:05:11", + "addPaused": true, + "assignedCategory": "", + "savePath": "C:/Users/JohnDoe/Downloads/Punisher" + } +} +``` +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios + +## Get all articles matching a rule ## + +Name: `matchingArticles` + +Parameter | Type | Description +----------------------------------|---------|------------ +`ruleName` | string | Rule name (e.g. "Linux") + + +Returns all articles that match a rule by feed name in JSON format, e.g.: + +```JSON +{ + "DistroWatch":[ + "sparkylinux-5.11-i686-minimalgui.iso.torrent", + "sparkylinux-5.11-x86_64-minimalgui.iso.torrent", + "sparkylinux-5.11-i686-xfce.iso.torrent", + "bluestar-linux-5.6.3-2020.04.09-x86_64.iso.torrent", + "robolinux64-mate3d-v10.10.iso.torrent", + ], + "Linuxtracker":[ + "[Alpine Linux] alpine-extended-3.11.6", + "[Alpine Linux] alpine-standard-3.11.6", + "[Linuxfx] linuxfx10-wxs-lts-beta5.iso", + "[Linux Lite] linux-lite-5.0-rc1-64bit.iso (MULTI)", + "[Scientific Linux] SL-7.8-x86_64-Pack", + "[NixOS] nixos-plasma5-20.03.1418.5272327b81e-x86_64-linux.iso" + ] +} +``` + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios + + +# Search # + +All Search API methods are under "search", e.g.: `/api/v2/search/methodName`. + +## Start search ## + +Name: `start` + +**Parameters:** + +Parameter | Type | Description +----------------------------------|---------|------------ +`pattern` | string | Pattern to search for (e.g. "Ubuntu 18.04") +`plugins` | string | Plugins to use for searching (e.g. "legittorrents"). Supports multiple plugins separated by `\|`. Also supports `all` and `enabled` +`category` | string | Categories to limit your search to (e.g. "legittorrents"). Available categories depend on the specified `plugins`. Also supports `all` + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +409 | User has reached the limit of max `Running` searches (currently set to 5) +200 | All other scenarios- see JSON below + +The response is a JSON object with the following fields + +Field | Type | Description +----------------------------------|---------|------------ +`id` | number | ID of the search job + +Example: + +```JSON +{ + "id": 12345 +} +``` + +## Stop search ## + +Name: `stop` + +**Parameters:** + +Parameter | Type | Description +----------------------------------|---------|------------ +`id` | number | ID of the search job + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +404 | Search job was not found +200 | All other scenarios + +## Get search status ## + +Name: `status` + +**Parameters:** + +Parameter | Type | Description +----------------------------------|---------|------------ +`id` _optional_ | number | ID of the search job. If not specified, all search jobs are returned + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +404 | Search job was not found +200 | All other scenarios- see JSON below + +The response is a JSON array of objects containing the following fields + +Field | Type | Description +----------------------------------|---------|------------ +`id` | number | ID of the search job +`status` | string | Current status of the search job (either `Running` or `Stopped`) +`total` | number | Total number of results. If the status is `Running` this number may contineu to increase + +Example: + +```JSON +[ + { + "id": 12345, + "status": "Running", + "total": 170 + } +] +``` + +## Get search results ## + +Name: `results` + +**Parameters:** + +Parameter | Type | Description +----------------------------------|---------|------------ +`id` | number | ID of the search job +`limit` _optional_ | number | max number of results to return. 0 or negative means no limit +`offset` _optional_ | number | result to start at. A negative number means count backwards (e.g. `-2` returns the 2 most recent results) + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +404 | Search job was not found +409 | Offset is too large, or too small (e.g. absolute value of negative number is greater than # results) +200 | All other scenarios- see JSON below + +The response is a JSON object with the following fields + +Field | Type | Description +----------------------------------|---------|------------ +`results` | array | Array of `result` objects- see table below +`status` | string | Current status of the search job (either `Running` or `Stopped`) +`total` | number | Total number of results. If the status is `Running` this number may continue to increase + +**Result object:** + +Field | Type | Description +----------------------------------|---------|------------ +`descrLink` | string | URL of the torrent's description page +`fileName` | string | Name of the file +`fileSize` | number | Size of the file in Bytes +`fileUrl` | string | Torrent download link (usually either .torrent file or magnet link) +`nbLeechers` | number | Number of leechers +`nbSeeders` | number | Number of seeders +`siteUrl` | string | URL of the torrent site + +Example: + +```JSON +{ + "results": [ + { + "descrLink": "http://www.legittorrents.info/index.php?page=torrent-details&id=8d5f512e1acb687029b8d7cc6c5a84dce51d7a41", + "fileName": "Ubuntu-10.04-32bit-NeTV.ova", + "fileSize": -1, + "fileUrl": "http://www.legittorrents.info/download.php?id=8d5f512e1acb687029b8d7cc6c5a84dce51d7a41&f=Ubuntu-10.04-32bit-NeTV.ova.torrent", + "nbLeechers": 1, + "nbSeeders": 0, + "siteUrl": "http://www.legittorrents.info" + }, + { + "descrLink": "http://www.legittorrents.info/index.php?page=torrent-details&id=d5179f53e105dc2c2401bcfaa0c2c4936a6aa475", + "fileName": "mangOH-Legato-17_06-Ubuntu-16_04.ova", + "fileSize": -1, + "fileUrl": "http://www.legittorrents.info/download.php?id=d5179f53e105dc2c2401bcfaa0c2c4936a6aa475&f=mangOH-Legato-17_06-Ubuntu-16_04.ova.torrent", + "nbLeechers": 0, + "nbSeeders": 59, + "siteUrl": "http://www.legittorrents.info" + } + ], + "status": "Running", + "total": 2 +} +``` + +## Delete search ## + +Name: `delete` + +**Parameters:** + +Parameter | Type | Description +----------------------------------|---------|------------ +`id` | number | ID of the search job + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +404 | Search job was not found +200 | All other scenarios + +## Get search plugins ## + +Name: `plugins` + +**Parameters:** + +None + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios- see JSON below + +The response is a JSON array of objects containing the following fields + +Field | Type | Description +----------------------------------|---------|------------ +`enabled` | bool | Whether the plugin is enabled +`fullName` | string | Full name of the plugin +`name` | string | Short name of the plugin +`supportedCategories` | array | List of category objects +`url` | string | URL of the torrent site +`version` | string | Installed version of the plugin + +```JSON +[ + { + "enabled": true, + "fullName": "Legit Torrents", + "name": "legittorrents", + "supportedCategories": [{ + "id": "all", + "name": "All categories" + }, { + "id": "anime", + "name": "Anime" + }, { + "id": "books", + "name": "Books" + }, { + "id": "games", + "name": "Games" + }, { + "id": "movies", + "name": "Movies" + }, { + "id": "music", + "name": "Music" + }, { + "id": "tv", + "name": "TV shows" + }], + "url": "http://www.legittorrents.info", + "version": "2.3" + } +] +``` + +## Install search plugin ## + +Name: `installPlugin` + +**Parameters:** + +Parameter | Type | Description +----------------------------------|---------|------------ +`sources` | string | Url or file path of the plugin to install (e.g. "[https://raw.githubusercontent.com/qbittorrent/search-plugins/master/nova3/engines/legittorrents.py](https://raw.githubusercontent.com/qbittorrent/search-plugins/master/nova3/engines/legittorrents.py)"). Supports multiple sources separated by `\|` + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios + +## Uninstall search plugin ## + +Name: `uninstallPlugin` + +**Parameters:** + +Parameter | Type | Description +----------------------------------|---------|------------ +`names` | string | Name of the plugin to uninstall (e.g. "legittorrents"). Supports multiple names separated by `\|` + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios + +## Enable search plugin ## + +Name: `enablePlugin` + +**Parameters:** + +Parameter | Type | Description +----------------------------------|---------|------------ +`names` | string | Name of the plugin to enable/disable (e.g. "legittorrents"). Supports multiple names separated by `\|` +`enable` | bool | Whether the plugins should be enabled + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios + +## Update search plugins ## + +Name: `updatePlugins` + +**Parameters:** + +None + +**Returns:** + +HTTP Status Code | Scenario +----------------------------------|--------------------- +200 | All scenarios + +# WebAPI versioning # +WebAPI uses the following versioning: `1.2.3`: +1. Main version. Should be changed only on some global changes (e.g. total redesign/relayout) +2. Changed on incompatible API changes (i.e. if it breaks outdated clients). E.g. if you change/remove something +3. Changed on compatible API changes (i.e. if it doesn't break outdated clients). E.g. if you add something new outdated clients still can access old subset of API. diff --git a/parser/src/group_parser/description.rs b/parser/src/group_parser/description.rs new file mode 100644 index 0000000..b83f8b3 --- /dev/null +++ b/parser/src/group_parser/description.rs @@ -0,0 +1,51 @@ +use md_parser::MdContent; + +pub fn get_group_description(content: &[MdContent]) -> Option { + let return_desc = content + .iter() + .map(|row| row.inner_value_as_string()) + .collect::>() + .join("\n") + .trim() + .to_string(); + + if return_desc.is_empty() { + None + } else { + Some(return_desc) + } +} + +pub fn get_method_description(content: &[MdContent]) -> Option { + let return_desc = content + .iter() + // skip until we get to the "Returns:" text + .skip_while(|row| match row { + MdContent::Asterix(text) => !text.starts_with("Returns:"), + _ => true, + }) + // there is one space before the table + .skip(2) + .skip_while(|row| match row { + MdContent::Text(text) => !text.is_empty(), + _ => true, + }) + // and there is one space after the table + .skip(1) + // then what is left should be the description + .flat_map(|row| match row { + MdContent::Text(text) => Some(text), + _ => None, + }) + .cloned() + .collect::>() + .join("\n") + .trim() + .to_string(); + + if return_desc.is_empty() { + None + } else { + Some(return_desc) + } +} diff --git a/parser/src/group_parser/mod.rs b/parser/src/group_parser/mod.rs new file mode 100644 index 0000000..4010bcb --- /dev/null +++ b/parser/src/group_parser/mod.rs @@ -0,0 +1,65 @@ +mod description; +mod parameters; +mod return_type; +mod url_parser; + +use md_parser::TokenTree; + +use crate::{util, ApiGroup, ApiMethod}; + +use self::{parameters::get_parameters, return_type::get_return_type}; + +pub fn parse_groups(trees: Vec) -> Vec { + trees.into_iter().map(parse_api_group).collect() +} + +fn parse_api_group(tree: TokenTree) -> ApiGroup { + let methods = tree + .children + .into_iter() + .flat_map(parse_api_method) + .collect(); + + let group_description = description::get_group_description(&tree.content); + let group_url = url_parser::get_group_url(&tree.content); + + let name = tree + .title + .unwrap() + .to_lowercase() + .trim_end_matches("(experimental)") + .trim() + .replace(' ', "_"); + + ApiGroup { + name, + methods, + description: group_description, + url: group_url, + } +} + +fn parse_api_method(child: TokenTree) -> Option { + util::find_content_starts_with(&child.content, "Name: ") + .map(|name| { + name.trim_start_matches("Name: ") + .trim_matches('`') + .to_string() + }) + .map(|name| to_api_method(&child, &name)) +} + +fn to_api_method(child: &TokenTree, name: &str) -> ApiMethod { + let method_description = description::get_method_description(&child.content); + let return_type = get_return_type(&child.content); + let parameters = get_parameters(&child.content); + let method_url = url_parser::get_method_url(&child.content); + + ApiMethod { + name: name.to_string(), + description: method_description, + parameters, + return_type, + url: method_url, + } +} diff --git a/parser/src/group_parser/parameters.rs b/parser/src/group_parser/parameters.rs new file mode 100644 index 0000000..0ba8e6f --- /dev/null +++ b/parser/src/group_parser/parameters.rs @@ -0,0 +1,48 @@ +use std::collections::HashMap; + +use md_parser::MdContent; + +use crate::types::{Type, OPTIONAL}; + +pub fn get_parameters(content: &[MdContent]) -> Option> { + let mut it = content + .iter() + .skip_while(|row| match row { + MdContent::Asterix(content) | MdContent::Text(content) => { + !content.starts_with("Parameters:") + } + _ => true, + }) + // Parameters: <-- skip + // <-- skip + // table with parameters <-- take + .skip(2); + + let parameter_table = match it.next() { + Some(MdContent::Table(table)) => table, + _ => return None, + }; + + // empty for now + let type_map = HashMap::default(); + + let table = parameter_table + .rows + .iter() + .flat_map(|row| { + let description = row.columns.get(2).cloned(); + + match &row.columns.get(2) { + // If the description contains a default value it means that the parameter is optional. + Some(desc) if desc.contains("default: ") => { + // type defines a variable as default if it contains: _optional_ + let name_with_optional = format!("{} {}", row.columns[0], OPTIONAL); + Type::from(&row.columns[1], &name_with_optional, description, &type_map) + } + _ => Type::from(&row.columns[1], &row.columns[0], description, &type_map), + } + }) + .collect(); + + Some(table) +} diff --git a/parser/src/group_parser/return_type.rs b/parser/src/group_parser/return_type.rs new file mode 100644 index 0000000..a7ab9bd --- /dev/null +++ b/parser/src/group_parser/return_type.rs @@ -0,0 +1,51 @@ +use md_parser::MdContent; + +use crate::{object_types::get_object_types, types::Type, ReturnType, ReturnTypeParameter}; + +pub fn get_return_type(content: &[MdContent]) -> Option { + let table = content + .iter() + // The response is a ... <-- Trying to find this line + // <-- The next line is empty + // Table with the return type <-- And then extract the following type table + .skip_while(|row| match row { + MdContent::Text(text) => !text.starts_with("The response is a"), + _ => true, + }) + .find_map(|row| match row { + MdContent::Table(table) => Some(table), + _ => None, + })?; + + let types = get_object_types(content); + + let parameters = table + .rows + .iter() + .map(|parameter| ReturnTypeParameter { + name: parameter.columns[0].clone(), + description: parameter.columns[2].clone(), + return_type: Type::from( + ¶meter.columns[1], + ¶meter.columns[0], + Some(parameter.columns[2].clone()), + &types, + ) + .unwrap_or_else(|| panic!("Failed to parse type {}", ¶meter.columns[1])), + }) + .collect(); + + let is_list = content + .iter() + .find_map(|row| match row { + MdContent::Text(text) if text.starts_with("The response is a") => Some(text), + _ => None, + }) + .map(|found| found.contains("array")) + .unwrap_or_else(|| false); + + Some(ReturnType { + parameters, + is_list, + }) +} diff --git a/parser/src/group_parser/url_parser.rs b/parser/src/group_parser/url_parser.rs new file mode 100644 index 0000000..3bb6be2 --- /dev/null +++ b/parser/src/group_parser/url_parser.rs @@ -0,0 +1,23 @@ +use md_parser::MdContent; +use regex::Regex; + +use crate::util; + +pub fn get_group_url(content: &[MdContent]) -> String { + let row = util::find_content_contains(content, "API methods are under") + .expect("Could not find api method"); + + let re = Regex::new(r#"All (?:\w+\s?)+ API methods are under "(\w+)", e.g."#) + .expect("Failed to create regex"); + + let res = re.captures(&row).expect("Failed find capture"); + res[1].to_string() +} + +pub fn get_method_url(content: &[MdContent]) -> String { + const START: &str = "Name: "; + + util::find_content_starts_with(content, START) + .map(|text| text.trim_start_matches(START).trim_matches('`').to_string()) + .expect("Could find method url") +} diff --git a/parser/src/lib.rs b/parser/src/lib.rs new file mode 100644 index 0000000..e055ec8 --- /dev/null +++ b/parser/src/lib.rs @@ -0,0 +1,85 @@ +mod group_parser; +mod object_types; +pub mod types; +mod util; + +use group_parser::parse_groups; +use md_parser::{self, TokenTree}; +use types::Type; + +#[derive(Debug)] +pub struct ApiGroup { + pub name: String, + pub methods: Vec, + pub description: Option, + pub url: String, +} + +#[derive(Debug)] +pub struct ApiMethod { + pub name: String, + pub description: Option, + pub parameters: Option>, + pub return_type: Option, + pub url: String, +} + +#[derive(Debug)] +pub struct ReturnType { + pub is_list: bool, + pub parameters: Vec, +} + +#[derive(Debug)] +pub struct ReturnTypeParameter { + pub name: String, + pub description: String, + pub return_type: Type, +} + +fn extract_relevant_parts(tree: TokenTree) -> Vec { + let relevant: Vec = tree + .children + .into_iter() + .skip_while(|row| match &row.title { + Some(title) => title != "Authentication", + None => false, + }) + .filter(|row| match &row.title { + Some(title) => title != "WebAPI versioning", + None => false, + }) + .collect(); + + relevant +} + +pub fn parse_api_groups(content: &str) -> Vec { + parse_groups(extract_relevant_parts(md_parser::TokenTreeFactory::create( + content, + ))) +} + +#[cfg(test)] +mod tests { + use super::*; + use std::fs; + + fn parse() -> TokenTree { + let content = include_str!("api-4_1.md"); + let md_tree = md_parser::TokenTreeFactory::create(content); + + let output = format!("{:#?}", md_tree); + fs::write("token_tree.txt", output).unwrap(); + + md_tree + } + + #[test] + fn it_works() { + let groups = parse_groups(extract_relevant_parts(parse())); + + let groups_as_str = format!("{:#?}", groups); + fs::write("groups.txt", groups_as_str).unwrap(); + } +} diff --git a/parser/src/object_types.rs b/parser/src/object_types.rs new file mode 100644 index 0000000..841b539 --- /dev/null +++ b/parser/src/object_types.rs @@ -0,0 +1,38 @@ +use std::collections::HashMap; + +use md_parser::MdContent; + +use crate::types::{TypeDescription, TypeDescriptions}; + +pub fn get_object_types(content: &[MdContent]) -> HashMap { + let mut output = HashMap::new(); + let mut content_it = content.iter(); + while let Some(entry) = content_it.next() { + if let MdContent::Text(content) = entry { + const POSSIBLE_VALUES_OF: &str = "Possible values of "; + if content.contains(POSSIBLE_VALUES_OF) { + // is empty + content_it.next(); + if let Some(MdContent::Table(table)) = content_it.next() { + let enum_types = table + .rows + .iter() + .map(|row| TypeDescriptions { + value: row.columns[0].to_string(), + description: row.columns[1].to_string(), + }) + .collect(); + + let name = content + .trim_start_matches(POSSIBLE_VALUES_OF) + .replace('`', "") + .replace(':', ""); + + output.insert(name, TypeDescription { values: enum_types }); + } + } + } + } + + output +} diff --git a/parser/src/types.rs b/parser/src/types.rs new file mode 100644 index 0000000..bf4deca --- /dev/null +++ b/parser/src/types.rs @@ -0,0 +1,119 @@ +use std::collections::HashMap; + +#[derive(Debug, Clone)] +pub struct TypeDescriptions { + pub value: String, + pub description: String, +} + +#[derive(Debug, Clone)] +pub struct TypeDescription { + pub values: Vec, +} + +#[derive(Debug, Clone)] +pub struct TypeInfo { + pub name: String, + pub is_optional: bool, + pub is_list: bool, + pub description: Option, + pub type_description: Option, +} + +impl TypeInfo { + pub fn new( + name: &str, + is_optional: bool, + is_list: bool, + description: Option, + type_description: Option, + ) -> Self { + Self { + name: name.into(), + is_optional, + is_list, + description, + type_description, + } + } +} + +pub const OPTIONAL: &str = "_optional_"; + +#[derive(Debug, Clone)] +pub enum Type { + Number(TypeInfo), + Float(TypeInfo), + Bool(TypeInfo), + String(TypeInfo), + StringArray(TypeInfo), + Object(TypeInfo), +} + +impl Type { + pub fn to_owned_type(&self) -> String { + match self { + Type::Number(_) => "i128".into(), + Type::Float(_) => "f32".into(), + Type::Bool(_) => "bool".into(), + Type::String(_) => "String".into(), + // TODO: fixme + Type::StringArray(_) => "String".into(), + Type::Object(_) => "String".into(), + } + } + + pub fn to_borrowed_type(&self) -> String { + match self { + Type::Number(_) => "i32".into(), + Type::Float(_) => "f32".into(), + Type::Bool(_) => "bool".into(), + Type::String(_) => "str".into(), + Type::StringArray(_) => "&[str]".into(), + Type::Object(_) => "str".into(), + } + } + + pub fn should_borrow(&self) -> bool { + matches!(self, Type::String(_) | Type::Object(_)) + } + + pub fn get_type_info(&self) -> &TypeInfo { + match self { + Type::Number(t) => t, + Type::Float(t) => t, + Type::Bool(t) => t, + Type::String(t) => t, + Type::StringArray(t) => t, + Type::Object(t) => t, + } + } + + pub fn from( + type_as_str: &str, + name: &str, + description: Option, + types: &HashMap, + ) -> Option { + let available_types = types.get(name).cloned(); + let type_name = match name.split_once(OPTIONAL) { + Some((split, _)) => split, + None => name, + } + .trim(); + + let is_optional = name.contains(OPTIONAL); + let type_info = TypeInfo::new(type_name, is_optional, false, description, available_types); + + match type_as_str { + "bool" => Some(Type::Bool(type_info)), + "integer" | "number" | "int" => Some(Type::Number(type_info)), + "string" => Some(Type::String(type_info)), + // This is probably not right but we don't have any information about the actual type. + "array" => Some(Type::StringArray(type_info)), + "object" => Some(Type::Object(type_info)), + "float" => Some(Type::Float(type_info)), + _ => None, + } + } +} diff --git a/parser/src/util.rs b/parser/src/util.rs new file mode 100644 index 0000000..c569b5b --- /dev/null +++ b/parser/src/util.rs @@ -0,0 +1,27 @@ +use md_parser::MdContent; + +pub fn find_content_starts_with(content: &[MdContent], starts_with: &str) -> Option { + content.iter().find_map(|row| match row { + MdContent::Text(content) => { + if content.starts_with(starts_with) { + Some(content.into()) + } else { + None + } + } + _ => None, + }) +} + +pub fn find_content_contains(content: &[MdContent], contains: &str) -> Option { + content.iter().find_map(|row| match row { + MdContent::Text(content) => { + if content.contains(contains) { + Some(content.into()) + } else { + None + } + } + _ => None, + }) +} diff --git a/parser/token_tree.txt b/parser/token_tree.txt new file mode 100644 index 0000000..52bd36c --- /dev/null +++ b/parser/token_tree.txt @@ -0,0 +1,13793 @@ +TokenTree { + title: None, + content: [ + Text( + "This WebUI API documentation applies to qBittorrent v4.1+. For other WebUI API versions, visit [WebUI API](https://github.com/qbittorrent/qBittorrent/wiki#WebUI-API).", + ), + Text( + "", + ), + ], + children: [ + TokenTree { + title: Some( + "Table of Contents", + ), + content: [ + Text( + "", + ), + Text( + "1. [Changes](#changes)", + ), + Text( + " 1. [API v2.0](#api-v20)", + ), + Text( + " 1. [API v2.0.1](#api-v201)", + ), + Text( + " 1. [API v2.0.2](#api-v202)", + ), + Text( + " 1. [API v2.1.0](#api-v210)", + ), + Text( + " 1. [API v2.1.1](#api-v211)", + ), + Text( + " 1. [API v2.2.0](#api-v220)", + ), + Text( + " 1. [API v2.2.1](#api-v221)", + ), + Text( + " 1. [API v2.3.0](#api-v230)", + ), + Text( + " 1. [API v2.4.0](#api-v240)", + ), + Text( + " 1. [API v2.4.1](#api-v241)", + ), + Text( + " 1. [API v2.5.0](#api-v250)", + ), + Text( + " 1. [API v2.5.1](#api-v251)", + ), + Text( + " 1. [API v2.6.0](#api-v260)", + ), + Text( + " 1. [API v2.6.1](#api-v261)", + ), + Text( + " 1. [API v2.6.2](#api-v262)", + ), + Text( + " 1. [API v2.7.0](#api-v270)", + ), + Text( + " 1. [API v2.8.0](#api-v280)", + ), + Text( + " 1. [API v2.8.1](#api-v281)", + ), + Text( + " 1. [API v2.8.2](#api-v282)", + ), + Text( + " 1. [API v2.8.3](#api-v283)", + ), + Text( + "1. [General information](#general-information)", + ), + Text( + "1. [Authentication](#authentication)", + ), + Text( + " 1. [Login](#login)", + ), + Text( + " 1. [Logout](#logout)", + ), + Text( + "1. [Application](#application)", + ), + Text( + " 1. [Get application version](#get-application-version)", + ), + Text( + " 1. [Get API version](#get-api-version)", + ), + Text( + " 1. [Get build info](#get-build-info)", + ), + Text( + " 1. [Shutdown application](#shutdown-application)", + ), + Text( + " 1. [Get application preferences](#get-application-preferences)", + ), + Text( + " 1. [Set application preferences](#set-application-preferences)", + ), + Text( + " 1. [Get default save path](#get-default-save-path)", + ), + Text( + "1. [Log](#log)", + ), + Text( + " 1. [Get log](#get-log)", + ), + Text( + " 1. [Get peer log](#get-peer-log)", + ), + Text( + "1. [Sync](#sync)", + ), + Text( + " 1. [Get main data](#get-main-data)", + ), + Text( + " 1. [Get torrent peers data](#get-torrent-peers-data)", + ), + Text( + "1. [Transfer info](#transfer-info)", + ), + Text( + " 1. [Get global transfer info](#get-global-transfer-info)", + ), + Text( + " 1. [Get alternative speed limits state](#get-alternative-speed-limits-state)", + ), + Text( + " 1. [Toggle alternative speed limits](#toggle-alternative-speed-limits)", + ), + Text( + " 1. [Get global download limit](#get-global-download-limit)", + ), + Text( + " 1. [Set global download limit](#set-global-download-limit)", + ), + Text( + " 1. [Get global upload limit](#get-global-upload-limit)", + ), + Text( + " 1. [Set global upload limit](#set-global-upload-limit)", + ), + Text( + " 1. [Ban peers](#ban-peers)", + ), + Text( + "1. [Torrent management](#torrent-management)", + ), + Text( + " 1. [Get torrent list](#get-torrent-list)", + ), + Text( + " 1. [Get torrent generic properties](#get-torrent-generic-properties)", + ), + Text( + " 1. [Get torrent trackers](#get-torrent-trackers)", + ), + Text( + " 1. [Get torrent web seeds](#get-torrent-web-seeds)", + ), + Text( + " 1. [Get torrent contents](#get-torrent-contents)", + ), + Text( + " 1. [Get torrent pieces' states](#get-torrent-pieces-states)", + ), + Text( + " 1. [Get torrent pieces' hashes](#get-torrent-pieces-hashes)", + ), + Text( + " 1. [Pause torrents](#pause-torrents)", + ), + Text( + " 1. [Resume torrents](#resume-torrents)", + ), + Text( + " 1. [Delete torrents](#delete-torrents)", + ), + Text( + " 1. [Recheck torrents](#recheck-torrents)", + ), + Text( + " 1. [Reannounce torrents](#reannounce-torrents)", + ), + Text( + " 1. [Edit trackers](#edit-trackers)", + ), + Text( + " 1. [Remove trackers](#remove-trackers)", + ), + Text( + " 1. [Add peers](#add-peers)", + ), + Text( + " 1. [Add new torrent](#add-new-torrent)", + ), + Text( + " 1. [Add trackers to torrent](#add-trackers-to-torrent)", + ), + Text( + " 1. [Increase torrent priority](#increase-torrent-priority)", + ), + Text( + " 1. [Decrease torrent priority](#decrease-torrent-priority)", + ), + Text( + " 1. [Maximal torrent priority](#maximal-torrent-priority)", + ), + Text( + " 1. [Minimal torrent priority](#minimal-torrent-priority)", + ), + Text( + " 1. [Set file priority](#set-file-priority)", + ), + Text( + " 1. [Get torrent download limit](#get-torrent-download-limit)", + ), + Text( + " 1. [Set torrent download limit](#set-torrent-download-limit)", + ), + Text( + " 1. [Set torrent share limit](#set-torrent-share-limit)", + ), + Text( + " 1. [Get torrent upload limit](#get-torrent-upload-limit)", + ), + Text( + " 1. [Set torrent upload limit](#set-torrent-upload-limit)", + ), + Text( + " 1. [Set torrent location](#set-torrent-location)", + ), + Text( + " 1. [Set torrent name](#set-torrent-name)", + ), + Text( + " 1. [Set torrent category](#set-torrent-category)", + ), + Text( + " 1. [Get all categories](#get-all-categories)", + ), + Text( + " 1. [Add new category](#add-new-category)", + ), + Text( + " 1. [Edit category](#edit-category)", + ), + Text( + " 1. [Remove categories](#remove-categories)", + ), + Text( + " 1. [Add torrent tags](#add-torrent-tags)", + ), + Text( + " 1. [Remove torrent tags](#remove-torrent-tags)", + ), + Text( + " 1. [Get all tags](#get-all-tags)", + ), + Text( + " 1. [Create tags](#create-tags)", + ), + Text( + " 1. [Delete tags](#delete-tags)", + ), + Text( + " 1. [Set automatic torrent management](#set-automatic-torrent-management)", + ), + Text( + " 1. [Toggle sequential download](#toggle-sequential-download)", + ), + Text( + " 1. [Set first/last piece priority](#set-firstlast-piece-priority)", + ), + Text( + " 1. [Set force start](#set-force-start)", + ), + Text( + " 1. [Set super seeding](#set-super-seeding)", + ), + Text( + " 1. [Rename file](#rename-file)", + ), + Text( + " 1. [Rename folder](#rename-folder)", + ), + Text( + "1. [RSS (experimental)](#rss-experimental)", + ), + Text( + " 1. [Add folder](#add-folder)", + ), + Text( + " 1. [Add feed](#add-feed)", + ), + Text( + " 1. [Remove item](#remove-item)", + ), + Text( + " 1. [Move item](#move-item)", + ), + Text( + " 1. [Get all items](#get-all-items)", + ), + Text( + " 1. [Mark as read](#mark-as-read)", + ), + Text( + " 1. [Refresh item](#refresh-item)", + ), + Text( + " 1. [Set auto-downloading rule](#set-auto-downloading-rule)", + ), + Text( + " 1. [Rename auto-downloading rule](#rename-auto-downloading-rule)", + ), + Text( + " 1. [Remove auto-downloading rule](#remove-auto-downloading-rule)", + ), + Text( + " 1. [Get all auto-downloading rules](#get-all-auto-downloading-rules)", + ), + Text( + " 1. [Get all articles matching a rule](#get-all-articles-matching-a-rule)", + ), + Text( + "1. [Search](#search)", + ), + Text( + " 1. [Start search](#start-search)", + ), + Text( + " 1. [Stop search](#stop-search)", + ), + Text( + " 1. [Get search status](#get-search-status)", + ), + Text( + " 1. [Get search results](#get-search-results)", + ), + Text( + " 1. [Delete search](#delete-search)", + ), + Text( + " 1. [Get search plugins](#get-search-plugins)", + ), + Text( + " 1. [Install search plugin](#install-search-plugin)", + ), + Text( + " 1. [Uninstall search plugin](#uninstall-search-plugin)", + ), + Text( + " 1. [Enable search plugin](#enable-search-plugin)", + ), + Text( + " 1. [Update search plugins](#update-search-plugins)", + ), + Text( + "1. [WebAPI versioning](#webapi-versioning)", + ), + Text( + "", + ), + Asterix( + "", + ), + Text( + "", + ), + ], + children: [], + }, + TokenTree { + title: Some( + "Changes", + ), + content: [ + Text( + "", + ), + ], + children: [ + TokenTree { + title: Some( + "API v2.0", + ), + content: [ + Text( + "", + ), + Text( + "- New version naming scheme: X.Y.Z (where X - major version, Y - minor version, Z - release version)", + ), + Text( + "- New API paths. All API methods are under `api/vX/` (where X is API major version)", + ), + Text( + "- API methods are under new scopes", + ), + Text( + "", + ), + ], + children: [], + }, + TokenTree { + title: Some( + "API v2.0.1", + ), + content: [ + Text( + "", + ), + Text( + "- Add `hashes` field to `/torrents/info` ([#8782](https://github.com/qbittorrent/qBittorrent/pull/8782))", + ), + Text( + "- Add `/torrents/setShareLimits/` method ([#8598](https://github.com/qbittorrent/qBittorrent/pull/8598))", + ), + Text( + "", + ), + ], + children: [], + }, + TokenTree { + title: Some( + "API v2.0.2", + ), + content: [ + Text( + "", + ), + Text( + "- Add `/torrents/reannounce` method ([#9229](https://github.com/qbittorrent/qBittorrent/pull/9229))", + ), + Text( + "", + ), + ], + children: [], + }, + TokenTree { + title: Some( + "API v2.1.0", + ), + content: [ + Text( + "", + ), + Text( + "- Change `/sync/maindata` `categories` field from `array` to `object` ([#9228](https://github.com/qbittorrent/qBittorrent/pull/9228))", + ), + Text( + "- Add `savePath` field to `/torrents/createCategory` ([#9228](https://github.com/qbittorrent/qBittorrent/pull/9228)). This method now requires the category to already exist and will not create new categories.", + ), + Text( + "- Add `/torrents/editCategory` method ([#9228](https://github.com/qbittorrent/qBittorrent/pull/9228))", + ), + Text( + "", + ), + ], + children: [], + }, + TokenTree { + title: Some( + "API v2.1.1", + ), + content: [ + Text( + "", + ), + Text( + "- Add `/torrents/categories` method ([#9586](https://github.com/qbittorrent/qBittorrent/pull/9586))", + ), + Text( + "- Add `/search/` methods ([#8584](https://github.com/qbittorrent/qBittorrent/pull/8584))", + ), + Text( + "- Add `free_space_on_disk` field to `/sync/maindata` ([#8217](https://github.com/qbittorrent/qBittorrent/pull/8217))", + ), + Text( + "", + ), + ], + children: [], + }, + TokenTree { + title: Some( + "API v2.2.0", + ), + content: [ + Text( + "", + ), + Text( + "- Add `/torrents/editTracker` and `/torrents/removeTracker` methods ([#9375](https://github.com/qbittorrent/qBittorrent/pull/9375))", + ), + Text( + "- Add `tier`, `num_seeds`, `num_leeches`, and `num_downloaded` fields to `/torrents/trackers` ([#9375](https://github.com/qbittorrent/qBittorrent/pull/9375))", + ), + Text( + "- Change `status` field from translated string to an integer for `/torrents/trackers` ([#9375](https://github.com/qbittorrent/qBittorrent/pull/9375))", + ), + Text( + "- Change `/torrents/filePrio` `id` field to accept multiple ids ([#9541](https://github.com/qbittorrent/qBittorrent/pull/9541))", + ), + Text( + "- Throw additional errors for failed requests to `/torrents/filePrio` ([#9541](https://github.com/qbittorrent/qBittorrent/pull/9541))", + ), + Text( + "- Add `autoTMM` field to `/torrents/add` ([#9752](https://github.com/qbittorrent/qBittorrent/pull/9752))", + ), + Text( + "- Add various fields to `/app/getPreferences` and `/app/setPreferences` (`create_subfolder_enabled`, `start_paused_enabled`, `auto_delete_mode`, `preallocate_all`, `incomplete_files_ext`, `auto_tmm_enabled`, `torrent_changed_tmm_enabled`, `save_path_changed_tmm_enabled`, `category_changed_tmm_enabled`, `mail_notification_sender`, `limit_lan_peers`, `slow_torrent_dl_rate_threshold`, `slow_torrent_ul_rate_threshold`, `slow_torrent_inactive_timer`, `alternative_webui_enabled`, `alternative_webui_path`) ([#9752](https://github.com/qbittorrent/qBittorrent/pull/9752))", + ), + Text( + "", + ), + ], + children: [], + }, + TokenTree { + title: Some( + "API v2.2.1", + ), + content: [ + Text( + "", + ), + Text( + "- Add `rss/refreshItem` ([#11067](https://github.com/qbittorrent/qBittorrent/pull/11067))", + ), + Text( + "", + ), + ], + children: [], + }, + TokenTree { + title: Some( + "API v2.3.0", + ), + content: [ + Text( + "", + ), + Text( + "- Remove `web_ui_password` field from `/app/preferences`, this field is still writable in `/app/setPreferences` method ([#9942](https://github.com/qbittorrent/qBittorrent/pull/9942))", + ), + Text( + "- Add `/app/buildInfo` method ([#10096](https://github.com/qbittorrent/qBittorrent/pull/10096))", + ), + Text( + "- Always use `/` as path separator in `/torrents/files` response ([#10153](https://github.com/qbittorrent/qBittorrent/pull/10153/))", + ), + Text( + "- Add `/torrents/addPeers` and `/transfer/banPeers` methods ([#10158](https://github.com/qbittorrent/qBittorrent/pull/10158))", + ), + Text( + "- Add `/torrents/addTags`, `/torrents/removeTags`, `/torrents/tags`, `/torrents/createTags`, `/torrents/deleteTags` methods ([#10527](https://github.com/qbittorrent/qBittorrent/pull/10527))", + ), + Text( + "", + ), + ], + children: [], + }, + TokenTree { + title: Some( + "API v2.4.0", + ), + content: [ + Text( + "", + ), + Text( + "- Add `/torrents/renameFile` method ([#11029](https://github.com/qbittorrent/qBittorrent/pull/11029))", + ), + Text( + "", + ), + ], + children: [], + }, + TokenTree { + title: Some( + "API v2.4.1", + ), + content: [ + Text( + "", + ), + Text( + "- Add `stalled`, `stalled_uploading` and `stalled_downloading` as possible values for the `filter` parameter in `/torrents/info` ([#11825](https://github.com/qbittorrent/qBittorrent/pull/11825))", + ), + Text( + "- Add various fields to `/app/preferences` and `/app/setPreferences` (`piece_extent_affinity`, `web_ui_secure_cookie_enabled`, `web_ui_max_auth_fail_count`, `web_ui_ban_duration`, `stop_tracker_timeout`) ([#11781](https://github.com/qbittorrent/qBittorrent/pull/11781), [#11726](https://github.com/qbittorrent/qBittorrent/pull/11726), [#12004](https://github.com/qbittorrent/qBittorrent/pull/12004), [#11834](https://github.com/qbittorrent/qBittorrent/pull/11834))", + ), + Text( + "", + ), + ], + children: [], + }, + TokenTree { + title: Some( + "API v2.5.0", + ), + content: [ + Text( + "- Removes `enable_super_seeding` as fields from `/app/preferences` and `/app/setPreferences` ([#12423](https://github.com/qbittorrent/qBittorrent/pull/12423))", + ), + Text( + "", + ), + ], + children: [], + }, + TokenTree { + title: Some( + "API v2.5.1", + ), + content: [ + Text( + "- Add `web_ui_use_custom_http_headers_enabled`, `web_ui_custom_http_headers`, `rss_download_repack_proper_episodes` and `rss_smart_episode_filters` as fields to `/app/preferences` and `/app/setPreferences` ([#12579](https://github.com/qbittorrent/qBittorrent/pull/12579), [#12549](https://github.com/qbittorrent/qBittorrent/pull/12549))", + ), + Text( + "- Add `/rss/markAsRead` and `/rss/matchingArticles` methods ([#12549](https://github.com/qbittorrent/qBittorrent/pull/12549))", + ), + Text( + "", + ), + ], + children: [], + }, + TokenTree { + title: Some( + "API v2.6.0", + ), + content: [ + Text( + "- Removed `/search/categories` method and modified `/search/plugins` method's response ([#12705](https://github.com/qbittorrent/qBittorrent/pull/12705))", + ), + Text( + "", + ), + ], + children: [], + }, + TokenTree { + title: Some( + "API v2.6.1", + ), + content: [ + Text( + "- Exposed `contentPath` via the `content_path` field in the response to `/torrents/info` ([#13625](https://github.com/qbittorrent/qBittorrent/pull/13625))", + ), + Text( + "", + ), + ], + children: [], + }, + TokenTree { + title: Some( + "API v2.6.2", + ), + content: [ + Text( + "- Added `tags` optional field to `/torrents/add` ([#13882](https://github.com/qbittorrent/qBittorrent/pull/13882))", + ), + Text( + "", + ), + ], + children: [], + }, + TokenTree { + title: Some( + "API v2.8.0", + ), + content: [ + Text( + "- Added `/torrents/renameFolder` method and modified `/torrents/renameFile` method's parameters ([#13995](https://github.com/qbittorrent/qBittorrent/pull/13995))", + ), + Text( + "", + ), + Text( + "Note that this change was released in qBittorrent v4.3.3, but the WebAPI version was incorrectly set to v2.7.0 (see [#14275](https://github.com/qbittorrent/qBittorrent/pull/14275#issuecomment-766310862) for details)", + ), + Text( + "", + ), + ], + children: [], + }, + TokenTree { + title: Some( + "API v2.8.1", + ), + content: [ + Text( + "- Added `ratioLimit` and `seedingTimeLimit` optional fields to `/torrents/add` ([#14519](https://github.com/qbittorrent/qBittorrent/pull/14519))", + ), + Text( + "- Added `seeding_time` field to `/torrents/info` ([#14554](https://github.com/qbittorrent/qBittorrent/pull/14554))", + ), + Text( + "", + ), + ], + children: [], + }, + TokenTree { + title: Some( + "API v2.8.2", + ), + content: [ + Text( + "- Added `indexes` optional parameter to `/torrents/files` ([#14795](https://github.com/qbittorrent/qBittorrent/pull/14795))", + ), + Text( + "- Added `index` field to `/torrents/files` response ([#14795](https://github.com/qbittorrent/qBittorrent/pull/14795))", + ), + Text( + "", + ), + ], + children: [], + }, + TokenTree { + title: Some( + "API v2.8.3", + ), + content: [ + Text( + "- Added `tag` optional parameter to `/torrents/info` ([#15152](https://github.com/qbittorrent/qBittorrent/pull/15152))", + ), + Text( + "", + ), + ], + children: [], + }, + ], + }, + TokenTree { + title: Some( + "General Information", + ), + content: [ + Text( + "", + ), + Text( + "- All API methods are under `/api/v2/APIName/methodName`, where `APIName` is a certain subgroup of API methods whose functionality is related.", + ), + Text( + "- Either `GET` or `POST` can be used as the request type for all API methods.", + ), + Text( + "- All API methods require [authentication](#authentication) (except the `/api/v2/auth/login` method itself, obviously).", + ), + Text( + "", + ), + ], + children: [], + }, + TokenTree { + title: Some( + "Authentication", + ), + content: [ + Text( + "", + ), + Text( + "All Authentication API methods are under \"auth\", e.g.: `/api/v2/auth/methodName`.", + ), + Text( + "", + ), + Text( + "qBittorrent uses cookie-based authentication.", + ), + Text( + "", + ), + ], + children: [ + TokenTree { + title: Some( + "Login", + ), + content: [ + Text( + "", + ), + Text( + "Name: `login`", + ), + Text( + "", + ), + Asterix( + "Parameters:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "Parameter | Type | Description", + columns: [ + "Parameter", + "Type", + "Description", + ], + }, + split: "----------------------------------|---------|------------", + rows: [ + TableRow { + raw: "`username` | string | Username used to access the WebUI", + columns: [ + "username", + "string", + "Username used to access the WebUI", + ], + }, + TableRow { + raw: "`password` | string | Password used to access the WebUI", + columns: [ + "password", + "string", + "Password used to access the WebUI", + ], + }, + ], + }, + ), + Asterix( + "Returns:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "HTTP Status Code | Scenario", + columns: [ + "HTTP Status Code", + "Scenario", + ], + }, + split: "----------------------------------|---------------------", + rows: [ + TableRow { + raw: "403 | User's IP is banned for too many failed login attempts", + columns: [ + "403", + "User's IP is banned for too many failed login attempts", + ], + }, + TableRow { + raw: "200 | All other scenarios", + columns: [ + "200", + "All other scenarios", + ], + }, + ], + }, + ), + Text( + "Upon success, the response will contain a cookie with your SID. You must supply the cookie whenever you want to perform an operation that requires authentication.", + ), + Text( + "", + ), + Text( + "Example showing how to login and execute a command that requires authentication using `curl`:", + ), + Text( + "", + ), + Text( + "```sh", + ), + Text( + "$ curl -i --header 'Referer: http://localhost:8080' --data 'username=admin&password=adminadmin' http://localhost:8080/api/v2/auth/login", + ), + Text( + "HTTP/1.1 200 OK", + ), + Text( + "Content-Encoding:", + ), + Text( + "Content-Length: 3", + ), + Text( + "Content-Type: text/plain; charset=UTF-8", + ), + Text( + "Set-Cookie: SID=hBc7TxF76ERhvIw0jQQ4LZ7Z1jQUV0tQ; path=/", + ), + Text( + "$ curl http://localhost:8080/api/v2/torrents/info --cookie \"SID=hBc7TxF76ERhvIw0jQQ4LZ7Z1jQUV0tQ\"", + ), + Text( + "```", + ), + Text( + "", + ), + Text( + "Note: Set `Referer` or `Origin` header to the exact same domain and port as used in the HTTP query `Host` header.", + ), + Text( + "", + ), + ], + children: [], + }, + TokenTree { + title: Some( + "Logout", + ), + content: [ + Text( + "", + ), + Text( + "Name: `logout`", + ), + Text( + "", + ), + Asterix( + "Parameters:", + ), + Text( + "", + ), + Text( + "None", + ), + Text( + "", + ), + Asterix( + "Returns:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "HTTP Status Code | Scenario", + columns: [ + "HTTP Status Code", + "Scenario", + ], + }, + split: "----------------------------------|---------------------", + rows: [ + TableRow { + raw: "200 | All scenarios", + columns: [ + "200", + "All scenarios", + ], + }, + ], + }, + ), + ], + children: [], + }, + ], + }, + TokenTree { + title: Some( + "Application", + ), + content: [ + Text( + "", + ), + Text( + "All Application API methods are under \"app\", e.g.: `/api/v2/app/methodName`.", + ), + Text( + "", + ), + ], + children: [ + TokenTree { + title: Some( + "Get application version", + ), + content: [ + Text( + "", + ), + Text( + "Name: `version`", + ), + Text( + "", + ), + Asterix( + "Parameters:", + ), + Text( + "", + ), + Text( + "None", + ), + Text( + "", + ), + Asterix( + "Returns:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "HTTP Status Code | Scenario", + columns: [ + "HTTP Status Code", + "Scenario", + ], + }, + split: "----------------------------------|---------------------", + rows: [ + TableRow { + raw: "200 | All scenarios", + columns: [ + "200", + "All scenarios", + ], + }, + ], + }, + ), + Text( + "The response is a string with the application version, e.g. `v4.1.3`", + ), + Text( + "", + ), + ], + children: [], + }, + TokenTree { + title: Some( + "Get API version", + ), + content: [ + Text( + "", + ), + Text( + "Name: `webapiVersion`", + ), + Text( + "", + ), + Asterix( + "Parameters:", + ), + Text( + "", + ), + Text( + "None", + ), + Text( + "", + ), + Asterix( + "Returns:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "HTTP Status Code | Scenario", + columns: [ + "HTTP Status Code", + "Scenario", + ], + }, + split: "----------------------------------|---------------------", + rows: [ + TableRow { + raw: "200 | All scenarios", + columns: [ + "200", + "All scenarios", + ], + }, + ], + }, + ), + Text( + "The response is a string with the WebAPI version, e.g. `2.0`", + ), + Text( + "", + ), + ], + children: [], + }, + TokenTree { + title: Some( + "Get build info", + ), + content: [ + Text( + "", + ), + Text( + "Name: `buildInfo`", + ), + Text( + "", + ), + Asterix( + "Parameters:", + ), + Text( + "", + ), + Text( + "None", + ), + Text( + "", + ), + Asterix( + "Returns:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "HTTP Status Code | Scenario", + columns: [ + "HTTP Status Code", + "Scenario", + ], + }, + split: "----------------------------------|---------------------", + rows: [ + TableRow { + raw: "200 | All scenarios- see JSON below", + columns: [ + "200", + "All scenarios- see JSON below", + ], + }, + ], + }, + ), + Text( + "The response is a JSON object containing the following fields", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "Property | Type | Description", + columns: [ + "Property", + "Type", + "Description", + ], + }, + split: "-------------|---------|------------", + rows: [ + TableRow { + raw: "`qt` | string | QT version", + columns: [ + "qt", + "string", + "QT version", + ], + }, + TableRow { + raw: "`libtorrent` | string | libtorrent version", + columns: [ + "libtorrent", + "string", + "libtorrent version", + ], + }, + TableRow { + raw: "`boost` | string | Boost version", + columns: [ + "boost", + "string", + "Boost version", + ], + }, + TableRow { + raw: "`openssl` | string | OpenSSL version", + columns: [ + "openssl", + "string", + "OpenSSL version", + ], + }, + TableRow { + raw: "`bitness` | int | Application bitness (e.g. 64-bit)", + columns: [ + "bitness", + "int", + "Application bitness (e.g. 64-bit)", + ], + }, + ], + }, + ), + ], + children: [], + }, + TokenTree { + title: Some( + "Shutdown application", + ), + content: [ + Text( + "", + ), + Text( + "Name: `shutdown`", + ), + Text( + "", + ), + Asterix( + "Parameters:", + ), + Text( + "", + ), + Text( + "None", + ), + Text( + "", + ), + Asterix( + "Returns:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "HTTP Status Code | Scenario", + columns: [ + "HTTP Status Code", + "Scenario", + ], + }, + split: "----------------------------------|---------------------", + rows: [ + TableRow { + raw: "200 | All scenarios", + columns: [ + "200", + "All scenarios", + ], + }, + ], + }, + ), + ], + children: [], + }, + TokenTree { + title: Some( + "Get application preferences", + ), + content: [ + Text( + "", + ), + Text( + "Name: `preferences`", + ), + Text( + "", + ), + Asterix( + "Parameters:", + ), + Text( + "", + ), + Text( + "None", + ), + Text( + "", + ), + Asterix( + "Returns:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "HTTP Status Code | Scenario", + columns: [ + "HTTP Status Code", + "Scenario", + ], + }, + split: "----------------------------------|---------------------", + rows: [ + TableRow { + raw: "200 | All scenarios- see JSON below", + columns: [ + "200", + "All scenarios- see JSON below", + ], + }, + ], + }, + ), + Text( + "The response is a JSON object with several fields (key-value) pairs representing the application's settings. The contents may vary depending on which settings are present in qBittorrent.ini.", + ), + Text( + "", + ), + Text( + "Possible fields:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "Property | Type | Description", + columns: [ + "Property", + "Type", + "Description", + ], + }, + split: "-----------------------------------------|---------|------------", + rows: [ + TableRow { + raw: "`locale` | string | Currently selected language (e.g. en_GB for English)", + columns: [ + "locale", + "string", + "Currently selected language (e.g. en_GB for English)", + ], + }, + TableRow { + raw: "`create_subfolder_enabled` | bool | True if a subfolder should be created when adding a torrent", + columns: [ + "create_subfolder_enabled", + "bool", + "True if a subfolder should be created when adding a torrent", + ], + }, + TableRow { + raw: "`start_paused_enabled` | bool | True if torrents should be added in a Paused state", + columns: [ + "start_paused_enabled", + "bool", + "True if torrents should be added in a Paused state", + ], + }, + TableRow { + raw: "`auto_delete_mode` | integer | TODO", + columns: [ + "auto_delete_mode", + "integer", + "TODO", + ], + }, + TableRow { + raw: "`preallocate_all` | bool | True if disk space should be pre-allocated for all files", + columns: [ + "preallocate_all", + "bool", + "True if disk space should be pre-allocated for all files", + ], + }, + TableRow { + raw: "`incomplete_files_ext` | bool | True if \".!qB\" should be appended to incomplete files", + columns: [ + "incomplete_files_ext", + "bool", + "True if \".!qB\" should be appended to incomplete files", + ], + }, + TableRow { + raw: "`auto_tmm_enabled` | bool | True if Automatic Torrent Management is enabled by default", + columns: [ + "auto_tmm_enabled", + "bool", + "True if Automatic Torrent Management is enabled by default", + ], + }, + TableRow { + raw: "`torrent_changed_tmm_enabled` | bool | True if torrent should be relocated when its Category changes", + columns: [ + "torrent_changed_tmm_enabled", + "bool", + "True if torrent should be relocated when its Category changes", + ], + }, + TableRow { + raw: "`save_path_changed_tmm_enabled` | bool | True if torrent should be relocated when the default save path changes", + columns: [ + "save_path_changed_tmm_enabled", + "bool", + "True if torrent should be relocated when the default save path changes", + ], + }, + TableRow { + raw: "`category_changed_tmm_enabled` | bool | True if torrent should be relocated when its Category's save path changes", + columns: [ + "category_changed_tmm_enabled", + "bool", + "True if torrent should be relocated when its Category's save path changes", + ], + }, + TableRow { + raw: "`save_path` | string | Default save path for torrents, separated by slashes", + columns: [ + "save_path", + "string", + "Default save path for torrents, separated by slashes", + ], + }, + TableRow { + raw: "`temp_path_enabled` | bool | True if folder for incomplete torrents is enabled", + columns: [ + "temp_path_enabled", + "bool", + "True if folder for incomplete torrents is enabled", + ], + }, + TableRow { + raw: "`temp_path` | string | Path for incomplete torrents, separated by slashes", + columns: [ + "temp_path", + "string", + "Path for incomplete torrents, separated by slashes", + ], + }, + TableRow { + raw: "`scan_dirs` | object | Property: directory to watch for torrent files, value: where torrents loaded from this directory should be downloaded to (see list of possible values below). Slashes are used as path separators; multiple key/value pairs can be specified", + columns: [ + "scan_dirs", + "object", + "Property: directory to watch for torrent files, value: where torrents loaded from this directory should be downloaded to (see list of possible values below). Slashes are used as path separators; multiple key/value pairs can be specified", + ], + }, + TableRow { + raw: "`export_dir` | string | Path to directory to copy .torrent files to. Slashes are used as path separators", + columns: [ + "export_dir", + "string", + "Path to directory to copy .torrent files to. Slashes are used as path separators", + ], + }, + TableRow { + raw: "`export_dir_fin` | string | Path to directory to copy .torrent files of completed downloads to. Slashes are used as path separators", + columns: [ + "export_dir_fin", + "string", + "Path to directory to copy .torrent files of completed downloads to. Slashes are used as path separators", + ], + }, + TableRow { + raw: "`mail_notification_enabled` | bool | True if e-mail notification should be enabled", + columns: [ + "mail_notification_enabled", + "bool", + "True if e-mail notification should be enabled", + ], + }, + TableRow { + raw: "`mail_notification_sender` | string | e-mail where notifications should originate from", + columns: [ + "mail_notification_sender", + "string", + "e-mail where notifications should originate from", + ], + }, + TableRow { + raw: "`mail_notification_email` | string | e-mail to send notifications to", + columns: [ + "mail_notification_email", + "string", + "e-mail to send notifications to", + ], + }, + TableRow { + raw: "`mail_notification_smtp` | string | smtp server for e-mail notifications", + columns: [ + "mail_notification_smtp", + "string", + "smtp server for e-mail notifications", + ], + }, + TableRow { + raw: "`mail_notification_ssl_enabled` | bool | True if smtp server requires SSL connection", + columns: [ + "mail_notification_ssl_enabled", + "bool", + "True if smtp server requires SSL connection", + ], + }, + TableRow { + raw: "`mail_notification_auth_enabled` | bool | True if smtp server requires authentication", + columns: [ + "mail_notification_auth_enabled", + "bool", + "True if smtp server requires authentication", + ], + }, + TableRow { + raw: "`mail_notification_username` | string | Username for smtp authentication", + columns: [ + "mail_notification_username", + "string", + "Username for smtp authentication", + ], + }, + TableRow { + raw: "`mail_notification_password` | string | Password for smtp authentication", + columns: [ + "mail_notification_password", + "string", + "Password for smtp authentication", + ], + }, + TableRow { + raw: "`autorun_enabled` | bool | True if external program should be run after torrent has finished downloading", + columns: [ + "autorun_enabled", + "bool", + "True if external program should be run after torrent has finished downloading", + ], + }, + TableRow { + raw: "`autorun_program` | string | Program path/name/arguments to run if `autorun_enabled` is enabled; path is separated by slashes; you can use `%f` and `%n` arguments, which will be expanded by qBittorent as path_to_torrent_file and torrent_name (from the GUI; not the .torrent file name) respectively", + columns: [ + "autorun_program", + "string", + "Program path/name/arguments to run if autorun_enabled is enabled; path is separated by slashes; you can use %f and %n arguments, which will be expanded by qBittorent as path_to_torrent_file and torrent_name (from the GUI; not the .torrent file name) respectively", + ], + }, + TableRow { + raw: "`queueing_enabled` | bool | True if torrent queuing is enabled", + columns: [ + "queueing_enabled", + "bool", + "True if torrent queuing is enabled", + ], + }, + TableRow { + raw: "`max_active_downloads` | integer | Maximum number of active simultaneous downloads", + columns: [ + "max_active_downloads", + "integer", + "Maximum number of active simultaneous downloads", + ], + }, + TableRow { + raw: "`max_active_torrents` | integer | Maximum number of active simultaneous downloads and uploads", + columns: [ + "max_active_torrents", + "integer", + "Maximum number of active simultaneous downloads and uploads", + ], + }, + TableRow { + raw: "`max_active_uploads` | integer | Maximum number of active simultaneous uploads", + columns: [ + "max_active_uploads", + "integer", + "Maximum number of active simultaneous uploads", + ], + }, + TableRow { + raw: "`dont_count_slow_torrents` | bool | If true torrents w/o any activity (stalled ones) will not be counted towards `max_active_*` limits; see [dont_count_slow_torrents](https://www.libtorrent.org/reference-Settings.html#dont_count_slow_torrents) for more information", + columns: [ + "dont_count_slow_torrents", + "bool", + "If true torrents w/o any activity (stalled ones) will not be counted towards max_active_* limits; see [dont_count_slow_torrents](https://www.libtorrent.org/reference-Settings.html#dont_count_slow_torrents) for more information", + ], + }, + TableRow { + raw: "`slow_torrent_dl_rate_threshold` | integer | Download rate in KiB/s for a torrent to be considered \"slow\"", + columns: [ + "slow_torrent_dl_rate_threshold", + "integer", + "Download rate in KiB/s for a torrent to be considered \"slow\"", + ], + }, + TableRow { + raw: "`slow_torrent_ul_rate_threshold` | integer | Upload rate in KiB/s for a torrent to be considered \"slow\"", + columns: [ + "slow_torrent_ul_rate_threshold", + "integer", + "Upload rate in KiB/s for a torrent to be considered \"slow\"", + ], + }, + TableRow { + raw: "`slow_torrent_inactive_timer` | integer | Seconds a torrent should be inactive before considered \"slow\"", + columns: [ + "slow_torrent_inactive_timer", + "integer", + "Seconds a torrent should be inactive before considered \"slow\"", + ], + }, + TableRow { + raw: "`max_ratio_enabled` | bool | True if share ratio limit is enabled", + columns: [ + "max_ratio_enabled", + "bool", + "True if share ratio limit is enabled", + ], + }, + TableRow { + raw: "`max_ratio` | float | Get the global share ratio limit", + columns: [ + "max_ratio", + "float", + "Get the global share ratio limit", + ], + }, + TableRow { + raw: "`max_ratio_act` | integer | Action performed when a torrent reaches the maximum share ratio. See list of possible values here below.", + columns: [ + "max_ratio_act", + "integer", + "Action performed when a torrent reaches the maximum share ratio. See list of possible values here below.", + ], + }, + TableRow { + raw: "`listen_port` | integer | Port for incoming connections", + columns: [ + "listen_port", + "integer", + "Port for incoming connections", + ], + }, + TableRow { + raw: "`upnp` | bool | True if UPnP/NAT-PMP is enabled", + columns: [ + "upnp", + "bool", + "True if UPnP/NAT-PMP is enabled", + ], + }, + TableRow { + raw: "`random_port` | bool | True if the port is randomly selected", + columns: [ + "random_port", + "bool", + "True if the port is randomly selected", + ], + }, + TableRow { + raw: "`dl_limit` | integer | Global download speed limit in KiB/s; `-1` means no limit is applied", + columns: [ + "dl_limit", + "integer", + "Global download speed limit in KiB/s; -1 means no limit is applied", + ], + }, + TableRow { + raw: "`up_limit` | integer | Global upload speed limit in KiB/s; `-1` means no limit is applied", + columns: [ + "up_limit", + "integer", + "Global upload speed limit in KiB/s; -1 means no limit is applied", + ], + }, + TableRow { + raw: "`max_connec` | integer | Maximum global number of simultaneous connections", + columns: [ + "max_connec", + "integer", + "Maximum global number of simultaneous connections", + ], + }, + TableRow { + raw: "`max_connec_per_torrent` | integer | Maximum number of simultaneous connections per torrent", + columns: [ + "max_connec_per_torrent", + "integer", + "Maximum number of simultaneous connections per torrent", + ], + }, + TableRow { + raw: "`max_uploads` | integer | Maximum number of upload slots", + columns: [ + "max_uploads", + "integer", + "Maximum number of upload slots", + ], + }, + TableRow { + raw: "`max_uploads_per_torrent` | integer | Maximum number of upload slots per torrent", + columns: [ + "max_uploads_per_torrent", + "integer", + "Maximum number of upload slots per torrent", + ], + }, + TableRow { + raw: "`stop_tracker_timeout` | integer | Timeout in seconds for a `stopped` announce request to trackers", + columns: [ + "stop_tracker_timeout", + "integer", + "Timeout in seconds for a stopped announce request to trackers", + ], + }, + TableRow { + raw: "`enable_piece_extent_affinity` | bool | True if the advanced libtorrent option `piece_extent_affinity` is enabled", + columns: [ + "enable_piece_extent_affinity", + "bool", + "True if the advanced libtorrent option piece_extent_affinity is enabled", + ], + }, + TableRow { + raw: "`bittorrent_protocol` | integer | Bittorrent Protocol to use (see list of possible values below)", + columns: [ + "bittorrent_protocol", + "integer", + "Bittorrent Protocol to use (see list of possible values below)", + ], + }, + TableRow { + raw: "`limit_utp_rate` | bool | True if `[du]l_limit` should be applied to uTP connections; this option is only available in qBittorent built against libtorrent version 0.16.X and higher", + columns: [ + "limit_utp_rate", + "bool", + "True if [du]l_limit should be applied to uTP connections; this option is only available in qBittorent built against libtorrent version 0.16.X and higher", + ], + }, + TableRow { + raw: "`limit_tcp_overhead` | bool | True if `[du]l_limit` should be applied to estimated TCP overhead (service data: e.g. packet headers)", + columns: [ + "limit_tcp_overhead", + "bool", + "True if [du]l_limit should be applied to estimated TCP overhead (service data: e.g. packet headers)", + ], + }, + TableRow { + raw: "`limit_lan_peers` | bool | True if `[du]l_limit` should be applied to peers on the LAN", + columns: [ + "limit_lan_peers", + "bool", + "True if [du]l_limit should be applied to peers on the LAN", + ], + }, + TableRow { + raw: "`alt_dl_limit` | integer | Alternative global download speed limit in KiB/s", + columns: [ + "alt_dl_limit", + "integer", + "Alternative global download speed limit in KiB/s", + ], + }, + TableRow { + raw: "`alt_up_limit` | integer | Alternative global upload speed limit in KiB/s", + columns: [ + "alt_up_limit", + "integer", + "Alternative global upload speed limit in KiB/s", + ], + }, + TableRow { + raw: "`scheduler_enabled` | bool | True if alternative limits should be applied according to schedule", + columns: [ + "scheduler_enabled", + "bool", + "True if alternative limits should be applied according to schedule", + ], + }, + TableRow { + raw: "`schedule_from_hour` | integer | Scheduler starting hour", + columns: [ + "schedule_from_hour", + "integer", + "Scheduler starting hour", + ], + }, + TableRow { + raw: "`schedule_from_min` | integer | Scheduler starting minute", + columns: [ + "schedule_from_min", + "integer", + "Scheduler starting minute", + ], + }, + TableRow { + raw: "`schedule_to_hour` | integer | Scheduler ending hour", + columns: [ + "schedule_to_hour", + "integer", + "Scheduler ending hour", + ], + }, + TableRow { + raw: "`schedule_to_min` | integer | Scheduler ending minute", + columns: [ + "schedule_to_min", + "integer", + "Scheduler ending minute", + ], + }, + TableRow { + raw: "`scheduler_days` | integer | Scheduler days. See possible values here below", + columns: [ + "scheduler_days", + "integer", + "Scheduler days. See possible values here below", + ], + }, + TableRow { + raw: "`dht` | bool | True if DHT is enabled", + columns: [ + "dht", + "bool", + "True if DHT is enabled", + ], + }, + TableRow { + raw: "`pex` | bool | True if PeX is enabled", + columns: [ + "pex", + "bool", + "True if PeX is enabled", + ], + }, + TableRow { + raw: "`lsd` | bool | True if LSD is enabled", + columns: [ + "lsd", + "bool", + "True if LSD is enabled", + ], + }, + TableRow { + raw: "`encryption` | integer | See list of possible values here below", + columns: [ + "encryption", + "integer", + "See list of possible values here below", + ], + }, + TableRow { + raw: "`anonymous_mode` | bool | If true anonymous mode will be enabled; read more [here](Anonymous-Mode); this option is only available in qBittorent built against libtorrent version 0.16.X and higher", + columns: [ + "anonymous_mode", + "bool", + "If true anonymous mode will be enabled; read more [here](Anonymous-Mode); this option is only available in qBittorent built against libtorrent version 0.16.X and higher", + ], + }, + TableRow { + raw: "`proxy_type` | integer | See list of possible values here below", + columns: [ + "proxy_type", + "integer", + "See list of possible values here below", + ], + }, + TableRow { + raw: "`proxy_ip` | string | Proxy IP address or domain name", + columns: [ + "proxy_ip", + "string", + "Proxy IP address or domain name", + ], + }, + TableRow { + raw: "`proxy_port` | integer | Proxy port", + columns: [ + "proxy_port", + "integer", + "Proxy port", + ], + }, + TableRow { + raw: "`proxy_peer_connections` | bool | True if peer and web seed connections should be proxified; this option will have any effect only in qBittorent built against libtorrent version 0.16.X and higher", + columns: [ + "proxy_peer_connections", + "bool", + "True if peer and web seed connections should be proxified; this option will have any effect only in qBittorent built against libtorrent version 0.16.X and higher", + ], + }, + TableRow { + raw: "`proxy_auth_enabled` | bool | True proxy requires authentication; doesn't apply to SOCKS4 proxies", + columns: [ + "proxy_auth_enabled", + "bool", + "True proxy requires authentication; doesn't apply to SOCKS4 proxies", + ], + }, + TableRow { + raw: "`proxy_username` | string | Username for proxy authentication", + columns: [ + "proxy_username", + "string", + "Username for proxy authentication", + ], + }, + TableRow { + raw: "`proxy_password` | string | Password for proxy authentication", + columns: [ + "proxy_password", + "string", + "Password for proxy authentication", + ], + }, + TableRow { + raw: "`proxy_torrents_only` | bool | True if proxy is only used for torrents", + columns: [ + "proxy_torrents_only", + "bool", + "True if proxy is only used for torrents", + ], + }, + TableRow { + raw: "`ip_filter_enabled` | bool | True if external IP filter should be enabled", + columns: [ + "ip_filter_enabled", + "bool", + "True if external IP filter should be enabled", + ], + }, + TableRow { + raw: "`ip_filter_path` | string | Path to IP filter file (.dat, .p2p, .p2b files are supported); path is separated by slashes", + columns: [ + "ip_filter_path", + "string", + "Path to IP filter file (.dat, .p2p, .p2b files are supported); path is separated by slashes", + ], + }, + TableRow { + raw: "`ip_filter_trackers` | bool | True if IP filters are applied to trackers", + columns: [ + "ip_filter_trackers", + "bool", + "True if IP filters are applied to trackers", + ], + }, + TableRow { + raw: "`web_ui_domain_list` | string | Comma-separated list of domains to accept when performing Host header validation", + columns: [ + "web_ui_domain_list", + "string", + "Comma-separated list of domains to accept when performing Host header validation", + ], + }, + TableRow { + raw: "`web_ui_address` | string | IP address to use for the WebUI", + columns: [ + "web_ui_address", + "string", + "IP address to use for the WebUI", + ], + }, + TableRow { + raw: "`web_ui_port` | integer | WebUI port", + columns: [ + "web_ui_port", + "integer", + "WebUI port", + ], + }, + TableRow { + raw: "`web_ui_upnp` | bool | True if UPnP is used for the WebUI port", + columns: [ + "web_ui_upnp", + "bool", + "True if UPnP is used for the WebUI port", + ], + }, + TableRow { + raw: "`web_ui_username` | string | WebUI username", + columns: [ + "web_ui_username", + "string", + "WebUI username", + ], + }, + TableRow { + raw: "`web_ui_password` | string | For API ≥ v2.3.0: Plaintext WebUI password, not readable, write-only. For API < v2.3.0: MD5 hash of WebUI password, hash is generated from the following string: `username:Web UI Access:plain_text_web_ui_password`", + columns: [ + "web_ui_password", + "string", + "For API ≥ v2.3.0: Plaintext WebUI password, not readable, write-only. For API < v2.3.0: MD5 hash of WebUI password, hash is generated from the following string: username:Web UI Access:plain_text_web_ui_password", + ], + }, + TableRow { + raw: "`web_ui_csrf_protection_enabled` | bool | True if WebUI CSRF protection is enabled", + columns: [ + "web_ui_csrf_protection_enabled", + "bool", + "True if WebUI CSRF protection is enabled", + ], + }, + TableRow { + raw: "`web_ui_clickjacking_protection_enabled` | bool | True if WebUI clickjacking protection is enabled", + columns: [ + "web_ui_clickjacking_protection_enabled", + "bool", + "True if WebUI clickjacking protection is enabled", + ], + }, + TableRow { + raw: "`web_ui_secure_cookie_enabled` | bool | True if WebUI cookie `Secure` flag is enabled", + columns: [ + "web_ui_secure_cookie_enabled", + "bool", + "True if WebUI cookie Secure flag is enabled", + ], + }, + TableRow { + raw: "`web_ui_max_auth_fail_count` | integer | Maximum number of authentication failures before WebUI access ban", + columns: [ + "web_ui_max_auth_fail_count", + "integer", + "Maximum number of authentication failures before WebUI access ban", + ], + }, + TableRow { + raw: "`web_ui_ban_duration` | integer | WebUI access ban duration in seconds", + columns: [ + "web_ui_ban_duration", + "integer", + "WebUI access ban duration in seconds", + ], + }, + TableRow { + raw: "`web_ui_session_timeout` | integer | Seconds until WebUI is automatically signed off", + columns: [ + "web_ui_session_timeout", + "integer", + "Seconds until WebUI is automatically signed off", + ], + }, + TableRow { + raw: "`web_ui_host_header_validation_enabled` | bool | True if WebUI host header validation is enabled", + columns: [ + "web_ui_host_header_validation_enabled", + "bool", + "True if WebUI host header validation is enabled", + ], + }, + TableRow { + raw: "`bypass_local_auth` | bool | True if authentication challenge for loopback address (127.0.0.1) should be disabled", + columns: [ + "bypass_local_auth", + "bool", + "True if authentication challenge for loopback address (127.0.0.1) should be disabled", + ], + }, + TableRow { + raw: "`bypass_auth_subnet_whitelist_enabled` | bool | True if webui authentication should be bypassed for clients whose ip resides within (at least) one of the subnets on the whitelist", + columns: [ + "bypass_auth_subnet_whitelist_enabled", + "bool", + "True if webui authentication should be bypassed for clients whose ip resides within (at least) one of the subnets on the whitelist", + ], + }, + TableRow { + raw: "`bypass_auth_subnet_whitelist` | string | (White)list of ipv4/ipv6 subnets for which webui authentication should be bypassed; list entries are separated by commas", + columns: [ + "bypass_auth_subnet_whitelist", + "string", + "(White)list of ipv4/ipv6 subnets for which webui authentication should be bypassed; list entries are separated by commas", + ], + }, + TableRow { + raw: "`alternative_webui_enabled` | bool | True if an alternative WebUI should be used", + columns: [ + "alternative_webui_enabled", + "bool", + "True if an alternative WebUI should be used", + ], + }, + TableRow { + raw: "`alternative_webui_path` | string | File path to the alternative WebUI", + columns: [ + "alternative_webui_path", + "string", + "File path to the alternative WebUI", + ], + }, + TableRow { + raw: "`use_https` | bool | True if WebUI HTTPS access is enabled", + columns: [ + "use_https", + "bool", + "True if WebUI HTTPS access is enabled", + ], + }, + TableRow { + raw: "`ssl_key` | string | For API < v2.0.1: SSL keyfile contents (this is a not a path)", + columns: [ + "ssl_key", + "string", + "For API < v2.0.1: SSL keyfile contents (this is a not a path)", + ], + }, + TableRow { + raw: "`ssl_cert` | string | For API < v2.0.1: SSL certificate contents (this is a not a path)", + columns: [ + "ssl_cert", + "string", + "For API < v2.0.1: SSL certificate contents (this is a not a path)", + ], + }, + TableRow { + raw: "`web_ui_https_key_path` | string | For API ≥ v2.0.1: Path to SSL keyfile", + columns: [ + "web_ui_https_key_path", + "string", + "For API ≥ v2.0.1: Path to SSL keyfile", + ], + }, + TableRow { + raw: "`web_ui_https_cert_path` | string | For API ≥ v2.0.1: Path to SSL certificate", + columns: [ + "web_ui_https_cert_path", + "string", + "For API ≥ v2.0.1: Path to SSL certificate", + ], + }, + TableRow { + raw: "`dyndns_enabled` | bool | True if server DNS should be updated dynamically", + columns: [ + "dyndns_enabled", + "bool", + "True if server DNS should be updated dynamically", + ], + }, + TableRow { + raw: "`dyndns_service` | integer | See list of possible values here below", + columns: [ + "dyndns_service", + "integer", + "See list of possible values here below", + ], + }, + TableRow { + raw: "`dyndns_username` | string | Username for DDNS service", + columns: [ + "dyndns_username", + "string", + "Username for DDNS service", + ], + }, + TableRow { + raw: "`dyndns_password` | string | Password for DDNS service", + columns: [ + "dyndns_password", + "string", + "Password for DDNS service", + ], + }, + TableRow { + raw: "`dyndns_domain` | string | Your DDNS domain name", + columns: [ + "dyndns_domain", + "string", + "Your DDNS domain name", + ], + }, + TableRow { + raw: "`rss_refresh_interval` | integer | RSS refresh interval", + columns: [ + "rss_refresh_interval", + "integer", + "RSS refresh interval", + ], + }, + TableRow { + raw: "`rss_max_articles_per_feed` | integer | Max stored articles per RSS feed", + columns: [ + "rss_max_articles_per_feed", + "integer", + "Max stored articles per RSS feed", + ], + }, + TableRow { + raw: "`rss_processing_enabled` | bool | Enable processing of RSS feeds", + columns: [ + "rss_processing_enabled", + "bool", + "Enable processing of RSS feeds", + ], + }, + TableRow { + raw: "`rss_auto_downloading_enabled` | bool | Enable auto-downloading of torrents from the RSS feeds", + columns: [ + "rss_auto_downloading_enabled", + "bool", + "Enable auto-downloading of torrents from the RSS feeds", + ], + }, + TableRow { + raw: "`rss_download_repack_proper_episodes` | bool | For API ≥ v2.5.1: Enable downloading of repack/proper Episodes", + columns: [ + "rss_download_repack_proper_episodes", + "bool", + "For API ≥ v2.5.1: Enable downloading of repack/proper Episodes", + ], + }, + TableRow { + raw: "`rss_smart_episode_filters` | string | For API ≥ v2.5.1: List of RSS Smart Episode Filters", + columns: [ + "rss_smart_episode_filters", + "string", + "For API ≥ v2.5.1: List of RSS Smart Episode Filters", + ], + }, + TableRow { + raw: "`add_trackers_enabled` | bool | Enable automatic adding of trackers to new torrents", + columns: [ + "add_trackers_enabled", + "bool", + "Enable automatic adding of trackers to new torrents", + ], + }, + TableRow { + raw: "`add_trackers` | string | List of trackers to add to new torrent", + columns: [ + "add_trackers", + "string", + "List of trackers to add to new torrent", + ], + }, + TableRow { + raw: "`web_ui_use_custom_http_headers_enabled` | bool | For API ≥ v2.5.1: Enable custom http headers", + columns: [ + "web_ui_use_custom_http_headers_enabled", + "bool", + "For API ≥ v2.5.1: Enable custom http headers", + ], + }, + TableRow { + raw: "`web_ui_custom_http_headers` | string | For API ≥ v2.5.1: List of custom http headers", + columns: [ + "web_ui_custom_http_headers", + "string", + "For API ≥ v2.5.1: List of custom http headers", + ], + }, + TableRow { + raw: "`max_seeding_time_enabled` | bool | True enables max seeding time ", + columns: [ + "max_seeding_time_enabled", + "bool", + "True enables max seeding time", + ], + }, + TableRow { + raw: "`max_seeding_time` | integer | Number of minutes to seed a torrent", + columns: [ + "max_seeding_time", + "integer", + "Number of minutes to seed a torrent", + ], + }, + TableRow { + raw: "`announce_ip` | string | TODO", + columns: [ + "announce_ip", + "string", + "TODO", + ], + }, + TableRow { + raw: "`announce_to_all_tiers` | bool | True always announce to all tiers", + columns: [ + "announce_to_all_tiers", + "bool", + "True always announce to all tiers", + ], + }, + TableRow { + raw: "`announce_to_all_trackers` | bool | True always announce to all trackers in a tier", + columns: [ + "announce_to_all_trackers", + "bool", + "True always announce to all trackers in a tier", + ], + }, + TableRow { + raw: "`async_io_threads` | integer | Number of asynchronous I/O threads", + columns: [ + "async_io_threads", + "integer", + "Number of asynchronous I/O threads", + ], + }, + TableRow { + raw: "`banned_IPs` | string | List of banned IPs", + columns: [ + "banned_IPs", + "string", + "List of banned IPs", + ], + }, + TableRow { + raw: "`checking_memory_use` | integer | Outstanding memory when checking torrents in MiB", + columns: [ + "checking_memory_use", + "integer", + "Outstanding memory when checking torrents in MiB", + ], + }, + TableRow { + raw: "`current_interface_address` | string | IP Address to bind to. Empty String means All addresses", + columns: [ + "current_interface_address", + "string", + "IP Address to bind to. Empty String means All addresses", + ], + }, + TableRow { + raw: "`current_network_interface` | string | Network Interface used", + columns: [ + "current_network_interface", + "string", + "Network Interface used", + ], + }, + TableRow { + raw: "`disk_cache` | integer | Disk cache used in MiB", + columns: [ + "disk_cache", + "integer", + "Disk cache used in MiB", + ], + }, + TableRow { + raw: "`disk_cache_ttl` | integer | Disk cache expiry interval in seconds", + columns: [ + "disk_cache_ttl", + "integer", + "Disk cache expiry interval in seconds", + ], + }, + TableRow { + raw: "`embedded_tracker_port` | integer | Port used for embedded tracker", + columns: [ + "embedded_tracker_port", + "integer", + "Port used for embedded tracker", + ], + }, + TableRow { + raw: "`enable_coalesce_read_write` | bool | True enables coalesce reads & writes", + columns: [ + "enable_coalesce_read_write", + "bool", + "True enables coalesce reads & writes", + ], + }, + TableRow { + raw: "`enable_embedded_tracker` | bool | True enables embedded tracker", + columns: [ + "enable_embedded_tracker", + "bool", + "True enables embedded tracker", + ], + }, + TableRow { + raw: "`enable_multi_connections_from_same_ip` | bool | True allows multiple connections from the same IP address", + columns: [ + "enable_multi_connections_from_same_ip", + "bool", + "True allows multiple connections from the same IP address", + ], + }, + TableRow { + raw: "`enable_os_cache` | bool | True enables os cache", + columns: [ + "enable_os_cache", + "bool", + "True enables os cache", + ], + }, + TableRow { + raw: "`enable_upload_suggestions` | bool | True enables sending of upload piece suggestions", + columns: [ + "enable_upload_suggestions", + "bool", + "True enables sending of upload piece suggestions", + ], + }, + TableRow { + raw: "`file_pool_size` | integer | File pool size", + columns: [ + "file_pool_size", + "integer", + "File pool size", + ], + }, + TableRow { + raw: "`outgoing_ports_max` | integer | Maximal outgoing port (0: Disabled)", + columns: [ + "outgoing_ports_max", + "integer", + "Maximal outgoing port (0: Disabled)", + ], + }, + TableRow { + raw: "`outgoing_ports_min` | integer | Minimal outgoing port (0: Disabled)", + columns: [ + "outgoing_ports_min", + "integer", + "Minimal outgoing port (0: Disabled)", + ], + }, + TableRow { + raw: "`recheck_completed_torrents` | bool | True rechecks torrents on completion", + columns: [ + "recheck_completed_torrents", + "bool", + "True rechecks torrents on completion", + ], + }, + TableRow { + raw: "`resolve_peer_countries` | bool | True resolves peer countries", + columns: [ + "resolve_peer_countries", + "bool", + "True resolves peer countries", + ], + }, + TableRow { + raw: "`save_resume_data_interval` | integer | Save resume data interval in min", + columns: [ + "save_resume_data_interval", + "integer", + "Save resume data interval in min", + ], + }, + TableRow { + raw: "`send_buffer_low_watermark` | integer | Send buffer low watermark in KiB", + columns: [ + "send_buffer_low_watermark", + "integer", + "Send buffer low watermark in KiB", + ], + }, + TableRow { + raw: "`send_buffer_watermark` | integer | Send buffer watermark in KiB", + columns: [ + "send_buffer_watermark", + "integer", + "Send buffer watermark in KiB", + ], + }, + TableRow { + raw: "`send_buffer_watermark_factor` | integer | Send buffer watermark factor in percent", + columns: [ + "send_buffer_watermark_factor", + "integer", + "Send buffer watermark factor in percent", + ], + }, + TableRow { + raw: "`socket_backlog_size` | integer | Socket backlog size", + columns: [ + "socket_backlog_size", + "integer", + "Socket backlog size", + ], + }, + TableRow { + raw: "`upload_choking_algorithm` | integer | Upload choking algorithm used (see list of possible values below)", + columns: [ + "upload_choking_algorithm", + "integer", + "Upload choking algorithm used (see list of possible values below)", + ], + }, + TableRow { + raw: "`upload_slots_behavior` | integer | Upload slots behavior used (see list of possible values below)", + columns: [ + "upload_slots_behavior", + "integer", + "Upload slots behavior used (see list of possible values below)", + ], + }, + TableRow { + raw: "`upnp_lease_duration` | integer | UPnP lease duration (0: Permanent lease)", + columns: [ + "upnp_lease_duration", + "integer", + "UPnP lease duration (0: Permanent lease)", + ], + }, + TableRow { + raw: "`utp_tcp_mixed_mode` | integer | μTP-TCP mixed mode algorithm (see list of possible values below)", + columns: [ + "utp_tcp_mixed_mode", + "integer", + "μTP-TCP mixed mode algorithm (see list of possible values below)", + ], + }, + ], + }, + ), + Text( + "Possible values of `scan_dirs`:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "Value | Description", + columns: [ + "Value", + "Description", + ], + }, + split: "----------------------------|------------", + rows: [ + TableRow { + raw: "`0` | Download to the monitored folder", + columns: [ + "0", + "Download to the monitored folder", + ], + }, + TableRow { + raw: "`1` | Download to the default save path", + columns: [ + "1", + "Download to the default save path", + ], + }, + TableRow { + raw: "`\"/path/to/download/to\"` | Download to this path", + columns: [ + "\"/path/to/download/to\"", + "Download to this path", + ], + }, + ], + }, + ), + Text( + "Possible values of `scheduler_days`:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "Value | Description", + columns: [ + "Value", + "Description", + ], + }, + split: "-------|------------", + rows: [ + TableRow { + raw: "`0` | Every day", + columns: [ + "0", + "Every day", + ], + }, + TableRow { + raw: "`1` | Every weekday", + columns: [ + "1", + "Every weekday", + ], + }, + TableRow { + raw: "`2` | Every weekend", + columns: [ + "2", + "Every weekend", + ], + }, + TableRow { + raw: "`3` | Every Monday", + columns: [ + "3", + "Every Monday", + ], + }, + TableRow { + raw: "`4` | Every Tuesday", + columns: [ + "4", + "Every Tuesday", + ], + }, + TableRow { + raw: "`5` | Every Wednesday", + columns: [ + "5", + "Every Wednesday", + ], + }, + TableRow { + raw: "`6` | Every Thursday", + columns: [ + "6", + "Every Thursday", + ], + }, + TableRow { + raw: "`7` | Every Friday", + columns: [ + "7", + "Every Friday", + ], + }, + TableRow { + raw: "`8` | Every Saturday", + columns: [ + "8", + "Every Saturday", + ], + }, + TableRow { + raw: "`9` | Every Sunday", + columns: [ + "9", + "Every Sunday", + ], + }, + ], + }, + ), + Text( + "Possible values of `encryption`:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "Value | Description", + columns: [ + "Value", + "Description", + ], + }, + split: "-------|------------", + rows: [ + TableRow { + raw: "`0` | Prefer encryption", + columns: [ + "0", + "Prefer encryption", + ], + }, + TableRow { + raw: "`1` | Force encryption on", + columns: [ + "1", + "Force encryption on", + ], + }, + TableRow { + raw: "`2` | Force encryption off", + columns: [ + "2", + "Force encryption off", + ], + }, + ], + }, + ), + Text( + "NB: the first options allows you to use both encrypted and unencrypted connections (this is the default); other options are mutually exclusive: e.g. by forcing encryption on you won't be able to use unencrypted connections and vice versa.", + ), + Text( + "", + ), + Text( + "Possible values of `proxy_type`:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "Value | Description", + columns: [ + "Value", + "Description", + ], + }, + split: "------|------------", + rows: [ + TableRow { + raw: "`-1` | Proxy is disabled", + columns: [ + "-1", + "Proxy is disabled", + ], + }, + TableRow { + raw: "`1` | HTTP proxy without authentication", + columns: [ + "1", + "HTTP proxy without authentication", + ], + }, + TableRow { + raw: "`2` | SOCKS5 proxy without authentication", + columns: [ + "2", + "SOCKS5 proxy without authentication", + ], + }, + TableRow { + raw: "`3` | HTTP proxy with authentication", + columns: [ + "3", + "HTTP proxy with authentication", + ], + }, + TableRow { + raw: "`4` | SOCKS5 proxy with authentication", + columns: [ + "4", + "SOCKS5 proxy with authentication", + ], + }, + TableRow { + raw: "`5` | SOCKS4 proxy without authentication", + columns: [ + "5", + "SOCKS4 proxy without authentication", + ], + }, + ], + }, + ), + Text( + "Possible values of `dyndns_service`:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "Value | Description", + columns: [ + "Value", + "Description", + ], + }, + split: "-------|------------", + rows: [ + TableRow { + raw: "`0` | Use DyDNS", + columns: [ + "0", + "Use DyDNS", + ], + }, + TableRow { + raw: "`1` | Use NOIP", + columns: [ + "1", + "Use NOIP", + ], + }, + ], + }, + ), + Text( + "Possible values of `max_ratio_act`:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "Value | Description", + columns: [ + "Value", + "Description", + ], + }, + split: "------|------------", + rows: [ + TableRow { + raw: "`0` | Pause torrent", + columns: [ + "0", + "Pause torrent", + ], + }, + TableRow { + raw: "`1` | Remove torrent", + columns: [ + "1", + "Remove torrent", + ], + }, + ], + }, + ), + Text( + "Possible values of `bittorrent_protocol`:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "Value | Description", + columns: [ + "Value", + "Description", + ], + }, + split: "-------|------------", + rows: [ + TableRow { + raw: "`0` | TCP and μTP", + columns: [ + "0", + "TCP and μTP", + ], + }, + TableRow { + raw: "`1` | TCP", + columns: [ + "1", + "TCP", + ], + }, + TableRow { + raw: "`2` | μTP", + columns: [ + "2", + "μTP", + ], + }, + ], + }, + ), + Text( + "Possible values of `upload_choking_algorithm`:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "Value | Description", + columns: [ + "Value", + "Description", + ], + }, + split: "-------|------------", + rows: [ + TableRow { + raw: "`0` | Round-robin", + columns: [ + "0", + "Round-robin", + ], + }, + TableRow { + raw: "`1` | Fastest upload", + columns: [ + "1", + "Fastest upload", + ], + }, + TableRow { + raw: "`2` | Anti-leech", + columns: [ + "2", + "Anti-leech", + ], + }, + ], + }, + ), + Text( + "Possible values of `upload_slots_behavior`:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "Value | Description", + columns: [ + "Value", + "Description", + ], + }, + split: "-------|------------", + rows: [ + TableRow { + raw: "`0` | Fixed slots", + columns: [ + "0", + "Fixed slots", + ], + }, + TableRow { + raw: "`1` | Upload rate based", + columns: [ + "1", + "Upload rate based", + ], + }, + ], + }, + ), + Text( + "Possible values of `utp_tcp_mixed_mode`:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "Value | Description", + columns: [ + "Value", + "Description", + ], + }, + split: "-------|------------", + rows: [ + TableRow { + raw: "`0` | Prefer TCP", + columns: [ + "0", + "Prefer TCP", + ], + }, + TableRow { + raw: "`1` | Peer proportional", + columns: [ + "1", + "Peer proportional", + ], + }, + ], + }, + ), + Text( + "Example:", + ), + Text( + "", + ), + Text( + "```JSON", + ), + Text( + "{", + ), + Text( + " \"add_trackers\": \"\",", + ), + Text( + " \"add_trackers_enabled\": false,", + ), + Text( + " \"alt_dl_limit\": 10240,", + ), + Text( + " \"alt_up_limit\": 10240,", + ), + Text( + " \"alternative_webui_enabled\": false,", + ), + Text( + " \"alternative_webui_path\": \"/home/user/Documents/qbit-webui\",", + ), + Text( + " \"announce_ip\": \"\",", + ), + Text( + " \"announce_to_all_tiers\": true,", + ), + Text( + " \"announce_to_all_trackers\": false,", + ), + Text( + " \"anonymous_mode\": false,", + ), + Text( + " \"async_io_threads\": 4,", + ), + Text( + " \"auto_delete_mode\": 0,", + ), + Text( + " \"auto_tmm_enabled\": false,", + ), + Text( + " \"autorun_enabled\": false,", + ), + Text( + " \"autorun_program\": \"\",", + ), + Text( + " \"banned_IPs\": \"\",", + ), + Text( + " \"bittorrent_protocol\": 0,", + ), + Text( + " \"bypass_auth_subnet_whitelist\": \"\",", + ), + Text( + " \"bypass_auth_subnet_whitelist_enabled\": false,", + ), + Text( + " \"bypass_local_auth\": false,", + ), + Text( + " \"category_changed_tmm_enabled\": false,", + ), + Text( + " \"checking_memory_use\": 32,", + ), + Text( + " \"create_subfolder_enabled\": true,", + ), + Text( + " \"current_interface_address\": \"\",", + ), + Text( + " \"current_network_interface\": \"\",", + ), + Text( + " \"dht\": true,", + ), + Text( + " \"disk_cache\": -1,", + ), + Text( + " \"disk_cache_ttl\": 60,", + ), + Text( + " \"dl_limit\": 0,", + ), + Text( + " \"dont_count_slow_torrents\": false,", + ), + Text( + " \"dyndns_domain\": \"changeme.dyndns.org\",", + ), + Text( + " \"dyndns_enabled\": false,", + ), + Text( + " \"dyndns_password\": \"\",", + ), + Text( + " \"dyndns_service\": 0,", + ), + Text( + " \"dyndns_username\": \"\",", + ), + Text( + " \"embedded_tracker_port\": 9000,", + ), + Text( + " \"enable_coalesce_read_write\": false,", + ), + Text( + " \"enable_embedded_tracker\": false,", + ), + Text( + " \"enable_multi_connections_from_same_ip\": false,", + ), + Text( + " \"enable_os_cache\": true,", + ), + Text( + " \"enable_piece_extent_affinity\": false,", + ), + Text( + " \"enable_upload_suggestions\": false,", + ), + Text( + " \"encryption\": 0,", + ), + Text( + " \"export_dir\": \"/home/user/Downloads/all\",", + ), + Text( + " \"export_dir_fin\": \"/home/user/Downloads/completed\",", + ), + Text( + " \"file_pool_size\": 40,", + ), + Text( + " \"incomplete_files_ext\": false,", + ), + Text( + " \"ip_filter_enabled\": false,", + ), + Text( + " \"ip_filter_path\": \"\",", + ), + Text( + " \"ip_filter_trackers\": false,", + ), + Text( + " \"limit_lan_peers\": true,", + ), + Text( + " \"limit_tcp_overhead\": false,", + ), + Text( + " \"limit_utp_rate\": true,", + ), + Text( + " \"listen_port\": 58925,", + ), + Text( + " \"locale\": \"en\",", + ), + Text( + " \"lsd\": true,", + ), + Text( + " \"mail_notification_auth_enabled\": false,", + ), + Text( + " \"mail_notification_email\": \"\",", + ), + Text( + " \"mail_notification_enabled\": false,", + ), + Text( + " \"mail_notification_password\": \"\",", + ), + Text( + " \"mail_notification_sender\": \"qBittorrent_notification@example.com\",", + ), + Text( + " \"mail_notification_smtp\": \"smtp.changeme.com\",", + ), + Text( + " \"mail_notification_ssl_enabled\": false,", + ), + Text( + " \"mail_notification_username\": \"\",", + ), + Text( + " \"max_active_downloads\": 3,", + ), + Text( + " \"max_active_torrents\": 5,", + ), + Text( + " \"max_active_uploads\": 3,", + ), + Text( + " \"max_connec\": 500,", + ), + Text( + " \"max_connec_per_torrent\": 100,", + ), + Text( + " \"max_ratio\": -1,", + ), + Text( + " \"max_ratio_act\": 0,", + ), + Text( + " \"max_ratio_enabled\": false,", + ), + Text( + " \"max_seeding_time\": -1,", + ), + Text( + " \"max_seeding_time_enabled\": false,", + ), + Text( + " \"max_uploads\": -1,", + ), + Text( + " \"max_uploads_per_torrent\": -1,", + ), + Text( + " \"outgoing_ports_max\": 0,", + ), + Text( + " \"outgoing_ports_min\": 0,", + ), + Text( + " \"pex\": true,", + ), + Text( + " \"preallocate_all\": false,", + ), + Text( + " \"proxy_auth_enabled\": false,", + ), + Text( + " \"proxy_ip\": \"0.0.0.0\",", + ), + Text( + " \"proxy_password\": \"\",", + ), + Text( + " \"proxy_peer_connections\": false,", + ), + Text( + " \"proxy_port\": 8080,", + ), + Text( + " \"proxy_torrents_only\": false,", + ), + Text( + " \"proxy_type\": 0,", + ), + Text( + " \"proxy_username\": \"\",", + ), + Text( + " \"queueing_enabled\": false,", + ), + Text( + " \"random_port\": false,", + ), + Text( + " \"recheck_completed_torrents\": false,", + ), + Text( + " \"resolve_peer_countries\": true,", + ), + Text( + " \"rss_auto_downloading_enabled\":true,", + ), + Text( + " \"rss_download_repack_proper_episodes\":true,", + ), + Text( + " \"rss_max_articles_per_feed\":50,", + ), + Text( + " \"rss_processing_enabled\":true,", + ), + Text( + " \"rss_refresh_interval\":30,", + ), + Text( + " \"rss_smart_episode_filters\":\"s(\\\\d+)e(\\\\d+)\\n(\\\\d+)x(\\\\d+)\\n(\\\\d{4}[.\\\\-]\\\\d{1,2}[.\\\\-]\\\\d{1,2})\",", + ), + Text( + " \"save_path\": \"/home/user/Downloads/\",", + ), + Text( + " \"save_path_changed_tmm_enabled\": false,", + ), + Text( + " \"save_resume_data_interval\": 60,", + ), + Text( + " \"scan_dirs\":", + ), + Text( + " {", + ), + Text( + " \"/home/user/Downloads/incoming/games\": 0,", + ), + Text( + " \"/home/user/Downloads/incoming/movies\": 1,", + ), + Text( + " },", + ), + Text( + " \"schedule_from_hour\": 8,", + ), + Text( + " \"schedule_from_min\": 0,", + ), + Text( + " \"schedule_to_hour\": 20,", + ), + Text( + " \"schedule_to_min\": 0,", + ), + Text( + " \"scheduler_days\": 0,", + ), + Text( + " \"scheduler_enabled\": false,", + ), + Text( + " \"send_buffer_low_watermark\": 10,", + ), + Text( + " \"send_buffer_watermark\": 500,", + ), + Text( + " \"send_buffer_watermark_factor\": 50,", + ), + Text( + " \"slow_torrent_dl_rate_threshold\": 2,", + ), + Text( + " \"slow_torrent_inactive_timer\": 60,", + ), + Text( + " \"slow_torrent_ul_rate_threshold\": 2,", + ), + Text( + " \"socket_backlog_size\": 30,", + ), + Text( + " \"start_paused_enabled\": false,", + ), + Text( + " \"stop_tracker_timeout\": 1,", + ), + Text( + " \"temp_path\": \"/home/user/Downloads/temp\",", + ), + Text( + " \"temp_path_enabled\": false,", + ), + Text( + " \"torrent_changed_tmm_enabled\": true,", + ), + Text( + " \"up_limit\": 0,", + ), + Text( + " \"upload_choking_algorithm\": 1,", + ), + Text( + " \"upload_slots_behavior\": 0,", + ), + Text( + " \"upnp\": true,", + ), + Text( + " \"use_https\": false,", + ), + Text( + " \"utp_tcp_mixed_mode\": 0,", + ), + Text( + " \"web_ui_address\": \"*\",", + ), + Text( + " \"web_ui_ban_duration\": 3600,", + ), + Text( + " \"web_ui_clickjacking_protection_enabled\": true,", + ), + Text( + " \"web_ui_csrf_protection_enabled\": true,", + ), + Text( + " \"web_ui_custom_http_headers\": \"\",", + ), + Text( + " \"web_ui_domain_list\": \"*\",", + ), + Text( + " \"web_ui_host_header_validation_enabled\": true,", + ), + Text( + " \"web_ui_https_cert_path\": \"\",", + ), + Text( + " \"web_ui_https_key_path\": \"\",", + ), + Text( + " \"web_ui_max_auth_fail_count\": 5,", + ), + Text( + " \"web_ui_port\": 8080,", + ), + Text( + " \"web_ui_secure_cookie_enabled\": true,", + ), + Text( + " \"web_ui_session_timeout\": 3600,", + ), + Text( + " \"web_ui_upnp\": false,", + ), + Text( + " \"web_ui_use_custom_http_headers_enabled\": false,", + ), + Text( + " \"web_ui_username\": \"admin\"", + ), + Text( + "}", + ), + Text( + "```", + ), + Text( + "", + ), + ], + children: [], + }, + TokenTree { + title: Some( + "Set application preferences", + ), + content: [ + Text( + "", + ), + Text( + "Name: `setPreferences`", + ), + Text( + "", + ), + Asterix( + "Parameters:", + ), + Text( + "", + ), + Text( + "A json object with key-value pairs of the settings you want to change and their new values.", + ), + Text( + "", + ), + Text( + "Example:", + ), + Text( + "", + ), + Text( + "```JSON", + ), + Text( + "json={\"save_path\":\"C:/Users/Dayman/Downloads\",\"queueing_enabled\":false,\"scan_dirs\":{\"C:/Games\": 0,\"D:/Downloads\": 1}}", + ), + Text( + "```", + ), + Text( + "", + ), + Asterix( + "Returns:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "HTTP Status Code | Scenario", + columns: [ + "HTTP Status Code", + "Scenario", + ], + }, + split: "----------------------------------|---------------------", + rows: [ + TableRow { + raw: "200 | All scenarios", + columns: [ + "200", + "All scenarios", + ], + }, + ], + }, + ), + Asterix( + "Notes**:", + ), + Text( + "", + ), + Text( + " 1. There is no need to pass all possible preferences' `token:value` pairs if you only want to change one option", + ), + Text( + " 1. Paths in `scan_dirs` must exist, otherwise this option will have no effect", + ), + Text( + " 1. String values must be quoted; integer and boolean values must never be quoted", + ), + Text( + "", + ), + Text( + "For a list of possible preference options see [Get application preferences](#get-application-preferences)", + ), + Text( + "", + ), + ], + children: [], + }, + TokenTree { + title: Some( + "Get default save path", + ), + content: [ + Text( + "", + ), + Text( + "Name: `defaultSavePath`", + ), + Text( + "", + ), + Asterix( + "Parameters:", + ), + Text( + "", + ), + Text( + "None", + ), + Text( + "", + ), + Asterix( + "Returns:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "HTTP Status Code | Scenario", + columns: [ + "HTTP Status Code", + "Scenario", + ], + }, + split: "----------------------------------|---------------------", + rows: [ + TableRow { + raw: "200 | All scenarios", + columns: [ + "200", + "All scenarios", + ], + }, + ], + }, + ), + Text( + "The response is a string with the default save path, e.g. `C:/Users/Dayman/Downloads`.", + ), + Text( + "", + ), + ], + children: [], + }, + ], + }, + TokenTree { + title: Some( + "Log", + ), + content: [ + Text( + "", + ), + Text( + "All Log API methods are under \"log\", e.g.: `/api/v2/log/methodName`.", + ), + Text( + "", + ), + ], + children: [ + TokenTree { + title: Some( + "Get log", + ), + content: [ + Text( + "", + ), + Text( + "Name: `main`", + ), + Text( + "", + ), + Asterix( + "Parameters:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "Parameter | Type | Description", + columns: [ + "Parameter", + "Type", + "Description", + ], + }, + split: "----------------|---------|------------", + rows: [ + TableRow { + raw: "`normal` | bool | Include normal messages (default: `true`)", + columns: [ + "normal", + "bool", + "Include normal messages (default: true)", + ], + }, + TableRow { + raw: "`info` | bool | Include info messages (default: `true`)", + columns: [ + "info", + "bool", + "Include info messages (default: true)", + ], + }, + TableRow { + raw: "`warning` | bool | Include warning messages (default: `true`)", + columns: [ + "warning", + "bool", + "Include warning messages (default: true)", + ], + }, + TableRow { + raw: "`critical` | bool | Include critical messages (default: `true`)", + columns: [ + "critical", + "bool", + "Include critical messages (default: true)", + ], + }, + TableRow { + raw: "`last_known_id` | integer | Exclude messages with \"message id\" <= `last_known_id` (default: `-1`)", + columns: [ + "last_known_id", + "integer", + "Exclude messages with \"message id\" <= last_known_id (default: -1)", + ], + }, + ], + }, + ), + Text( + "Example:", + ), + Text( + "", + ), + Text( + "```http", + ), + Text( + "/api/v2/log/main?normal=true&info=true&warning=true&critical=true&last_known_id=-1", + ), + Text( + "```", + ), + Text( + "", + ), + Asterix( + "Returns:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "HTTP Status Code | Scenario", + columns: [ + "HTTP Status Code", + "Scenario", + ], + }, + split: "----------------------------------|---------------------", + rows: [ + TableRow { + raw: "200 | All scenarios- see JSON below", + columns: [ + "200", + "All scenarios- see JSON below", + ], + }, + ], + }, + ), + Text( + "The response is a JSON array in which each element is an entry of the log.", + ), + Text( + "", + ), + Text( + "Each element of the array has the following properties:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "Property | Type | Description", + columns: [ + "Property", + "Type", + "Description", + ], + }, + split: "------------|---------|------------", + rows: [ + TableRow { + raw: "`id` | integer | ID of the message", + columns: [ + "id", + "integer", + "ID of the message", + ], + }, + TableRow { + raw: "`message` | string | Text of the message", + columns: [ + "message", + "string", + "Text of the message", + ], + }, + TableRow { + raw: "`timestamp` | integer | Milliseconds since epoch", + columns: [ + "timestamp", + "integer", + "Milliseconds since epoch", + ], + }, + TableRow { + raw: "`type` | integer | Type of the message: Log::NORMAL: `1`, Log::INFO: `2`, Log::WARNING: `4`, Log::CRITICAL: `8`", + columns: [ + "type", + "integer", + "Type of the message: Log::NORMAL: 1, Log::INFO: 2, Log::WARNING: 4, Log::CRITICAL: 8", + ], + }, + ], + }, + ), + Text( + "Example:", + ), + Text( + "", + ), + Text( + "```JSON", + ), + Text( + "[", + ), + Text( + " {", + ), + Text( + " \"id\":0,", + ), + Text( + " \"message\":\"qBittorrent v3.4.0 started\",", + ), + Text( + " \"timestamp\":1507969127860,", + ), + Text( + " \"type\":1", + ), + Text( + " },", + ), + Text( + " {", + ), + Text( + " \"id\":1,", + ), + Text( + " \"message\":\"qBittorrent is trying to listen on any interface port: 19036\",", + ), + Text( + " \"timestamp\":1507969127869,", + ), + Text( + " \"type\":2", + ), + Text( + " },", + ), + Text( + " {", + ), + Text( + " \"id\":2,", + ), + Text( + " \"message\":\"Peer ID: -qB3400-\",", + ), + Text( + " \"timestamp\":1507969127870,", + ), + Text( + " \"type\":1", + ), + Text( + " },", + ), + Text( + " {", + ), + Text( + " \"id\":3,", + ), + Text( + " \"message\":\"HTTP User-Agent is 'qBittorrent/3.4.0'\",", + ), + Text( + " \"timestamp\":1507969127870,", + ), + Text( + " \"type\":1", + ), + Text( + " },", + ), + Text( + " {", + ), + Text( + " \"id\":4,", + ), + Text( + " \"message\":\"DHT support [ON]\",", + ), + Text( + " \"timestamp\":1507969127871,", + ), + Text( + " \"type\":2", + ), + Text( + " },", + ), + Text( + " {", + ), + Text( + " \"id\":5,", + ), + Text( + " \"message\":\"Local Peer Discovery support [ON]\",", + ), + Text( + " \"timestamp\":1507969127871,", + ), + Text( + " \"type\":2", + ), + Text( + " },", + ), + Text( + " {", + ), + Text( + " \"id\":6,", + ), + Text( + " \"message\":\"PeX support [ON]\",", + ), + Text( + " \"timestamp\":1507969127871,", + ), + Text( + " \"type\":2", + ), + Text( + " },", + ), + Text( + " {", + ), + Text( + " \"id\":7,", + ), + Text( + " \"message\":\"Anonymous mode [OFF]\",", + ), + Text( + " \"timestamp\":1507969127871,", + ), + Text( + " \"type\":2", + ), + Text( + " },", + ), + Text( + " {", + ), + Text( + " \"id\":8,", + ), + Text( + " \"message\":\"Encryption support [ON]\",", + ), + Text( + " \"timestamp\":1507969127871,", + ), + Text( + " \"type\":2", + ), + Text( + " },", + ), + Text( + " {", + ), + Text( + " \"id\":9,", + ), + Text( + " \"message\":\"Embedded Tracker [OFF]\",", + ), + Text( + " \"timestamp\":1507969127871,", + ), + Text( + " \"type\":2", + ), + Text( + " },", + ), + Text( + " {", + ), + Text( + " \"id\":10,", + ), + Text( + " \"message\":\"UPnP / NAT-PMP support [ON]\",", + ), + Text( + " \"timestamp\":1507969127873,", + ), + Text( + " \"type\":2", + ), + Text( + " },", + ), + Text( + " {", + ), + Text( + " \"id\":11,", + ), + Text( + " \"message\":\"Web UI: Now listening on port 8080\",", + ), + Text( + " \"timestamp\":1507969127883,", + ), + Text( + " \"type\":1", + ), + Text( + " },", + ), + Text( + " {", + ), + Text( + " \"id\":12,", + ), + Text( + " \"message\":\"Options were saved successfully.\",", + ), + Text( + " \"timestamp\":1507969128055,", + ), + Text( + " \"type\":1", + ), + Text( + " },", + ), + Text( + " {", + ), + Text( + " \"id\":13,", + ), + Text( + " \"message\":\"qBittorrent is successfully listening on interface :: port: TCP/19036\",", + ), + Text( + " \"timestamp\":1507969128270,", + ), + Text( + " \"type\":2", + ), + Text( + " },", + ), + Text( + " {", + ), + Text( + " \"id\":14,", + ), + Text( + " \"message\":\"qBittorrent is successfully listening on interface 0.0.0.0 port: TCP/19036\",", + ), + Text( + " \"timestamp\":1507969128271,", + ), + Text( + " \"type\":2", + ), + Text( + " },", + ), + Text( + " {", + ), + Text( + " \"id\":15,", + ), + Text( + " \"message\":\"qBittorrent is successfully listening on interface 0.0.0.0 port: UDP/19036\",", + ), + Text( + " \"timestamp\":1507969128272,", + ), + Text( + " \"type\":2", + ), + Text( + " }", + ), + Text( + "]", + ), + Text( + "```", + ), + Text( + "", + ), + ], + children: [], + }, + TokenTree { + title: Some( + "Get peer log", + ), + content: [ + Text( + "", + ), + Text( + "Name: `peers`", + ), + Text( + "", + ), + Asterix( + "Parameters:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "Parameter | Type | Description", + columns: [ + "Parameter", + "Type", + "Description", + ], + }, + split: "----------------|---------|------------", + rows: [ + TableRow { + raw: "`last_known_id` | integer | Exclude messages with \"message id\" <= `last_known_id` (default: `-1`)", + columns: [ + "last_known_id", + "integer", + "Exclude messages with \"message id\" <= last_known_id (default: -1)", + ], + }, + ], + }, + ), + Asterix( + "Returns:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "HTTP Status Code | Scenario", + columns: [ + "HTTP Status Code", + "Scenario", + ], + }, + split: "----------------------------------|---------------------", + rows: [ + TableRow { + raw: "200 | All scenarios- see JSON below", + columns: [ + "200", + "All scenarios- see JSON below", + ], + }, + ], + }, + ), + Text( + "The response a JSON array. Each element of the array of objects (each object is the information relative to a peer) containing the following fields", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "Property | Type | Description", + columns: [ + "Property", + "Type", + "Description", + ], + }, + split: "------------|---------|------------", + rows: [ + TableRow { + raw: "`id` | integer | ID of the peer", + columns: [ + "id", + "integer", + "ID of the peer", + ], + }, + TableRow { + raw: "`ip` | string | IP of the peer", + columns: [ + "ip", + "string", + "IP of the peer", + ], + }, + TableRow { + raw: "`timestamp` | integer | Milliseconds since epoch", + columns: [ + "timestamp", + "integer", + "Milliseconds since epoch", + ], + }, + TableRow { + raw: "`blocked` | boolean | Whether or not the peer was blocked", + columns: [ + "blocked", + "boolean", + "Whether or not the peer was blocked", + ], + }, + TableRow { + raw: "`reason` | string | Reason of the block", + columns: [ + "reason", + "string", + "Reason of the block", + ], + }, + ], + }, + ), + ], + children: [], + }, + ], + }, + TokenTree { + title: Some( + "Sync", + ), + content: [ + Text( + "", + ), + Text( + "Sync API implements requests for obtaining changes since the last request.", + ), + Text( + "All Sync API methods are under \"sync\", e.g.: `/api/v2/sync/methodName`.", + ), + Text( + "", + ), + ], + children: [ + TokenTree { + title: Some( + "Get main data", + ), + content: [ + Text( + "", + ), + Text( + "Name: `maindata`", + ), + Text( + "", + ), + Asterix( + "Parameters:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "Parameter | Type | Description", + columns: [ + "Parameter", + "Type", + "Description", + ], + }, + split: "----------|---------|------------", + rows: [ + TableRow { + raw: "`rid` | integer | Response ID. If not provided, `rid=0` will be assumed. If the given `rid` is different from the one of last server reply, `full_update` will be `true` (see the server reply details for more info)", + columns: [ + "rid", + "integer", + "Response ID. If not provided, rid=0 will be assumed. If the given rid is different from the one of last server reply, full_update will be true (see the server reply details for more info)", + ], + }, + ], + }, + ), + Text( + "Example:", + ), + Text( + "", + ), + Text( + "```http", + ), + Text( + "/api/v2/sync/maindata?rid=14", + ), + Text( + "```", + ), + Text( + "", + ), + Asterix( + "Returns:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "HTTP Status Code | Scenario", + columns: [ + "HTTP Status Code", + "Scenario", + ], + }, + split: "----------------------------------|---------------------", + rows: [ + TableRow { + raw: "200 | All scenarios- see JSON below", + columns: [ + "200", + "All scenarios- see JSON below", + ], + }, + ], + }, + ), + Text( + "The response is a JSON object with the following possible fields", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "Property | Type | Description", + columns: [ + "Property", + "Type", + "Description", + ], + }, + split: "------------------------------|---------|------------", + rows: [ + TableRow { + raw: "`rid` | integer | Response ID", + columns: [ + "rid", + "integer", + "Response ID", + ], + }, + TableRow { + raw: "`full_update` | bool | Whether the response contains all the data or partial data", + columns: [ + "full_update", + "bool", + "Whether the response contains all the data or partial data", + ], + }, + TableRow { + raw: "`torrents` | object | Property: torrent hash, value: same as [torrent list](#get-torrent-list)", + columns: [ + "torrents", + "object", + "Property: torrent hash, value: same as [torrent list](#get-torrent-list)", + ], + }, + TableRow { + raw: "`torrents_removed` | array | List of hashes of torrents removed since last request", + columns: [ + "torrents_removed", + "array", + "List of hashes of torrents removed since last request", + ], + }, + TableRow { + raw: "`categories` | object | Info for categories added since last request", + columns: [ + "categories", + "object", + "Info for categories added since last request", + ], + }, + TableRow { + raw: "`categories_removed` | array | List of categories removed since last request", + columns: [ + "categories_removed", + "array", + "List of categories removed since last request", + ], + }, + TableRow { + raw: "`tags` | array | List of tags added since last request", + columns: [ + "tags", + "array", + "List of tags added since last request", + ], + }, + TableRow { + raw: "`tags_removed` | array | List of tags removed since last request", + columns: [ + "tags_removed", + "array", + "List of tags removed since last request", + ], + }, + TableRow { + raw: "`server_state` | object | Global transfer info", + columns: [ + "server_state", + "object", + "Global transfer info", + ], + }, + ], + }, + ), + Text( + "Example:", + ), + Text( + "", + ), + Text( + "```JSON", + ), + Text( + "{", + ), + Text( + " \"rid\":15,", + ), + Text( + " \"torrents\":", + ), + Text( + " {", + ), + Text( + " \"8c212779b4abde7c6bc608063a0d008b7e40ce32\":", + ), + Text( + " {", + ), + Text( + " \"state\":\"pausedUP\"", + ), + Text( + " }", + ), + Text( + " }", + ), + Text( + "}", + ), + Text( + "```", + ), + Text( + "", + ), + ], + children: [], + }, + TokenTree { + title: Some( + "Get torrent peers data", + ), + content: [ + Text( + "", + ), + Text( + "Name: `torrentPeers`", + ), + Text( + "", + ), + Asterix( + "Parameters:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "Parameter | Type | Description", + columns: [ + "Parameter", + "Type", + "Description", + ], + }, + split: "----------|---------|------------", + rows: [ + TableRow { + raw: "`hash` | string | Torrent hash", + columns: [ + "hash", + "string", + "Torrent hash", + ], + }, + TableRow { + raw: "`rid` | integer | Response ID. If not provided, `rid=0` will be assumed. If the given `rid` is different from the one of last server reply, `full_update` will be `true` (see the server reply details for more info)", + columns: [ + "rid", + "integer", + "Response ID. If not provided, rid=0 will be assumed. If the given rid is different from the one of last server reply, full_update will be true (see the server reply details for more info)", + ], + }, + ], + }, + ), + Text( + "Example:", + ), + Text( + "", + ), + Text( + "```http", + ), + Text( + "/api/v2/sync/torrentPeers?hash=8c212779b4abde7c6bc608063a0d008b7e40ce32?rid=14", + ), + Text( + "```", + ), + Text( + "", + ), + Asterix( + "Returns:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "HTTP Status Code | Scenario", + columns: [ + "HTTP Status Code", + "Scenario", + ], + }, + split: "----------------------------------|---------------------", + rows: [ + TableRow { + raw: "404 | Torrent hash was not found", + columns: [ + "404", + "Torrent hash was not found", + ], + }, + TableRow { + raw: "200 | All other scenarios- see JSON below", + columns: [ + "200", + "All other scenarios- see JSON below", + ], + }, + ], + }, + ), + Text( + "The response is TODO", + ), + Text( + "", + ), + ], + children: [], + }, + ], + }, + TokenTree { + title: Some( + "Transfer info", + ), + content: [ + Text( + "", + ), + Text( + "All Transfer info API methods are under \"transfer\", e.g.: `/api/v2/transfer/methodName`.", + ), + Text( + "", + ), + ], + children: [ + TokenTree { + title: Some( + "Get global transfer info", + ), + content: [ + Text( + "", + ), + Text( + "This method returns info you usually see in qBt status bar.", + ), + Text( + "", + ), + Text( + "Name: `info`", + ), + Text( + "", + ), + Asterix( + "Parameters:", + ), + Text( + "", + ), + Text( + "None", + ), + Text( + "", + ), + Asterix( + "Returns:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "HTTP Status Code | Scenario", + columns: [ + "HTTP Status Code", + "Scenario", + ], + }, + split: "----------------------------------|---------------------", + rows: [ + TableRow { + raw: "200 | All scenarios- see JSON below", + columns: [ + "200", + "All scenarios- see JSON below", + ], + }, + ], + }, + ), + Text( + "The response is a JSON object with the following fields", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "Property | Type | Description", + columns: [ + "Property", + "Type", + "Description", + ], + }, + split: "--------------------|---------|------------", + rows: [ + TableRow { + raw: "`dl_info_speed` | integer | Global download rate (bytes/s)", + columns: [ + "dl_info_speed", + "integer", + "Global download rate (bytes/s)", + ], + }, + TableRow { + raw: "`dl_info_data` | integer | Data downloaded this session (bytes)", + columns: [ + "dl_info_data", + "integer", + "Data downloaded this session (bytes)", + ], + }, + TableRow { + raw: "`up_info_speed` | integer | Global upload rate (bytes/s)", + columns: [ + "up_info_speed", + "integer", + "Global upload rate (bytes/s)", + ], + }, + TableRow { + raw: "`up_info_data` | integer | Data uploaded this session (bytes)", + columns: [ + "up_info_data", + "integer", + "Data uploaded this session (bytes)", + ], + }, + TableRow { + raw: "`dl_rate_limit` | integer | Download rate limit (bytes/s)", + columns: [ + "dl_rate_limit", + "integer", + "Download rate limit (bytes/s)", + ], + }, + TableRow { + raw: "`up_rate_limit` | integer | Upload rate limit (bytes/s)", + columns: [ + "up_rate_limit", + "integer", + "Upload rate limit (bytes/s)", + ], + }, + TableRow { + raw: "`dht_nodes` | integer | DHT nodes connected to", + columns: [ + "dht_nodes", + "integer", + "DHT nodes connected to", + ], + }, + TableRow { + raw: "`connection_status` | string | Connection status. See possible values here below", + columns: [ + "connection_status", + "string", + "Connection status. See possible values here below", + ], + }, + ], + }, + ), + Text( + "In addition to the above in partial data requests (see [Get partial data](#get-partial-data) for more info):", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "Property | Type | Description", + columns: [ + "Property", + "Type", + "Description", + ], + }, + split: "-----------------------|---------|------------", + rows: [ + TableRow { + raw: "`queueing` | bool | True if torrent queueing is enabled", + columns: [ + "queueing", + "bool", + "True if torrent queueing is enabled", + ], + }, + TableRow { + raw: "`use_alt_speed_limits` | bool | True if alternative speed limits are enabled", + columns: [ + "use_alt_speed_limits", + "bool", + "True if alternative speed limits are enabled", + ], + }, + TableRow { + raw: "`refresh_interval` | integer | Transfer list refresh interval (milliseconds)", + columns: [ + "refresh_interval", + "integer", + "Transfer list refresh interval (milliseconds)", + ], + }, + ], + }, + ), + Text( + "Possible values of `connection_status`:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "Value |", + columns: [ + "Value", + "", + ], + }, + split: "--------------------|", + rows: [ + TableRow { + raw: "`connected` |", + columns: [ + "connected", + "", + ], + }, + TableRow { + raw: "`firewalled` |", + columns: [ + "firewalled", + "", + ], + }, + TableRow { + raw: "`disconnected` |", + columns: [ + "disconnected", + "", + ], + }, + ], + }, + ), + Text( + "Example:", + ), + Text( + "", + ), + Text( + "```JSON", + ), + Text( + "{", + ), + Text( + " \"connection_status\":\"connected\",", + ), + Text( + " \"dht_nodes\":386,", + ), + Text( + " \"dl_info_data\":681521119,", + ), + Text( + " \"dl_info_speed\":0,", + ), + Text( + " \"dl_rate_limit\":0,", + ), + Text( + " \"up_info_data\":10747904,", + ), + Text( + " \"up_info_speed\":0,", + ), + Text( + " \"up_rate_limit\":1048576", + ), + Text( + "}", + ), + Text( + "```", + ), + Text( + "", + ), + ], + children: [], + }, + TokenTree { + title: Some( + "Get alternative speed limits state", + ), + content: [ + Text( + "", + ), + Text( + "Name: `speedLimitsMode`", + ), + Text( + "", + ), + Asterix( + "Parameters:", + ), + Text( + "", + ), + Text( + "None", + ), + Text( + "", + ), + Asterix( + "Returns:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "HTTP Status Code | Scenario", + columns: [ + "HTTP Status Code", + "Scenario", + ], + }, + split: "----------------------------------|---------------------", + rows: [ + TableRow { + raw: "200 | All scenarios", + columns: [ + "200", + "All scenarios", + ], + }, + ], + }, + ), + Text( + "The response is `1` if alternative speed limits are enabled, `0` otherwise.", + ), + Text( + "", + ), + ], + children: [], + }, + TokenTree { + title: Some( + "Toggle alternative speed limits", + ), + content: [ + Text( + "", + ), + Text( + "Name: `toggleSpeedLimitsMode`", + ), + Text( + "", + ), + Asterix( + "Parameters:", + ), + Text( + "", + ), + Text( + "None", + ), + Text( + "", + ), + Asterix( + "Returns:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "HTTP Status Code | Scenario", + columns: [ + "HTTP Status Code", + "Scenario", + ], + }, + split: "----------------------------------|---------------------", + rows: [ + TableRow { + raw: "200 | All scenarios", + columns: [ + "200", + "All scenarios", + ], + }, + ], + }, + ), + ], + children: [], + }, + TokenTree { + title: Some( + "Get global download limit", + ), + content: [ + Text( + "", + ), + Text( + "Name: `downloadLimit`", + ), + Text( + "", + ), + Asterix( + "Parameters:", + ), + Text( + "", + ), + Text( + "None", + ), + Text( + "", + ), + Asterix( + "Returns:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "HTTP Status Code | Scenario", + columns: [ + "HTTP Status Code", + "Scenario", + ], + }, + split: "----------------------------------|---------------------", + rows: [ + TableRow { + raw: "200 | All scenarios", + columns: [ + "200", + "All scenarios", + ], + }, + ], + }, + ), + Text( + "The response is the value of current global download speed limit in bytes/second; this value will be zero if no limit is applied.", + ), + Text( + "", + ), + ], + children: [], + }, + TokenTree { + title: Some( + "Set global download limit", + ), + content: [ + Text( + "", + ), + Text( + "Name: `setDownloadLimit`", + ), + Text( + "", + ), + Asterix( + "Parameters:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "Parameter | Type | Description", + columns: [ + "Parameter", + "Type", + "Description", + ], + }, + split: "----------------------------------|---------|------------", + rows: [ + TableRow { + raw: "`limit` | integer | The global download speed limit to set in bytes/second", + columns: [ + "limit", + "integer", + "The global download speed limit to set in bytes/second", + ], + }, + ], + }, + ), + Asterix( + "Returns:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "HTTP Status Code | Scenario", + columns: [ + "HTTP Status Code", + "Scenario", + ], + }, + split: "----------------------------------|---------------------", + rows: [ + TableRow { + raw: "200 | All scenarios", + columns: [ + "200", + "All scenarios", + ], + }, + ], + }, + ), + ], + children: [], + }, + TokenTree { + title: Some( + "Get global upload limit", + ), + content: [ + Text( + "", + ), + Text( + "Name: `uploadLimit`", + ), + Text( + "", + ), + Asterix( + "Parameters:", + ), + Text( + "", + ), + Text( + "None", + ), + Text( + "", + ), + Asterix( + "Returns:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "HTTP Status Code | Scenario", + columns: [ + "HTTP Status Code", + "Scenario", + ], + }, + split: "----------------------------------|---------------------", + rows: [ + TableRow { + raw: "200 | All scenarios", + columns: [ + "200", + "All scenarios", + ], + }, + ], + }, + ), + Text( + "The response is the value of current global upload speed limit in bytes/second; this value will be zero if no limit is applied.", + ), + Text( + "", + ), + ], + children: [], + }, + TokenTree { + title: Some( + "Set global upload limit", + ), + content: [ + Text( + "", + ), + Text( + "Name: `setUploadLimit`", + ), + Text( + "", + ), + Asterix( + "Parameters:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "Parameter | Type | Description", + columns: [ + "Parameter", + "Type", + "Description", + ], + }, + split: "----------------------------------|---------|------------", + rows: [ + TableRow { + raw: "`limit` | integer | The global upload speed limit to set in bytes/second", + columns: [ + "limit", + "integer", + "The global upload speed limit to set in bytes/second", + ], + }, + ], + }, + ), + Asterix( + "Returns:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "HTTP Status Code | Scenario", + columns: [ + "HTTP Status Code", + "Scenario", + ], + }, + split: "----------------------------------|---------------------", + rows: [ + TableRow { + raw: "200 | All scenarios", + columns: [ + "200", + "All scenarios", + ], + }, + ], + }, + ), + ], + children: [], + }, + TokenTree { + title: Some( + "Ban peers", + ), + content: [ + Text( + "", + ), + Text( + "Name: `banPeers`", + ), + Text( + "", + ), + Asterix( + "Parameters:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "Parameter | Type | Description", + columns: [ + "Parameter", + "Type", + "Description", + ], + }, + split: "----------------------------------|---------|------------", + rows: [ + TableRow { + raw: "`peers` | string | The peer to ban, or multiple peers separated by a pipe `\\|`. Each peer is a colon-separated `host:port`", + columns: [ + "peers", + "string", + "The peer to ban, or multiple peers separated by a pipe \\", + ". Each peer is a colon-separated host:port", + ], + }, + ], + }, + ), + Asterix( + "Returns:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "HTTP Status Code | Scenario", + columns: [ + "HTTP Status Code", + "Scenario", + ], + }, + split: "----------------------------------|---------------------", + rows: [ + TableRow { + raw: "200 | All scenarios", + columns: [ + "200", + "All scenarios", + ], + }, + ], + }, + ), + ], + children: [], + }, + ], + }, + TokenTree { + title: Some( + "Torrent management", + ), + content: [ + Text( + "", + ), + Text( + "All Torrent management API methods are under \"torrents\", e.g.: `/api/v2/torrents/methodName`.", + ), + Text( + "", + ), + ], + children: [ + TokenTree { + title: Some( + "Get torrent list", + ), + content: [ + Text( + "", + ), + Text( + "Name: `info`", + ), + Text( + "", + ), + Asterix( + "Parameters:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "Parameter | Type | Description", + columns: [ + "Parameter", + "Type", + "Description", + ], + }, + split: "----------------------|---------|------------", + rows: [ + TableRow { + raw: "`filter` _optional_ | string | Filter torrent list by state. Allowed state filters: `all`, `downloading`, `seeding`, `completed`, `paused`, `active`, `inactive`, `resumed`, `stalled`, `stalled_uploading`, `stalled_downloading`, `errored`", + columns: [ + "filter _optional_", + "string", + "Filter torrent list by state. Allowed state filters: all, downloading, seeding, completed, paused, active, inactive, resumed, stalled, stalled_uploading, stalled_downloading, errored", + ], + }, + TableRow { + raw: "`category` _optional_ | string | Get torrents with the given category (empty string means \"without category\"; no \"category\" parameter means \"any category\" <- broken until [#11748](https://github.com/qbittorrent/qBittorrent/issues/11748) is resolved). Remember to URL-encode the category name. For example, `My category` becomes `My%20category`", + columns: [ + "category _optional_", + "string", + "Get torrents with the given category (empty string means \"without category\"; no \"category\" parameter means \"any category\" <- broken until [#11748](https://github.com/qbittorrent/qBittorrent/issues/11748) is resolved). Remember to URL-encode the category name. For example, My category becomes My%20category", + ], + }, + TableRow { + raw: "`tag` _optional_ since 2.8.3 | string | Get torrents with the given tag (empty string means \"without tag\"; no \"tag\" parameter means \"any tag\". Remember to URL-encode the category name. For example, `My tag` becomes `My%20tag`", + columns: [ + "tag _optional_ since 2.8.3", + "string", + "Get torrents with the given tag (empty string means \"without tag\"; no \"tag\" parameter means \"any tag\". Remember to URL-encode the category name. For example, My tag becomes My%20tag", + ], + }, + TableRow { + raw: "`sort` _optional_ | string | Sort torrents by given key. They can be sorted using any field of the response's JSON array (which are documented below) as the sort key.", + columns: [ + "sort _optional_", + "string", + "Sort torrents by given key. They can be sorted using any field of the response's JSON array (which are documented below) as the sort key.", + ], + }, + TableRow { + raw: "`reverse` _optional_ | bool | Enable reverse sorting. Defaults to `false`", + columns: [ + "reverse _optional_", + "bool", + "Enable reverse sorting. Defaults to false", + ], + }, + TableRow { + raw: "`limit` _optional_ | integer | Limit the number of torrents returned", + columns: [ + "limit _optional_", + "integer", + "Limit the number of torrents returned", + ], + }, + TableRow { + raw: "`offset` _optional_ | integer | Set offset (if less than 0, offset from end)", + columns: [ + "offset _optional_", + "integer", + "Set offset (if less than 0, offset from end)", + ], + }, + TableRow { + raw: "`hashes` _optional_ | string | Filter by hashes. Can contain multiple hashes separated by `\\|`", + columns: [ + "hashes _optional_", + "string", + "Filter by hashes. Can contain multiple hashes separated by \\", + "", + ], + }, + ], + }, + ), + Text( + "Example:", + ), + Text( + "", + ), + Text( + "```http", + ), + Text( + "/api/v2/torrents/info?filter=downloading&category=sample%20category&sort=ratio", + ), + Text( + "```", + ), + Text( + "", + ), + Asterix( + "Returns:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "HTTP Status Code | Scenario", + columns: [ + "HTTP Status Code", + "Scenario", + ], + }, + split: "----------------------------------|---------------------", + rows: [ + TableRow { + raw: "200 | All scenarios- see JSON below", + columns: [ + "200", + "All scenarios- see JSON below", + ], + }, + ], + }, + ), + Text( + "The response is a JSON array with the following fields", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "Property | Type | Description", + columns: [ + "Property", + "Type", + "Description", + ], + }, + split: "---------------------|---------|------------", + rows: [ + TableRow { + raw: "`added_on` | integer | Time (Unix Epoch) when the torrent was added to the client", + columns: [ + "added_on", + "integer", + "Time (Unix Epoch) when the torrent was added to the client", + ], + }, + TableRow { + raw: "`amount_left` | integer | Amount of data left to download (bytes)", + columns: [ + "amount_left", + "integer", + "Amount of data left to download (bytes)", + ], + }, + TableRow { + raw: "`auto_tmm` | bool | Whether this torrent is managed by Automatic Torrent Management", + columns: [ + "auto_tmm", + "bool", + "Whether this torrent is managed by Automatic Torrent Management", + ], + }, + TableRow { + raw: "`availability` | float | Percentage of file pieces currently available", + columns: [ + "availability", + "float", + "Percentage of file pieces currently available", + ], + }, + TableRow { + raw: "`category` | string | Category of the torrent", + columns: [ + "category", + "string", + "Category of the torrent", + ], + }, + TableRow { + raw: "`completed` | integer | Amount of transfer data completed (bytes)", + columns: [ + "completed", + "integer", + "Amount of transfer data completed (bytes)", + ], + }, + TableRow { + raw: "`completion_on` | integer | Time (Unix Epoch) when the torrent completed", + columns: [ + "completion_on", + "integer", + "Time (Unix Epoch) when the torrent completed", + ], + }, + TableRow { + raw: "`content_path` | string | Absolute path of torrent content (root path for multifile torrents, absolute file path for singlefile torrents)", + columns: [ + "content_path", + "string", + "Absolute path of torrent content (root path for multifile torrents, absolute file path for singlefile torrents)", + ], + }, + TableRow { + raw: "`dl_limit` | integer | Torrent download speed limit (bytes/s). `-1` if ulimited.", + columns: [ + "dl_limit", + "integer", + "Torrent download speed limit (bytes/s). -1 if ulimited.", + ], + }, + TableRow { + raw: "`dlspeed` | integer | Torrent download speed (bytes/s)", + columns: [ + "dlspeed", + "integer", + "Torrent download speed (bytes/s)", + ], + }, + TableRow { + raw: "`downloaded` | integer | Amount of data downloaded", + columns: [ + "downloaded", + "integer", + "Amount of data downloaded", + ], + }, + TableRow { + raw: "`downloaded_session` | integer | Amount of data downloaded this session", + columns: [ + "downloaded_session", + "integer", + "Amount of data downloaded this session", + ], + }, + TableRow { + raw: "`eta` | integer | Torrent ETA (seconds)", + columns: [ + "eta", + "integer", + "Torrent ETA (seconds)", + ], + }, + TableRow { + raw: "`f_l_piece_prio` | bool | True if first last piece are prioritized", + columns: [ + "f_l_piece_prio", + "bool", + "True if first last piece are prioritized", + ], + }, + TableRow { + raw: "`force_start` | bool | True if force start is enabled for this torrent", + columns: [ + "force_start", + "bool", + "True if force start is enabled for this torrent", + ], + }, + TableRow { + raw: "`hash` | string | Torrent hash", + columns: [ + "hash", + "string", + "Torrent hash", + ], + }, + TableRow { + raw: "`last_activity` | integer | Last time (Unix Epoch) when a chunk was downloaded/uploaded", + columns: [ + "last_activity", + "integer", + "Last time (Unix Epoch) when a chunk was downloaded/uploaded", + ], + }, + TableRow { + raw: "`magnet_uri` | string | Magnet URI corresponding to this torrent", + columns: [ + "magnet_uri", + "string", + "Magnet URI corresponding to this torrent", + ], + }, + TableRow { + raw: "`max_ratio` | float | Maximum share ratio until torrent is stopped from seeding/uploading", + columns: [ + "max_ratio", + "float", + "Maximum share ratio until torrent is stopped from seeding/uploading", + ], + }, + TableRow { + raw: "`max_seeding_time` | integer | Maximum seeding time (seconds) until torrent is stopped from seeding", + columns: [ + "max_seeding_time", + "integer", + "Maximum seeding time (seconds) until torrent is stopped from seeding", + ], + }, + TableRow { + raw: "`name` | string | Torrent name", + columns: [ + "name", + "string", + "Torrent name", + ], + }, + TableRow { + raw: "`num_complete` | integer | Number of seeds in the swarm", + columns: [ + "num_complete", + "integer", + "Number of seeds in the swarm", + ], + }, + TableRow { + raw: "`num_incomplete` | integer | Number of leechers in the swarm", + columns: [ + "num_incomplete", + "integer", + "Number of leechers in the swarm", + ], + }, + TableRow { + raw: "`num_leechs` | integer | Number of leechers connected to", + columns: [ + "num_leechs", + "integer", + "Number of leechers connected to", + ], + }, + TableRow { + raw: "`num_seeds` | integer | Number of seeds connected to", + columns: [ + "num_seeds", + "integer", + "Number of seeds connected to", + ], + }, + TableRow { + raw: "`priority` | integer | Torrent priority. Returns -1 if queuing is disabled or torrent is in seed mode", + columns: [ + "priority", + "integer", + "Torrent priority. Returns -1 if queuing is disabled or torrent is in seed mode", + ], + }, + TableRow { + raw: "`progress` | float | Torrent progress (percentage/100)", + columns: [ + "progress", + "float", + "Torrent progress (percentage/100)", + ], + }, + TableRow { + raw: "`ratio` | float | Torrent share ratio. Max ratio value: 9999.", + columns: [ + "ratio", + "float", + "Torrent share ratio. Max ratio value: 9999.", + ], + }, + TableRow { + raw: "`ratio_limit` | float | TODO (what is different from `max_ratio`?)", + columns: [ + "ratio_limit", + "float", + "TODO (what is different from max_ratio?)", + ], + }, + TableRow { + raw: "`save_path` | string | Path where this torrent's data is stored", + columns: [ + "save_path", + "string", + "Path where this torrent's data is stored", + ], + }, + TableRow { + raw: "`seeding_time` | integer | Torrent elapsed time while complete (seconds)", + columns: [ + "seeding_time", + "integer", + "Torrent elapsed time while complete (seconds)", + ], + }, + TableRow { + raw: "`seeding_time_limit` | integer | TODO (what is different from `max_seeding_time`?) seeding_time_limit is a per torrent setting, when Automatic Torrent Management is disabled, furthermore then max_seeding_time is set to seeding_time_limit for this torrent. If Automatic Torrent Management is enabled, the value is -2. And if max_seeding_time is unset it have a default value -1.", + columns: [ + "seeding_time_limit", + "integer", + "TODO (what is different from max_seeding_time?) seeding_time_limit is a per torrent setting, when Automatic Torrent Management is disabled, furthermore then max_seeding_time is set to seeding_time_limit for this torrent. If Automatic Torrent Management is enabled, the value is -2. And if max_seeding_time is unset it have a default value -1.", + ], + }, + TableRow { + raw: "`seen_complete` | integer | Time (Unix Epoch) when this torrent was last seen complete", + columns: [ + "seen_complete", + "integer", + "Time (Unix Epoch) when this torrent was last seen complete", + ], + }, + TableRow { + raw: "`seq_dl` | bool | True if sequential download is enabled", + columns: [ + "seq_dl", + "bool", + "True if sequential download is enabled", + ], + }, + TableRow { + raw: "`size` | integer | Total size (bytes) of files selected for download", + columns: [ + "size", + "integer", + "Total size (bytes) of files selected for download", + ], + }, + TableRow { + raw: "`state` | string | Torrent state. See table here below for the possible values", + columns: [ + "state", + "string", + "Torrent state. See table here below for the possible values", + ], + }, + TableRow { + raw: "`super_seeding` | bool | True if super seeding is enabled", + columns: [ + "super_seeding", + "bool", + "True if super seeding is enabled", + ], + }, + TableRow { + raw: "`tags` | string | Comma-concatenated tag list of the torrent", + columns: [ + "tags", + "string", + "Comma-concatenated tag list of the torrent", + ], + }, + TableRow { + raw: "`time_active` | integer | Total active time (seconds)", + columns: [ + "time_active", + "integer", + "Total active time (seconds)", + ], + }, + TableRow { + raw: "`total_size` | integer | Total size (bytes) of all file in this torrent (including unselected ones)", + columns: [ + "total_size", + "integer", + "Total size (bytes) of all file in this torrent (including unselected ones)", + ], + }, + TableRow { + raw: "`tracker` | string | The first tracker with working status. Returns empty string if no tracker is working.", + columns: [ + "tracker", + "string", + "The first tracker with working status. Returns empty string if no tracker is working.", + ], + }, + TableRow { + raw: "`up_limit` | integer | Torrent upload speed limit (bytes/s). `-1` if ulimited.", + columns: [ + "up_limit", + "integer", + "Torrent upload speed limit (bytes/s). -1 if ulimited.", + ], + }, + TableRow { + raw: "`uploaded` | integer | Amount of data uploaded", + columns: [ + "uploaded", + "integer", + "Amount of data uploaded", + ], + }, + TableRow { + raw: "`uploaded_session` | integer | Amount of data uploaded this session", + columns: [ + "uploaded_session", + "integer", + "Amount of data uploaded this session", + ], + }, + TableRow { + raw: "`upspeed` | integer | Torrent upload speed (bytes/s)", + columns: [ + "upspeed", + "integer", + "Torrent upload speed (bytes/s)", + ], + }, + ], + }, + ), + Text( + "Possible values of `state`:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "Value | Description", + columns: [ + "Value", + "Description", + ], + }, + split: "--------------|------------", + rows: [ + TableRow { + raw: "`error` | Some error occurred, applies to paused torrents", + columns: [ + "error", + "Some error occurred, applies to paused torrents", + ], + }, + TableRow { + raw: "`missingFiles`| Torrent data files is missing", + columns: [ + "missingFiles", + "Torrent data files is missing", + ], + }, + TableRow { + raw: "`uploading` | Torrent is being seeded and data is being transferred", + columns: [ + "uploading", + "Torrent is being seeded and data is being transferred", + ], + }, + TableRow { + raw: "`pausedUP` | Torrent is paused and has finished downloading", + columns: [ + "pausedUP", + "Torrent is paused and has finished downloading", + ], + }, + TableRow { + raw: "`queuedUP` | Queuing is enabled and torrent is queued for upload", + columns: [ + "queuedUP", + "Queuing is enabled and torrent is queued for upload", + ], + }, + TableRow { + raw: "`stalledUP` | Torrent is being seeded, but no connection were made", + columns: [ + "stalledUP", + "Torrent is being seeded, but no connection were made", + ], + }, + TableRow { + raw: "`checkingUP` | Torrent has finished downloading and is being checked", + columns: [ + "checkingUP", + "Torrent has finished downloading and is being checked", + ], + }, + TableRow { + raw: "`forcedUP` | Torrent is forced to uploading and ignore queue limit", + columns: [ + "forcedUP", + "Torrent is forced to uploading and ignore queue limit", + ], + }, + TableRow { + raw: "`allocating` | Torrent is allocating disk space for download", + columns: [ + "allocating", + "Torrent is allocating disk space for download", + ], + }, + TableRow { + raw: "`downloading` | Torrent is being downloaded and data is being transferred", + columns: [ + "downloading", + "Torrent is being downloaded and data is being transferred", + ], + }, + TableRow { + raw: "`metaDL` | Torrent has just started downloading and is fetching metadata", + columns: [ + "metaDL", + "Torrent has just started downloading and is fetching metadata", + ], + }, + TableRow { + raw: "`pausedDL` | Torrent is paused and has NOT finished downloading", + columns: [ + "pausedDL", + "Torrent is paused and has NOT finished downloading", + ], + }, + TableRow { + raw: "`queuedDL` | Queuing is enabled and torrent is queued for download", + columns: [ + "queuedDL", + "Queuing is enabled and torrent is queued for download", + ], + }, + TableRow { + raw: "`stalledDL` | Torrent is being downloaded, but no connection were made", + columns: [ + "stalledDL", + "Torrent is being downloaded, but no connection were made", + ], + }, + TableRow { + raw: "`checkingDL` | Same as checkingUP, but torrent has NOT finished downloading", + columns: [ + "checkingDL", + "Same as checkingUP, but torrent has NOT finished downloading", + ], + }, + TableRow { + raw: "`forcedDL` | Torrent is forced to downloading to ignore queue limit", + columns: [ + "forcedDL", + "Torrent is forced to downloading to ignore queue limit", + ], + }, + TableRow { + raw: "`checkingResumeData`| Checking resume data on qBt startup", + columns: [ + "checkingResumeData", + "Checking resume data on qBt startup", + ], + }, + TableRow { + raw: "`moving` | Torrent is moving to another location", + columns: [ + "moving", + "Torrent is moving to another location", + ], + }, + TableRow { + raw: "`unknown` | Unknown status", + columns: [ + "unknown", + "Unknown status", + ], + }, + ], + }, + ), + Text( + "Example:", + ), + Text( + "", + ), + Text( + "```JSON", + ), + Text( + "[", + ), + Text( + " {", + ), + Text( + " \"dlspeed\":9681262,", + ), + Text( + " \"eta\":87,", + ), + Text( + " \"f_l_piece_prio\":false,", + ), + Text( + " \"force_start\":false,", + ), + Text( + " \"hash\":\"8c212779b4abde7c6bc608063a0d008b7e40ce32\",", + ), + Text( + " \"category\":\"\",", + ), + Text( + " \"tags\": \"\",", + ), + Text( + " \"name\":\"debian-8.1.0-amd64-CD-1.iso\",", + ), + Text( + " \"num_complete\":-1,", + ), + Text( + " \"num_incomplete\":-1,", + ), + Text( + " \"num_leechs\":2,", + ), + Text( + " \"num_seeds\":54,", + ), + Text( + " \"priority\":1,", + ), + Text( + " \"progress\":0.16108787059783936,", + ), + Text( + " \"ratio\":0,", + ), + Text( + " \"seq_dl\":false,", + ), + Text( + " \"size\":657457152,", + ), + Text( + " \"state\":\"downloading\",", + ), + Text( + " \"super_seeding\":false,", + ), + Text( + " \"upspeed\":0", + ), + Text( + " },", + ), + Text( + " {", + ), + Text( + " another_torrent_info", + ), + Text( + " }", + ), + Text( + "]", + ), + Text( + "```", + ), + Text( + "", + ), + ], + children: [], + }, + TokenTree { + title: Some( + "Get torrent generic properties", + ), + content: [ + Text( + "", + ), + Text( + "Requires knowing the torrent hash. You can get it from [torrent list](#get-torrent-list).", + ), + Text( + "", + ), + Text( + "Name: `properties`", + ), + Text( + "", + ), + Asterix( + "Parameters:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "Parameter | Type | Description", + columns: [ + "Parameter", + "Type", + "Description", + ], + }, + split: "----------|--------|------------", + rows: [ + TableRow { + raw: "`hash` | string | The hash of the torrent you want to get the generic properties of", + columns: [ + "hash", + "string", + "The hash of the torrent you want to get the generic properties of", + ], + }, + ], + }, + ), + Asterix( + "Returns:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "HTTP Status Code | Scenario", + columns: [ + "HTTP Status Code", + "Scenario", + ], + }, + split: "----------------------------------|---------------------", + rows: [ + TableRow { + raw: "404 | Torrent hash was not found", + columns: [ + "404", + "Torrent hash was not found", + ], + }, + TableRow { + raw: "200 | All other scenarios- see JSON below", + columns: [ + "200", + "All other scenarios- see JSON below", + ], + }, + ], + }, + ), + Text( + "The response is:", + ), + Text( + "", + ), + Text( + "- empty, if the torrent hash is invalid", + ), + Text( + "- otherwise, a JSON object with the following fields", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "Property | Type | Description", + columns: [ + "Property", + "Type", + "Description", + ], + }, + split: "--------------------------|---------|------------", + rows: [ + TableRow { + raw: "`save_path` | string | Torrent save path", + columns: [ + "save_path", + "string", + "Torrent save path", + ], + }, + TableRow { + raw: "`creation_date` | integer | Torrent creation date (Unix timestamp)", + columns: [ + "creation_date", + "integer", + "Torrent creation date (Unix timestamp)", + ], + }, + TableRow { + raw: "`piece_size` | integer | Torrent piece size (bytes)", + columns: [ + "piece_size", + "integer", + "Torrent piece size (bytes)", + ], + }, + TableRow { + raw: "`comment` | string | Torrent comment", + columns: [ + "comment", + "string", + "Torrent comment", + ], + }, + TableRow { + raw: "`total_wasted` | integer | Total data wasted for torrent (bytes)", + columns: [ + "total_wasted", + "integer", + "Total data wasted for torrent (bytes)", + ], + }, + TableRow { + raw: "`total_uploaded` | integer | Total data uploaded for torrent (bytes)", + columns: [ + "total_uploaded", + "integer", + "Total data uploaded for torrent (bytes)", + ], + }, + TableRow { + raw: "`total_uploaded_session` | integer | Total data uploaded this session (bytes)", + columns: [ + "total_uploaded_session", + "integer", + "Total data uploaded this session (bytes)", + ], + }, + TableRow { + raw: "`total_downloaded` | integer | Total data downloaded for torrent (bytes)", + columns: [ + "total_downloaded", + "integer", + "Total data downloaded for torrent (bytes)", + ], + }, + TableRow { + raw: "`total_downloaded_session`| integer | Total data downloaded this session (bytes)", + columns: [ + "total_downloaded_session", + "integer", + "Total data downloaded this session (bytes)", + ], + }, + TableRow { + raw: "`up_limit` | integer | Torrent upload limit (bytes/s)", + columns: [ + "up_limit", + "integer", + "Torrent upload limit (bytes/s)", + ], + }, + TableRow { + raw: "`dl_limit` | integer | Torrent download limit (bytes/s)", + columns: [ + "dl_limit", + "integer", + "Torrent download limit (bytes/s)", + ], + }, + TableRow { + raw: "`time_elapsed` | integer | Torrent elapsed time (seconds)", + columns: [ + "time_elapsed", + "integer", + "Torrent elapsed time (seconds)", + ], + }, + TableRow { + raw: "`seeding_time` | integer | Torrent elapsed time while complete (seconds)", + columns: [ + "seeding_time", + "integer", + "Torrent elapsed time while complete (seconds)", + ], + }, + TableRow { + raw: "`nb_connections` | integer | Torrent connection count", + columns: [ + "nb_connections", + "integer", + "Torrent connection count", + ], + }, + TableRow { + raw: "`nb_connections_limit` | integer | Torrent connection count limit", + columns: [ + "nb_connections_limit", + "integer", + "Torrent connection count limit", + ], + }, + TableRow { + raw: "`share_ratio` | float | Torrent share ratio", + columns: [ + "share_ratio", + "float", + "Torrent share ratio", + ], + }, + TableRow { + raw: "`addition_date` | integer | When this torrent was added (unix timestamp)", + columns: [ + "addition_date", + "integer", + "When this torrent was added (unix timestamp)", + ], + }, + TableRow { + raw: "`completion_date` | integer | Torrent completion date (unix timestamp)", + columns: [ + "completion_date", + "integer", + "Torrent completion date (unix timestamp)", + ], + }, + TableRow { + raw: "`created_by` | string | Torrent creator", + columns: [ + "created_by", + "string", + "Torrent creator", + ], + }, + TableRow { + raw: "`dl_speed_avg` | integer | Torrent average download speed (bytes/second)", + columns: [ + "dl_speed_avg", + "integer", + "Torrent average download speed (bytes/second)", + ], + }, + TableRow { + raw: "`dl_speed` | integer | Torrent download speed (bytes/second)", + columns: [ + "dl_speed", + "integer", + "Torrent download speed (bytes/second)", + ], + }, + TableRow { + raw: "`eta` | integer | Torrent ETA (seconds)", + columns: [ + "eta", + "integer", + "Torrent ETA (seconds)", + ], + }, + TableRow { + raw: "`last_seen` | integer | Last seen complete date (unix timestamp)", + columns: [ + "last_seen", + "integer", + "Last seen complete date (unix timestamp)", + ], + }, + TableRow { + raw: "`peers` | integer | Number of peers connected to", + columns: [ + "peers", + "integer", + "Number of peers connected to", + ], + }, + TableRow { + raw: "`peers_total` | integer | Number of peers in the swarm", + columns: [ + "peers_total", + "integer", + "Number of peers in the swarm", + ], + }, + TableRow { + raw: "`pieces_have` | integer | Number of pieces owned", + columns: [ + "pieces_have", + "integer", + "Number of pieces owned", + ], + }, + TableRow { + raw: "`pieces_num` | integer | Number of pieces of the torrent", + columns: [ + "pieces_num", + "integer", + "Number of pieces of the torrent", + ], + }, + TableRow { + raw: "`reannounce` | integer | Number of seconds until the next announce", + columns: [ + "reannounce", + "integer", + "Number of seconds until the next announce", + ], + }, + TableRow { + raw: "`seeds` | integer | Number of seeds connected to", + columns: [ + "seeds", + "integer", + "Number of seeds connected to", + ], + }, + TableRow { + raw: "`seeds_total` | integer | Number of seeds in the swarm", + columns: [ + "seeds_total", + "integer", + "Number of seeds in the swarm", + ], + }, + TableRow { + raw: "`total_size` | integer | Torrent total size (bytes)", + columns: [ + "total_size", + "integer", + "Torrent total size (bytes)", + ], + }, + TableRow { + raw: "`up_speed_avg` | integer | Torrent average upload speed (bytes/second)", + columns: [ + "up_speed_avg", + "integer", + "Torrent average upload speed (bytes/second)", + ], + }, + TableRow { + raw: "`up_speed` | integer | Torrent upload speed (bytes/second)", + columns: [ + "up_speed", + "integer", + "Torrent upload speed (bytes/second)", + ], + }, + ], + }, + ), + Text( + "NB: `-1` is returned if the type of the property is integer but its value is not known.", + ), + Text( + "", + ), + Text( + "Example:", + ), + Text( + "", + ), + Text( + "```JSON", + ), + Text( + "{", + ), + Text( + " \"addition_date\":1438429165,", + ), + Text( + " \"comment\":\"\\\"Debian CD from cdimage.debian.org\\\"\",", + ), + Text( + " \"completion_date\":1438429234,", + ), + Text( + " \"created_by\":\"\",", + ), + Text( + " \"creation_date\":1433605214,", + ), + Text( + " \"dl_limit\":-1,", + ), + Text( + " \"dl_speed\":0,", + ), + Text( + " \"dl_speed_avg\":9736015,", + ), + Text( + " \"eta\":8640000,", + ), + Text( + " \"last_seen\":1438430354,", + ), + Text( + " \"nb_connections\":3,", + ), + Text( + " \"nb_connections_limit\":250,", + ), + Text( + " \"peers\":1,", + ), + Text( + " \"peers_total\":89,", + ), + Text( + " \"piece_size\":524288,", + ), + Text( + " \"pieces_have\":1254,", + ), + Text( + " \"pieces_num\":1254,", + ), + Text( + " \"reannounce\":672,", + ), + Text( + " \"save_path\":\"/Downloads/debian-8.1.0-amd64-CD-1.iso\",", + ), + Text( + " \"seeding_time\":1128,", + ), + Text( + " \"seeds\":1,", + ), + Text( + " \"seeds_total\":254,", + ), + Text( + " \"share_ratio\":0.00072121022562178299,", + ), + Text( + " \"time_elapsed\":1197,", + ), + Text( + " \"total_downloaded\":681521119,", + ), + Text( + " \"total_downloaded_session\":681521119,", + ), + Text( + " \"total_size\":657457152,", + ), + Text( + " \"total_uploaded\":491520,", + ), + Text( + " \"total_uploaded_session\":491520,", + ), + Text( + " \"total_wasted\":23481724,", + ), + Text( + " \"up_limit\":-1,", + ), + Text( + " \"up_speed\":0,", + ), + Text( + " \"up_speed_avg\":410", + ), + Text( + "}", + ), + Text( + "```", + ), + Text( + "", + ), + ], + children: [], + }, + TokenTree { + title: Some( + "Get torrent trackers", + ), + content: [ + Text( + "", + ), + Text( + "Requires knowing the torrent hash. You can get it from [torrent list](#get-torrent-list).", + ), + Text( + "", + ), + Text( + "Name: `trackers`", + ), + Text( + "", + ), + Asterix( + "Parameters:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "Parameter | Type | Description", + columns: [ + "Parameter", + "Type", + "Description", + ], + }, + split: "----------|--------|------------", + rows: [ + TableRow { + raw: "`hash` | string | The hash of the torrent you want to get the trackers of", + columns: [ + "hash", + "string", + "The hash of the torrent you want to get the trackers of", + ], + }, + ], + }, + ), + Asterix( + "Returns:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "HTTP Status Code | Scenario", + columns: [ + "HTTP Status Code", + "Scenario", + ], + }, + split: "----------------------------------|---------------------", + rows: [ + TableRow { + raw: "404 | Torrent hash was not found", + columns: [ + "404", + "Torrent hash was not found", + ], + }, + TableRow { + raw: "200 | All other scenarios- see JSON below", + columns: [ + "200", + "All other scenarios- see JSON below", + ], + }, + ], + }, + ), + Text( + "The response is a JSON array, where each element contains info about one tracker, with the following fields", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "Property | Type | Description", + columns: [ + "Property", + "Type", + "Description", + ], + }, + split: "-----------------|----------|-------------", + rows: [ + TableRow { + raw: "`url` | string | Tracker url", + columns: [ + "url", + "string", + "Tracker url", + ], + }, + TableRow { + raw: "`status` | integer | Tracker status. See the table below for possible values", + columns: [ + "status", + "integer", + "Tracker status. See the table below for possible values", + ], + }, + TableRow { + raw: "`tier` | integer | Tracker priority tier. Lower tier trackers are tried before higher tiers. Tier numbers are valid when `>= 0`, `< 0` is used as placeholder when `tier` does not exist for special entries (such as DHT).", + columns: [ + "tier", + "integer", + "Tracker priority tier. Lower tier trackers are tried before higher tiers. Tier numbers are valid when >= 0, < 0 is used as placeholder when tier does not exist for special entries (such as DHT).", + ], + }, + TableRow { + raw: "`num_peers` | integer | Number of peers for current torrent, as reported by the tracker", + columns: [ + "num_peers", + "integer", + "Number of peers for current torrent, as reported by the tracker", + ], + }, + TableRow { + raw: "`num_seeds` | integer | Number of seeds for current torrent, asreported by the tracker", + columns: [ + "num_seeds", + "integer", + "Number of seeds for current torrent, asreported by the tracker", + ], + }, + TableRow { + raw: "`num_leeches` | integer | Number of leeches for current torrent, as reported by the tracker", + columns: [ + "num_leeches", + "integer", + "Number of leeches for current torrent, as reported by the tracker", + ], + }, + TableRow { + raw: "`num_downloaded` | integer | Number of completed downlods for current torrent, as reported by the tracker", + columns: [ + "num_downloaded", + "integer", + "Number of completed downlods for current torrent, as reported by the tracker", + ], + }, + TableRow { + raw: "`msg` | string | Tracker message (there is no way of knowing what this message is - it's up to tracker admins)", + columns: [ + "msg", + "string", + "Tracker message (there is no way of knowing what this message is - it's up to tracker admins)", + ], + }, + ], + }, + ), + Text( + "Possible values of `status`:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "Value | Description", + columns: [ + "Value", + "Description", + ], + }, + split: "-------|------------", + rows: [ + TableRow { + raw: "0 | Tracker is disabled (used for DHT, PeX, and LSD)", + columns: [ + "0", + "Tracker is disabled (used for DHT, PeX, and LSD)", + ], + }, + TableRow { + raw: "1 | Tracker has not been contacted yet", + columns: [ + "1", + "Tracker has not been contacted yet", + ], + }, + TableRow { + raw: "2 | Tracker has been contacted and is working", + columns: [ + "2", + "Tracker has been contacted and is working", + ], + }, + TableRow { + raw: "3 | Tracker is updating", + columns: [ + "3", + "Tracker is updating", + ], + }, + TableRow { + raw: "4 | Tracker has been contacted, but it is not working (or doesn't send proper replies)", + columns: [ + "4", + "Tracker has been contacted, but it is not working (or doesn't send proper replies)", + ], + }, + ], + }, + ), + Text( + "Example:", + ), + Text( + "", + ), + Text( + "```JSON", + ), + Text( + "[", + ), + Text( + " {", + ), + Text( + " \"msg\":\"\",", + ), + Text( + " \"num_peers\":100,", + ), + Text( + " \"status\":2,", + ), + Text( + " \"url\":\"http://bttracker.debian.org:6969/announce\"", + ), + Text( + " },", + ), + Text( + " {", + ), + Text( + " another_tracker_info", + ), + Text( + " }", + ), + Text( + "]", + ), + Text( + "```", + ), + Text( + "", + ), + ], + children: [], + }, + TokenTree { + title: Some( + "Get torrent web seeds", + ), + content: [ + Text( + "", + ), + Text( + "Requires knowing the torrent hash. You can get it from [torrent list](#get-torrent-list).", + ), + Text( + "", + ), + Text( + "Name: `webseeds`", + ), + Text( + "", + ), + Asterix( + "Parameters:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "Parameter | Type | Description", + columns: [ + "Parameter", + "Type", + "Description", + ], + }, + split: "----------|--------|------------", + rows: [ + TableRow { + raw: "`hash` | string | The hash of the torrent you want to get the webseeds of", + columns: [ + "hash", + "string", + "The hash of the torrent you want to get the webseeds of", + ], + }, + ], + }, + ), + Asterix( + "Returns:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "HTTP Status Code | Scenario", + columns: [ + "HTTP Status Code", + "Scenario", + ], + }, + split: "----------------------------------|---------------------", + rows: [ + TableRow { + raw: "404 | Torrent hash was not found", + columns: [ + "404", + "Torrent hash was not found", + ], + }, + TableRow { + raw: "200 | All other scenarios- see JSON below", + columns: [ + "200", + "All other scenarios- see JSON below", + ], + }, + ], + }, + ), + Text( + "The response is a JSON array, where each element is information about one webseed, with the following fields", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "Property | Type | Description", + columns: [ + "Property", + "Type", + "Description", + ], + }, + split: "--------------|----------|------------", + rows: [ + TableRow { + raw: "`url` | string | URL of the web seed", + columns: [ + "url", + "string", + "URL of the web seed", + ], + }, + ], + }, + ), + Text( + "Example:", + ), + Text( + "", + ), + Text( + "```JSON", + ), + Text( + "[", + ), + Text( + " {", + ), + Text( + " \"url\":\"http://some_url/\"", + ), + Text( + " },", + ), + Text( + " {", + ), + Text( + " \"url\":\"http://some_other_url/\"", + ), + Text( + " }", + ), + Text( + "]", + ), + Text( + "```", + ), + Text( + "", + ), + ], + children: [], + }, + TokenTree { + title: Some( + "Get torrent contents", + ), + content: [ + Text( + "", + ), + Text( + "Requires knowing the torrent hash. You can get it from [torrent list](#get-torrent-list).", + ), + Text( + "", + ), + Text( + "Name: `files`", + ), + Text( + "", + ), + Asterix( + "Parameters:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "Parameter | Type | Description", + columns: [ + "Parameter", + "Type", + "Description", + ], + }, + split: "----------|--------|------------", + rows: [ + TableRow { + raw: "`hash` | string | The hash of the torrent you want to get the contents of", + columns: [ + "hash", + "string", + "The hash of the torrent you want to get the contents of", + ], + }, + TableRow { + raw: "`indexes` _optional_ since 2.8.2 | string | The indexes of the files you want to retrieve. `indexes` can contain multiple values separated by `\\|`.", + columns: [ + "indexes _optional_ since 2.8.2", + "string", + "The indexes of the files you want to retrieve. indexes can contain multiple values separated by \\", + ".", + ], + }, + ], + }, + ), + Asterix( + "Returns:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "HTTP Status Code | Scenario", + columns: [ + "HTTP Status Code", + "Scenario", + ], + }, + split: "----------------------------------|---------------------", + rows: [ + TableRow { + raw: "404 | Torrent hash was not found", + columns: [ + "404", + "Torrent hash was not found", + ], + }, + TableRow { + raw: "200 | All other scenarios- see JSON below", + columns: [ + "200", + "All other scenarios- see JSON below", + ], + }, + ], + }, + ), + Text( + "The response is:", + ), + Text( + "", + ), + Text( + "- empty, if the torrent hash is invalid", + ), + Text( + "- otherwise, a JSON array, where each element contains info about one file, with the following fields", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "Property | Type | Description", + columns: [ + "Property", + "Type", + "Description", + ], + }, + split: "---------------|---------------|-------------", + rows: [ + TableRow { + raw: "`index` since 2.8.2 | integer | File index", + columns: [ + "index since 2.8.2", + "integer", + "File index", + ], + }, + TableRow { + raw: "`name` | string | File name (including relative path)", + columns: [ + "name", + "string", + "File name (including relative path)", + ], + }, + TableRow { + raw: "`size` | integer | File size (bytes)", + columns: [ + "size", + "integer", + "File size (bytes)", + ], + }, + TableRow { + raw: "`progress` | float | File progress (percentage/100)", + columns: [ + "progress", + "float", + "File progress (percentage/100)", + ], + }, + TableRow { + raw: "`priority` | integer | File priority. See possible values here below", + columns: [ + "priority", + "integer", + "File priority. See possible values here below", + ], + }, + TableRow { + raw: "`is_seed` | bool | True if file is seeding/complete", + columns: [ + "is_seed", + "bool", + "True if file is seeding/complete", + ], + }, + TableRow { + raw: "`piece_range` | integer array | The first number is the starting piece index and the second number is the ending piece index (inclusive)", + columns: [ + "piece_range", + "integer array", + "The first number is the starting piece index and the second number is the ending piece index (inclusive)", + ], + }, + TableRow { + raw: "`availability` | float | Percentage of file pieces currently available (percentage/100)", + columns: [ + "availability", + "float", + "Percentage of file pieces currently available (percentage/100)", + ], + }, + ], + }, + ), + Text( + "Possible values of `priority`:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "Value | Description", + columns: [ + "Value", + "Description", + ], + }, + split: "-----------|------------", + rows: [ + TableRow { + raw: "`0` | Do not download", + columns: [ + "0", + "Do not download", + ], + }, + TableRow { + raw: "`1` | Normal priority", + columns: [ + "1", + "Normal priority", + ], + }, + TableRow { + raw: "`6` | High priority", + columns: [ + "6", + "High priority", + ], + }, + TableRow { + raw: "`7` | Maximal priority", + columns: [ + "7", + "Maximal priority", + ], + }, + ], + }, + ), + Text( + "Example:", + ), + Text( + "", + ), + Text( + "```JSON", + ), + Text( + "", + ), + Text( + "[", + ), + Text( + " {", + ), + Text( + " \"index\":0,", + ), + Text( + " \"is_seed\":false,", + ), + Text( + " \"name\":\"debian-8.1.0-amd64-CD-1.iso\",", + ), + Text( + " \"piece_range\":[0,1253],", + ), + Text( + " \"priority\":1,", + ), + Text( + " \"progress\":0,", + ), + Text( + " \"size\":657457152,", + ), + Text( + " \"availability\":0.5,", + ), + Text( + " }", + ), + Text( + "]", + ), + Text( + "```", + ), + Text( + "", + ), + ], + children: [], + }, + TokenTree { + title: Some( + "Get torrent pieces' states", + ), + content: [ + Text( + "", + ), + Text( + "Requires knowing the torrent hash. You can get it from [torrent list](#get-torrent-list).", + ), + Text( + "", + ), + Text( + "Name: `pieceStates`", + ), + Text( + "", + ), + Asterix( + "Parameters:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "Parameter | Type | Description", + columns: [ + "Parameter", + "Type", + "Description", + ], + }, + split: "----------|--------|------------", + rows: [ + TableRow { + raw: "`hash` | string | The hash of the torrent you want to get the pieces' states of", + columns: [ + "hash", + "string", + "The hash of the torrent you want to get the pieces' states of", + ], + }, + ], + }, + ), + Asterix( + "Returns:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "HTTP Status Code | Scenario", + columns: [ + "HTTP Status Code", + "Scenario", + ], + }, + split: "----------------------------------|---------------------", + rows: [ + TableRow { + raw: "404 | Torrent hash was not found", + columns: [ + "404", + "Torrent hash was not found", + ], + }, + TableRow { + raw: "200 | All other scenarios- see JSON below", + columns: [ + "200", + "All other scenarios- see JSON below", + ], + }, + ], + }, + ), + Text( + "The response is:", + ), + Text( + "", + ), + Text( + "- empty, if the torrent hash is invalid", + ), + Text( + "- otherwise, an array of states (integers) of all pieces (in order) of a specific torrent.", + ), + Text( + "", + ), + Text( + "Value meanings are defined as below:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "Value | Description", + columns: [ + "Value", + "Description", + ], + }, + split: "-----------|------------", + rows: [ + TableRow { + raw: "`0` | Not downloaded yet", + columns: [ + "0", + "Not downloaded yet", + ], + }, + TableRow { + raw: "`1` | Now downloading", + columns: [ + "1", + "Now downloading", + ], + }, + TableRow { + raw: "`2` | Already downloaded", + columns: [ + "2", + "Already downloaded", + ], + }, + ], + }, + ), + Text( + "Example:", + ), + Text( + "", + ), + Text( + "```JSON", + ), + Text( + "[0,0,2,1,0,0,2,1]", + ), + Text( + "```", + ), + Text( + "", + ), + ], + children: [], + }, + TokenTree { + title: Some( + "Get torrent pieces' hashes", + ), + content: [ + Text( + "", + ), + Text( + "Requires knowing the torrent hash. You can get it from [torrent list](#get-torrent-list).", + ), + Text( + "", + ), + Text( + "Name: `pieceHashes`", + ), + Text( + "", + ), + Asterix( + "Parameters:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "Parameter | Type | Description", + columns: [ + "Parameter", + "Type", + "Description", + ], + }, + split: "----------|--------|------------", + rows: [ + TableRow { + raw: "`hash` | string | The hash of the torrent you want to get the pieces' hashes of", + columns: [ + "hash", + "string", + "The hash of the torrent you want to get the pieces' hashes of", + ], + }, + ], + }, + ), + Asterix( + "Returns:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "HTTP Status Code | Scenario", + columns: [ + "HTTP Status Code", + "Scenario", + ], + }, + split: "----------------------------------|---------------------", + rows: [ + TableRow { + raw: "404 | Torrent hash was not found", + columns: [ + "404", + "Torrent hash was not found", + ], + }, + TableRow { + raw: "200 | All other scenarios- see JSON below", + columns: [ + "200", + "All other scenarios- see JSON below", + ], + }, + ], + }, + ), + Text( + "The response is:", + ), + Text( + "", + ), + Text( + "- empty, if the torrent hash is invalid", + ), + Text( + "- otherwise, an array of hashes (strings) of all pieces (in order) of a specific torrent.", + ), + Text( + "", + ), + Text( + "Example:", + ), + Text( + "", + ), + Text( + "```JSON", + ), + Text( + "[\"54eddd830a5b58480a6143d616a97e3a6c23c439\",\"f8a99d225aa4241db100f88407fc3bdaead583ab\",\"928fb615b9bd4dd8f9e9022552c8f8f37ef76f58\"]", + ), + Text( + "```", + ), + Text( + "", + ), + ], + children: [], + }, + TokenTree { + title: Some( + "Pause torrents", + ), + content: [ + Text( + "", + ), + Text( + "Requires knowing the torrent hashes. You can get it from [torrent list](#get-torrent-list).", + ), + Text( + "", + ), + Text( + "Name: `pause`", + ), + Text( + "", + ), + Asterix( + "Parameters:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "Parameter | Type | Description", + columns: [ + "Parameter", + "Type", + "Description", + ], + }, + split: "------------|----------|------------", + rows: [ + TableRow { + raw: "`hashes` | string | The hashes of the torrents you want to pause. `hashes` can contain multiple hashes separated by `\\|`, to pause multiple torrents, or set to `all`, to pause all torrents.", + columns: [ + "hashes", + "string", + "The hashes of the torrents you want to pause. hashes can contain multiple hashes separated by \\", + ", to pause multiple torrents, or set to all, to pause all torrents.", + ], + }, + ], + }, + ), + Text( + "Example:", + ), + Text( + "", + ), + Text( + "```http", + ), + Table( + Table { + header: TableRow { + raw: "/api/v2/torrents/pause?hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32|54eddd830a5b58480a6143d616a97e3a6c23c439", + columns: [ + "/api/v2/torrents/pause?hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32", + "54eddd830a5b58480a6143d616a97e3a6c23c439", + ], + }, + split: "```", + rows: [], + }, + ), + Asterix( + "Returns:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "HTTP Status Code | Scenario", + columns: [ + "HTTP Status Code", + "Scenario", + ], + }, + split: "----------------------------------|---------------------", + rows: [ + TableRow { + raw: "200 | All scenarios", + columns: [ + "200", + "All scenarios", + ], + }, + ], + }, + ), + ], + children: [], + }, + TokenTree { + title: Some( + "Resume torrents", + ), + content: [ + Text( + "", + ), + Text( + "Requires knowing the torrent hashes. You can get it from [torrent list](#get-torrent-list).", + ), + Text( + "", + ), + Text( + "Name: `resume`", + ), + Text( + "", + ), + Asterix( + "Parameters:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "Parameter | Type | Description", + columns: [ + "Parameter", + "Type", + "Description", + ], + }, + split: "------------|----------|------------", + rows: [ + TableRow { + raw: "`hashes` | string | The hashes of the torrents you want to resume. `hashes` can contain multiple hashes separated by `\\|`, to resume multiple torrents, or set to `all`, to resume all torrents.", + columns: [ + "hashes", + "string", + "The hashes of the torrents you want to resume. hashes can contain multiple hashes separated by \\", + ", to resume multiple torrents, or set to all, to resume all torrents.", + ], + }, + ], + }, + ), + Text( + "Example:", + ), + Text( + "", + ), + Text( + "```http", + ), + Table( + Table { + header: TableRow { + raw: "/api/v2/torrents/resume?hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32|54eddd830a5b58480a6143d616a97e3a6c23c439", + columns: [ + "/api/v2/torrents/resume?hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32", + "54eddd830a5b58480a6143d616a97e3a6c23c439", + ], + }, + split: "```", + rows: [], + }, + ), + Asterix( + "Returns:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "HTTP Status Code | Scenario", + columns: [ + "HTTP Status Code", + "Scenario", + ], + }, + split: "----------------------------------|---------------------", + rows: [ + TableRow { + raw: "200 | All scenarios", + columns: [ + "200", + "All scenarios", + ], + }, + ], + }, + ), + ], + children: [], + }, + TokenTree { + title: Some( + "Delete torrents", + ), + content: [ + Text( + "", + ), + Text( + "Requires knowing the torrent hashes. You can get it from [torrent list](#get-torrent-list).", + ), + Text( + "", + ), + Text( + "Name: `delete`", + ), + Text( + "", + ), + Asterix( + "Parameters:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "Parameter | Type | Description", + columns: [ + "Parameter", + "Type", + "Description", + ], + }, + split: "------------|----------|------------", + rows: [ + TableRow { + raw: "`hashes` | string | The hashes of the torrents you want to delete. `hashes` can contain multiple hashes separated by `\\|`, to delete multiple torrents, or set to `all`, to delete all torrents.", + columns: [ + "hashes", + "string", + "The hashes of the torrents you want to delete. hashes can contain multiple hashes separated by \\", + ", to delete multiple torrents, or set to all, to delete all torrents.", + ], + }, + TableRow { + raw: "`deleteFiles` | If set to `true`, the downloaded data will also be deleted, otherwise has no effect.", + columns: [ + "deleteFiles", + "If set to true, the downloaded data will also be deleted, otherwise has no effect.", + ], + }, + ], + }, + ), + Text( + "Example:", + ), + Text( + "", + ), + Text( + "```http", + ), + Text( + "/api/v2/torrents/delete?hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32&deleteFiles=false", + ), + Text( + "```", + ), + Text( + "", + ), + Asterix( + "Returns:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "HTTP Status Code | Scenario", + columns: [ + "HTTP Status Code", + "Scenario", + ], + }, + split: "----------------------------------|---------------------", + rows: [ + TableRow { + raw: "200 | All scenarios", + columns: [ + "200", + "All scenarios", + ], + }, + ], + }, + ), + ], + children: [], + }, + TokenTree { + title: Some( + "Recheck torrents", + ), + content: [ + Text( + "", + ), + Text( + "Requires knowing the torrent hashes. You can get it from [torrent list](#get-torrent-list).", + ), + Text( + "", + ), + Text( + "Name: `recheck`", + ), + Text( + "", + ), + Asterix( + "Parameters:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "Parameter | Type | Description", + columns: [ + "Parameter", + "Type", + "Description", + ], + }, + split: "------------|----------|------------", + rows: [ + TableRow { + raw: "`hashes` | string | The hashes of the torrents you want to recheck. `hashes` can contain multiple hashes separated by `\\|`, to recheck multiple torrents, or set to `all`, to recheck all torrents.", + columns: [ + "hashes", + "string", + "The hashes of the torrents you want to recheck. hashes can contain multiple hashes separated by \\", + ", to recheck multiple torrents, or set to all, to recheck all torrents.", + ], + }, + ], + }, + ), + Text( + "Example:", + ), + Text( + "", + ), + Text( + "```http", + ), + Table( + Table { + header: TableRow { + raw: "/api/v2/torrents/recheck?hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32|54eddd830a5b58480a6143d616a97e3a6c23c439", + columns: [ + "/api/v2/torrents/recheck?hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32", + "54eddd830a5b58480a6143d616a97e3a6c23c439", + ], + }, + split: "```", + rows: [], + }, + ), + Asterix( + "Returns:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "HTTP Status Code | Scenario", + columns: [ + "HTTP Status Code", + "Scenario", + ], + }, + split: "----------------------------------|---------------------", + rows: [ + TableRow { + raw: "200 | All scenarios", + columns: [ + "200", + "All scenarios", + ], + }, + ], + }, + ), + ], + children: [], + }, + TokenTree { + title: Some( + "Reannounce torrents", + ), + content: [ + Text( + "", + ), + Text( + "Requires knowing the torrent hashes. You can get it from [torrent list](#get-torrent-list).", + ), + Text( + "", + ), + Text( + "Name: `reannounce`", + ), + Text( + "", + ), + Asterix( + "Parameters:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "Parameter | Type | Description", + columns: [ + "Parameter", + "Type", + "Description", + ], + }, + split: "------------|----------|------------", + rows: [ + TableRow { + raw: "`hashes` | string | The hashes of the torrents you want to reannounce. `hashes` can contain multiple hashes separated by `\\|`, to reannounce multiple torrents, or set to `all`, to reannounce all torrents.", + columns: [ + "hashes", + "string", + "The hashes of the torrents you want to reannounce. hashes can contain multiple hashes separated by \\", + ", to reannounce multiple torrents, or set to all, to reannounce all torrents.", + ], + }, + ], + }, + ), + Text( + "Example:", + ), + Text( + "", + ), + Text( + "```http", + ), + Table( + Table { + header: TableRow { + raw: "/api/v2/torrents/reannounce?hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32|54eddd830a5b58480a6143d616a97e3a6c23c439", + columns: [ + "/api/v2/torrents/reannounce?hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32", + "54eddd830a5b58480a6143d616a97e3a6c23c439", + ], + }, + split: "```", + rows: [], + }, + ), + Asterix( + "Returns:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "HTTP Status Code | Scenario", + columns: [ + "HTTP Status Code", + "Scenario", + ], + }, + split: "----------------------------------|---------------------", + rows: [ + TableRow { + raw: "200 | All scenarios", + columns: [ + "200", + "All scenarios", + ], + }, + ], + }, + ), + ], + children: [], + }, + TokenTree { + title: Some( + "Add new torrent", + ), + content: [ + Text( + "", + ), + Text( + "This method can add torrents from server local file or from URLs. `http://`, `https://`, `magnet:` and `bc://bt/` links are supported.", + ), + Text( + "", + ), + Text( + "Name: `add`", + ), + Text( + "", + ), + Text( + "Add torrent from URLs example:", + ), + Text( + "", + ), + Text( + "```http", + ), + Text( + "POST /api/v2/torrents/add HTTP/1.1", + ), + Text( + "User-Agent: Fiddler", + ), + Text( + "Host: 127.0.0.1", + ), + Text( + "Cookie: SID=your_sid", + ), + Text( + "Content-Type: multipart/form-data; boundary=---------------------------6688794727912", + ), + Text( + "Content-Length: length", + ), + Text( + "", + ), + Text( + "-----------------------------6688794727912", + ), + Text( + "Content-Disposition: form-data; name=\"urls\"", + ), + Text( + "", + ), + Text( + "https://torcache.net/torrent/3B1A1469C180F447B77021074DBBCCAEF62611E7.torrent", + ), + Text( + "https://torcache.net/torrent/3B1A1469C180F447B77021074DBBCCAEF62611E8.torrent", + ), + Text( + "-----------------------------6688794727912", + ), + Text( + "Content-Disposition: form-data; name=\"savepath\"", + ), + Text( + "", + ), + Text( + "C:/Users/qBit/Downloads", + ), + Text( + "-----------------------------6688794727912", + ), + Text( + "Content-Disposition: form-data; name=\"cookie\"", + ), + Text( + "", + ), + Text( + "ui=28979218048197", + ), + Text( + "-----------------------------6688794727912", + ), + Text( + "Content-Disposition: form-data; name=\"category\"", + ), + Text( + "", + ), + Text( + "movies", + ), + Text( + "-----------------------------6688794727912", + ), + Text( + "Content-Disposition: form-data; name=\"skip_checking\"", + ), + Text( + "", + ), + Text( + "true", + ), + Text( + "-----------------------------6688794727912", + ), + Text( + "Content-Disposition: form-data; name=\"paused\"", + ), + Text( + "", + ), + Text( + "true", + ), + Text( + "-----------------------------6688794727912", + ), + Text( + "Content-Disposition: form-data; name=\"root_folder\"", + ), + Text( + "", + ), + Text( + "true", + ), + Text( + "-----------------------------6688794727912--", + ), + Text( + "```", + ), + Text( + "", + ), + Text( + "Add torrents from files example:", + ), + Text( + "", + ), + Text( + "```http", + ), + Text( + "POST /api/v2/torrents/add HTTP/1.1", + ), + Text( + "Content-Type: multipart/form-data; boundary=-------------------------acebdf13572468", + ), + Text( + "User-Agent: Fiddler", + ), + Text( + "Host: 127.0.0.1", + ), + Text( + "Cookie: SID=your_sid", + ), + Text( + "Content-Length: length", + ), + Text( + "", + ), + Text( + "---------------------------acebdf13572468", + ), + Text( + "Content-Disposition: form-data; name=\"torrents\"; filename=\"8f18036b7a205c9347cb84a253975e12f7adddf2.torrent\"", + ), + Text( + "Content-Type: application/x-bittorrent", + ), + Text( + "", + ), + Text( + "file_binary_data_goes_here", + ), + Text( + "---------------------------acebdf13572468", + ), + Text( + "Content-Disposition: form-data; name=\"torrents\"; filename=\"UFS.torrent\"", + ), + Text( + "Content-Type: application/x-bittorrent", + ), + Text( + "", + ), + Text( + "file_binary_data_goes_here", + ), + Text( + "---------------------------acebdf13572468--", + ), + Text( + "", + ), + Text( + "```", + ), + Text( + "", + ), + Text( + "The above example will add two torrent files. `file_binary_data_goes_here` represents raw data of torrent file (basically a byte array).", + ), + Text( + "", + ), + Asterix( + "Parameters:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "Property | Type | Description", + columns: [ + "Property", + "Type", + "Description", + ], + }, + split: "--------------------------------|---------|------------", + rows: [ + TableRow { + raw: "`urls` | string | URLs separated with newlines", + columns: [ + "urls", + "string", + "URLs separated with newlines", + ], + }, + TableRow { + raw: "`torrents` | raw | Raw data of torrent file. `torrents` can be presented multiple times.", + columns: [ + "torrents", + "raw", + "Raw data of torrent file. torrents can be presented multiple times.", + ], + }, + TableRow { + raw: "`savepath` _optional_ | string | Download folder", + columns: [ + "savepath _optional_", + "string", + "Download folder", + ], + }, + TableRow { + raw: "`cookie` _optional_ | string | Cookie sent to download the .torrent file", + columns: [ + "cookie _optional_", + "string", + "Cookie sent to download the .torrent file", + ], + }, + TableRow { + raw: "`category` _optional_ | string | Category for the torrent", + columns: [ + "category _optional_", + "string", + "Category for the torrent", + ], + }, + TableRow { + raw: "`tags` _optional_ | string | Tags for the torrent, split by ','", + columns: [ + "tags _optional_", + "string", + "Tags for the torrent, split by ','", + ], + }, + TableRow { + raw: "`skip_checking` _optional_ | string | Skip hash checking. Possible values are `true`, `false` (default)", + columns: [ + "skip_checking _optional_", + "string", + "Skip hash checking. Possible values are true, false (default)", + ], + }, + TableRow { + raw: "`paused` _optional_ | string | Add torrents in the paused state. Possible values are `true`, `false` (default)", + columns: [ + "paused _optional_", + "string", + "Add torrents in the paused state. Possible values are true, false (default)", + ], + }, + TableRow { + raw: "`root_folder` _optional_ | string | Create the root folder. Possible values are `true`, `false`, unset (default)", + columns: [ + "root_folder _optional_", + "string", + "Create the root folder. Possible values are true, false, unset (default)", + ], + }, + TableRow { + raw: "`rename` _optional_ | string | Rename torrent", + columns: [ + "rename _optional_", + "string", + "Rename torrent", + ], + }, + TableRow { + raw: "`upLimit` _optional_ | integer | Set torrent upload speed limit. Unit in bytes/second", + columns: [ + "upLimit _optional_", + "integer", + "Set torrent upload speed limit. Unit in bytes/second", + ], + }, + TableRow { + raw: "`dlLimit` _optional_ | integer | Set torrent download speed limit. Unit in bytes/second", + columns: [ + "dlLimit _optional_", + "integer", + "Set torrent download speed limit. Unit in bytes/second", + ], + }, + TableRow { + raw: "`ratioLimit` _optional_ since 2.8.1 | float | Set torrent share ratio limit", + columns: [ + "ratioLimit _optional_ since 2.8.1", + "float", + "Set torrent share ratio limit", + ], + }, + TableRow { + raw: "`seedingTimeLimit` _optional_ since 2.8.1 | integer | Set torrent seeding time limit. Unit in seconds", + columns: [ + "seedingTimeLimit _optional_ since 2.8.1", + "integer", + "Set torrent seeding time limit. Unit in seconds", + ], + }, + TableRow { + raw: "`autoTMM` _optional_ | bool | Whether Automatic Torrent Management should be used", + columns: [ + "autoTMM _optional_", + "bool", + "Whether Automatic Torrent Management should be used", + ], + }, + TableRow { + raw: "`sequentialDownload` _optional_ | string | Enable sequential download. Possible values are `true`, `false` (default)", + columns: [ + "sequentialDownload _optional_", + "string", + "Enable sequential download. Possible values are true, false (default)", + ], + }, + TableRow { + raw: "`firstLastPiecePrio` _optional_ | string | Prioritize download first last piece. Possible values are `true`, `false` (default)", + columns: [ + "firstLastPiecePrio _optional_", + "string", + "Prioritize download first last piece. Possible values are true, false (default)", + ], + }, + ], + }, + ), + Asterix( + "Returns:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "HTTP Status Code | Scenario", + columns: [ + "HTTP Status Code", + "Scenario", + ], + }, + split: "----------------------------------|---------------------", + rows: [ + TableRow { + raw: "415 | Torrent file is not valid", + columns: [ + "415", + "Torrent file is not valid", + ], + }, + TableRow { + raw: "200 | All other scenarios", + columns: [ + "200", + "All other scenarios", + ], + }, + ], + }, + ), + ], + children: [], + }, + TokenTree { + title: Some( + "Add trackers to torrent", + ), + content: [ + Text( + "", + ), + Text( + "Requires knowing the torrent hash. You can get it from [torrent list](#get-torrent-list).", + ), + Text( + "", + ), + Text( + "Name: `addTrackers`", + ), + Text( + "", + ), + Text( + "```http", + ), + Text( + "POST /api/v2/torrents/addTrackers HTTP/1.1", + ), + Text( + "User-Agent: Fiddler", + ), + Text( + "Host: 127.0.0.1", + ), + Text( + "Cookie: SID=your_sid", + ), + Text( + "Content-Type: application/x-www-form-urlencoded", + ), + Text( + "Content-Length: length", + ), + Text( + "", + ), + Text( + "hash=8c212779b4abde7c6bc608063a0d008b7e40ce32&urls=http://192.168.0.1/announce%0Audp://192.168.0.1:3333/dummyAnnounce", + ), + Text( + "```", + ), + Text( + "", + ), + Text( + "This adds two trackers to torrent with hash `8c212779b4abde7c6bc608063a0d008b7e40ce32`. Note `%0A` (aka LF newline) between trackers. Ampersand in tracker urls **MUST** be escaped.", + ), + Text( + "", + ), + Asterix( + "Returns:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "HTTP Status Code | Scenario", + columns: [ + "HTTP Status Code", + "Scenario", + ], + }, + split: "----------------------------------|---------------------", + rows: [ + TableRow { + raw: "404 | Torrent hash was not found", + columns: [ + "404", + "Torrent hash was not found", + ], + }, + TableRow { + raw: "200 | All other scenarios", + columns: [ + "200", + "All other scenarios", + ], + }, + ], + }, + ), + ], + children: [], + }, + TokenTree { + title: Some( + "Edit trackers", + ), + content: [ + Text( + "", + ), + Text( + "Name: `editTracker`", + ), + Text( + "", + ), + Asterix( + "Parameters:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "Parameter | Type | Description", + columns: [ + "Parameter", + "Type", + "Description", + ], + }, + split: "----------------------------------|---------|------------", + rows: [ + TableRow { + raw: "`hash` | string | The hash of the torrent", + columns: [ + "hash", + "string", + "The hash of the torrent", + ], + }, + TableRow { + raw: "`origUrl` | string | The tracker URL you want to edit", + columns: [ + "origUrl", + "string", + "The tracker URL you want to edit", + ], + }, + TableRow { + raw: "`newUrl` | string | The new URL to replace the `origUrl`", + columns: [ + "newUrl", + "string", + "The new URL to replace the origUrl", + ], + }, + ], + }, + ), + Asterix( + "Returns:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "HTTP Status Code | Scenario", + columns: [ + "HTTP Status Code", + "Scenario", + ], + }, + split: "----------------------------------|---------------------", + rows: [ + TableRow { + raw: "400 | `newUrl` is not a valid URL", + columns: [ + "400", + "newUrl is not a valid URL", + ], + }, + TableRow { + raw: "404 | Torrent hash was not found", + columns: [ + "404", + "Torrent hash was not found", + ], + }, + TableRow { + raw: "409 | `newUrl` already exists for the torrent", + columns: [ + "409", + "newUrl already exists for the torrent", + ], + }, + TableRow { + raw: "409 | `origUrl` was not found", + columns: [ + "409", + "origUrl was not found", + ], + }, + TableRow { + raw: "200 | All other scenarios", + columns: [ + "200", + "All other scenarios", + ], + }, + ], + }, + ), + ], + children: [], + }, + TokenTree { + title: Some( + "Remove trackers", + ), + content: [ + Text( + "", + ), + Text( + "Name: `removeTrackers`", + ), + Text( + "", + ), + Asterix( + "Parameters:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "Parameter | Type | Description", + columns: [ + "Parameter", + "Type", + "Description", + ], + }, + split: "----------------------------------|---------|------------", + rows: [ + TableRow { + raw: "`hash` | string | The hash of the torrent", + columns: [ + "hash", + "string", + "The hash of the torrent", + ], + }, + TableRow { + raw: "`urls` | string | URLs to remove, separated by `\\|`", + columns: [ + "urls", + "string", + "URLs to remove, separated by \\", + "", + ], + }, + ], + }, + ), + Asterix( + "Returns:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "HTTP Status Code | Scenario", + columns: [ + "HTTP Status Code", + "Scenario", + ], + }, + split: "----------------------------------|---------------------", + rows: [ + TableRow { + raw: "404 | Torrent hash was not found", + columns: [ + "404", + "Torrent hash was not found", + ], + }, + TableRow { + raw: "409 | All `urls` were not found", + columns: [ + "409", + "All urls were not found", + ], + }, + TableRow { + raw: "200 | All other scenarios", + columns: [ + "200", + "All other scenarios", + ], + }, + ], + }, + ), + ], + children: [], + }, + TokenTree { + title: Some( + "Add peers", + ), + content: [ + Text( + "", + ), + Text( + "Name: `addPeers`", + ), + Text( + "", + ), + Asterix( + "Parameters:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "Parameter | Type | Description", + columns: [ + "Parameter", + "Type", + "Description", + ], + }, + split: "----------------------------------|---------|------------", + rows: [ + TableRow { + raw: "`hashes` | string | The hash of the torrent, or multiple hashes separated by a pipe `\\|`", + columns: [ + "hashes", + "string", + "The hash of the torrent, or multiple hashes separated by a pipe \\", + "", + ], + }, + TableRow { + raw: "`peers` | string | The peer to add, or multiple peers separated by a pipe `\\|`. Each peer is a colon-separated `host:port`", + columns: [ + "peers", + "string", + "The peer to add, or multiple peers separated by a pipe \\", + ". Each peer is a colon-separated host:port", + ], + }, + ], + }, + ), + Asterix( + "Returns:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "HTTP Status Code | Scenario", + columns: [ + "HTTP Status Code", + "Scenario", + ], + }, + split: "----------------------------------|---------------------", + rows: [ + TableRow { + raw: "400 | None of the supplied peers are valid", + columns: [ + "400", + "None of the supplied peers are valid", + ], + }, + TableRow { + raw: "200 | All other scenarios", + columns: [ + "200", + "All other scenarios", + ], + }, + ], + }, + ), + ], + children: [], + }, + TokenTree { + title: Some( + "Increase torrent priority", + ), + content: [ + Text( + "", + ), + Text( + "Requires knowing the torrent hash. You can get it from [torrent list](#get-torrent-list).", + ), + Text( + "", + ), + Text( + "Name: `increasePrio`", + ), + Text( + "", + ), + Asterix( + "Parameters:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "Parameter | Type | Description", + columns: [ + "Parameter", + "Type", + "Description", + ], + }, + split: "------------|----------|------------", + rows: [ + TableRow { + raw: "`hashes` | string | The hashes of the torrents you want to increase the priority of. `hashes` can contain multiple hashes separated by `\\|`, to increase the priority of multiple torrents, or set to `all`, to increase the priority of all torrents.", + columns: [ + "hashes", + "string", + "The hashes of the torrents you want to increase the priority of. hashes can contain multiple hashes separated by \\", + ", to increase the priority of multiple torrents, or set to all, to increase the priority of all torrents.", + ], + }, + ], + }, + ), + Text( + "Example:", + ), + Text( + "", + ), + Text( + "```http", + ), + Table( + Table { + header: TableRow { + raw: "/api/v2/torrents/increasePrio?hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32|54eddd830a5b58480a6143d616a97e3a6c23c439", + columns: [ + "/api/v2/torrents/increasePrio?hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32", + "54eddd830a5b58480a6143d616a97e3a6c23c439", + ], + }, + split: "```", + rows: [], + }, + ), + Asterix( + "Returns:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "HTTP Status Code | Scenario", + columns: [ + "HTTP Status Code", + "Scenario", + ], + }, + split: "----------------------------------|---------------------", + rows: [ + TableRow { + raw: "409 | Torrent queueing is not enabled", + columns: [ + "409", + "Torrent queueing is not enabled", + ], + }, + TableRow { + raw: "200 | All other scenarios", + columns: [ + "200", + "All other scenarios", + ], + }, + ], + }, + ), + ], + children: [], + }, + TokenTree { + title: Some( + "Decrease torrent priority", + ), + content: [ + Text( + "", + ), + Text( + "Requires knowing the torrent hash. You can get it from [torrent list](#get-torrent-list).", + ), + Text( + "", + ), + Text( + "Name: `decreasePrio`", + ), + Text( + "", + ), + Asterix( + "Parameters:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "Parameter | Type | Description", + columns: [ + "Parameter", + "Type", + "Description", + ], + }, + split: "------------|----------|------------", + rows: [ + TableRow { + raw: "`hashes` | string | The hashes of the torrents you want to decrease the priority of. `hashes` can contain multiple hashes separated by `\\|`, to decrease the priority of multiple torrents, or set to `all`, to decrease the priority of all torrents.", + columns: [ + "hashes", + "string", + "The hashes of the torrents you want to decrease the priority of. hashes can contain multiple hashes separated by \\", + ", to decrease the priority of multiple torrents, or set to all, to decrease the priority of all torrents.", + ], + }, + ], + }, + ), + Text( + "Example:", + ), + Text( + "", + ), + Text( + "```http", + ), + Table( + Table { + header: TableRow { + raw: "/api/v2/torrents/decreasePrio?hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32|54eddd830a5b58480a6143d616a97e3a6c23c439", + columns: [ + "/api/v2/torrents/decreasePrio?hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32", + "54eddd830a5b58480a6143d616a97e3a6c23c439", + ], + }, + split: "```", + rows: [], + }, + ), + Asterix( + "Returns:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "HTTP Status Code | Scenario", + columns: [ + "HTTP Status Code", + "Scenario", + ], + }, + split: "----------------------------------|---------------------", + rows: [ + TableRow { + raw: "409 | Torrent queueing is not enabled", + columns: [ + "409", + "Torrent queueing is not enabled", + ], + }, + TableRow { + raw: "200 | All other scenarios", + columns: [ + "200", + "All other scenarios", + ], + }, + ], + }, + ), + ], + children: [], + }, + TokenTree { + title: Some( + "Maximal torrent priority", + ), + content: [ + Text( + "", + ), + Text( + "Requires knowing the torrent hash. You can get it from [torrent list](#get-torrent-list).", + ), + Text( + "", + ), + Text( + "Name: `topPrio`", + ), + Text( + "", + ), + Asterix( + "Parameters:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "Parameter | Type | Description", + columns: [ + "Parameter", + "Type", + "Description", + ], + }, + split: "------------|----------|------------", + rows: [ + TableRow { + raw: "`hashes` | string | The hashes of the torrents you want to set to the maximum priority. `hashes` can contain multiple hashes separated by `\\|`, to set multiple torrents to the maximum priority, or set to `all`, to set all torrents to the maximum priority.", + columns: [ + "hashes", + "string", + "The hashes of the torrents you want to set to the maximum priority. hashes can contain multiple hashes separated by \\", + ", to set multiple torrents to the maximum priority, or set to all, to set all torrents to the maximum priority.", + ], + }, + ], + }, + ), + Text( + "Example:", + ), + Text( + "", + ), + Text( + "```http", + ), + Table( + Table { + header: TableRow { + raw: "/api/v2/torrents/topPrio?hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32|54eddd830a5b58480a6143d616a97e3a6c23c439", + columns: [ + "/api/v2/torrents/topPrio?hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32", + "54eddd830a5b58480a6143d616a97e3a6c23c439", + ], + }, + split: "```", + rows: [], + }, + ), + Asterix( + "Returns:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "HTTP Status Code | Scenario", + columns: [ + "HTTP Status Code", + "Scenario", + ], + }, + split: "----------------------------------|---------------------", + rows: [ + TableRow { + raw: "409 | Torrent queueing is not enabled", + columns: [ + "409", + "Torrent queueing is not enabled", + ], + }, + TableRow { + raw: "200 | All other scenarios", + columns: [ + "200", + "All other scenarios", + ], + }, + ], + }, + ), + ], + children: [], + }, + TokenTree { + title: Some( + "Minimal torrent priority", + ), + content: [ + Text( + "", + ), + Text( + "Requires knowing the torrent hash. You can get it from [torrent list](#get-torrent-list).", + ), + Text( + "", + ), + Text( + "Name: `bottomPrio`", + ), + Text( + "", + ), + Asterix( + "Parameters:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "Parameter | Type | Description", + columns: [ + "Parameter", + "Type", + "Description", + ], + }, + split: "------------|----------|------------", + rows: [ + TableRow { + raw: "`hashes` | string | The hashes of the torrents you want to set to the minimum priority. `hashes` can contain multiple hashes separated by `\\|`, to set multiple torrents to the minimum priority, or set to `all`, to set all torrents to the minimum priority.", + columns: [ + "hashes", + "string", + "The hashes of the torrents you want to set to the minimum priority. hashes can contain multiple hashes separated by \\", + ", to set multiple torrents to the minimum priority, or set to all, to set all torrents to the minimum priority.", + ], + }, + ], + }, + ), + Text( + "Example:", + ), + Text( + "", + ), + Text( + "```http", + ), + Table( + Table { + header: TableRow { + raw: "/api/v2/torrents/bottomPrio?hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32|54eddd830a5b58480a6143d616a97e3a6c23c439", + columns: [ + "/api/v2/torrents/bottomPrio?hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32", + "54eddd830a5b58480a6143d616a97e3a6c23c439", + ], + }, + split: "```", + rows: [], + }, + ), + Asterix( + "Returns:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "HTTP Status Code | Scenario", + columns: [ + "HTTP Status Code", + "Scenario", + ], + }, + split: "----------------------------------|---------------------", + rows: [ + TableRow { + raw: "409 | Torrent queueing is not enabled", + columns: [ + "409", + "Torrent queueing is not enabled", + ], + }, + TableRow { + raw: "200 | All other scenarios", + columns: [ + "200", + "All other scenarios", + ], + }, + ], + }, + ), + ], + children: [], + }, + TokenTree { + title: Some( + "Set file priority", + ), + content: [ + Text( + "", + ), + Text( + "Name: `filePrio`", + ), + Text( + "", + ), + Asterix( + "Parameters:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "Parameter | Type | Description", + columns: [ + "Parameter", + "Type", + "Description", + ], + }, + split: "----------------------------------|---------|------------", + rows: [ + TableRow { + raw: "`hash` | string | The hash of the torrent", + columns: [ + "hash", + "string", + "The hash of the torrent", + ], + }, + TableRow { + raw: "`id` | string | File ids, separated by `\\|`", + columns: [ + "id", + "string", + "File ids, separated by \\", + "", + ], + }, + TableRow { + raw: "`priority` | number | File priority to set (consult [torrent contents API](#get-torrent-contents) for possible values)", + columns: [ + "priority", + "number", + "File priority to set (consult [torrent contents API](#get-torrent-contents) for possible values)", + ], + }, + ], + }, + ), + Text( + "`id` values correspond to file position inside the array returned by [torrent contents API](#get-torrent-contents), e.g. `id=0` for first file, `id=1` for second file, etc.", + ), + Text( + "", + ), + Text( + "Since 2.8.2 it is reccomended to use `index` field returned by [torrent contents API](#get-torrent-contents) (since the files can be filtered and the `index` value may differ from the position inside the response array).", + ), + Text( + "", + ), + Asterix( + "Returns:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "HTTP Status Code | Scenario", + columns: [ + "HTTP Status Code", + "Scenario", + ], + }, + split: "----------------------------------|---------------------", + rows: [ + TableRow { + raw: "400 | Priority is invalid", + columns: [ + "400", + "Priority is invalid", + ], + }, + TableRow { + raw: "400 | At least one file `id` is not a valid integer", + columns: [ + "400", + "At least one file id is not a valid integer", + ], + }, + TableRow { + raw: "404 | Torrent hash was not found", + columns: [ + "404", + "Torrent hash was not found", + ], + }, + TableRow { + raw: "409 | Torrent metadata hasn't downloaded yet", + columns: [ + "409", + "Torrent metadata hasn't downloaded yet", + ], + }, + TableRow { + raw: "409 | At least one file `id` was not found", + columns: [ + "409", + "At least one file id was not found", + ], + }, + TableRow { + raw: "200 | All other scenarios", + columns: [ + "200", + "All other scenarios", + ], + }, + ], + }, + ), + ], + children: [], + }, + TokenTree { + title: Some( + "Get torrent download limit", + ), + content: [ + Text( + "", + ), + Text( + "Requires knowing the torrent hash. You can get it from [torrent list](#get-torrent-list).", + ), + Text( + "", + ), + Text( + "Name: `downloadLimit`", + ), + Text( + "", + ), + Text( + "```http", + ), + Text( + "POST /api/v2/torrents/downloadLimit HTTP/1.1", + ), + Text( + "User-Agent: Fiddler", + ), + Text( + "Host: 127.0.0.1", + ), + Text( + "Cookie: SID=your_sid", + ), + Text( + "Content-Type: application/x-www-form-urlencoded", + ), + Text( + "Content-Length: length", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32|284b83c9c7935002391129fd97f43db5d7cc2ba0", + columns: [ + "hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32", + "284b83c9c7935002391129fd97f43db5d7cc2ba0", + ], + }, + split: "```", + rows: [], + }, + ), + Table( + Table { + header: TableRow { + raw: "`hashes` can contain multiple hashes separated by `|` or set to `all`", + columns: [ + "hashes can contain multiple hashes separated by", + "or set to all", + ], + }, + split: "", + rows: [], + }, + ), + Text( + "", + ), + Text( + "```http", + ), + Text( + "HTTP/1.1 200 OK", + ), + Text( + "content-type: application/json", + ), + Text( + "content-length: length", + ), + Text( + "", + ), + Text( + "{\"8c212779b4abde7c6bc608063a0d008b7e40ce32\":338944,\"284b83c9c7935002391129fd97f43db5d7cc2ba0\":123}", + ), + Text( + "```", + ), + Text( + "", + ), + Text( + "`8c212779b4abde7c6bc608063a0d008b7e40ce32` is the hash of the torrent and `338944` its download speed limit in bytes per second; this value will be zero if no limit is applied.", + ), + Text( + "", + ), + ], + children: [], + }, + TokenTree { + title: Some( + "Set torrent download limit", + ), + content: [ + Text( + "", + ), + Text( + "Requires knowing the torrent hash. You can get it from [torrent list](#get-torrent-list).", + ), + Text( + "", + ), + Text( + "```http", + ), + Text( + "POST /api/v2/torrents/setDownloadLimit HTTP/1.1", + ), + Text( + "User-Agent: Fiddler", + ), + Text( + "Host: 127.0.0.1", + ), + Text( + "Cookie: SID=your_sid", + ), + Text( + "Content-Type: application/x-www-form-urlencoded", + ), + Text( + "Content-Length: length", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32|284b83c9c7935002391129fd97f43db5d7cc2ba0&limit=131072", + columns: [ + "hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32", + "284b83c9c7935002391129fd97f43db5d7cc2ba0&limit=131072", + ], + }, + split: "```", + rows: [], + }, + ), + Table( + Table { + header: TableRow { + raw: "`hashes` can contain multiple hashes separated by `|` or set to `all`", + columns: [ + "hashes can contain multiple hashes separated by", + "or set to all", + ], + }, + split: "`limit` is the download speed limit in bytes per second you want to set.", + rows: [], + }, + ), + Asterix( + "Returns:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "HTTP Status Code | Scenario", + columns: [ + "HTTP Status Code", + "Scenario", + ], + }, + split: "----------------------------------|---------------------", + rows: [ + TableRow { + raw: "200 | All scenarios", + columns: [ + "200", + "All scenarios", + ], + }, + ], + }, + ), + ], + children: [], + }, + TokenTree { + title: Some( + "Set torrent share limit", + ), + content: [ + Text( + "", + ), + Text( + "Requires knowing the torrent hash. You can get it from [torrent list](#get-torrent-list).", + ), + Text( + "", + ), + Text( + "Name: `setShareLimits`", + ), + Text( + "", + ), + Text( + "```http", + ), + Text( + "POST /api/v2/torrents/setShareLimits HTTP/1.1", + ), + Text( + "User-Agent: Fiddler", + ), + Text( + "Host: 127.0.0.1", + ), + Text( + "Cookie: SID=your_sid", + ), + Text( + "Content-Type: application/x-www-form-urlencoded", + ), + Text( + "Content-Length: length", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32|284b83c9c7935002391129fd97f43db5d7cc2ba0&ratioLimit=1.0&seedingTimeLimit=60", + columns: [ + "hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32", + "284b83c9c7935002391129fd97f43db5d7cc2ba0&ratioLimit=1.0&seedingTimeLimit=60", + ], + }, + split: "```", + rows: [], + }, + ), + Table( + Table { + header: TableRow { + raw: "`hashes` can contain multiple hashes separated by `|` or set to `all`", + columns: [ + "hashes can contain multiple hashes separated by", + "or set to all", + ], + }, + split: "`ratioLimit` is the max ratio the torrent should be seeded until. `-2` means the global limit should be used, `-1` means no limit.", + rows: [], + }, + ), + Text( + "", + ), + Asterix( + "Returns:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "HTTP Status Code | Scenario", + columns: [ + "HTTP Status Code", + "Scenario", + ], + }, + split: "----------------------------------|---------------------", + rows: [ + TableRow { + raw: "200 | All scenarios", + columns: [ + "200", + "All scenarios", + ], + }, + ], + }, + ), + ], + children: [], + }, + TokenTree { + title: Some( + "Get torrent upload limit", + ), + content: [ + Text( + "", + ), + Text( + "Requires knowing the torrent hash. You can get it from [torrent list](#get-torrent-list).", + ), + Text( + "", + ), + Text( + "Name: `uploadLimit`", + ), + Text( + "", + ), + Text( + "```http", + ), + Text( + "POST /api/v2/torrents/uploadLimit HTTP/1.1", + ), + Text( + "User-Agent: Fiddler", + ), + Text( + "Host: 127.0.0.1", + ), + Text( + "Cookie: SID=your_sid", + ), + Text( + "Content-Type: application/x-www-form-urlencoded", + ), + Text( + "Content-Length: length", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32|284b83c9c7935002391129fd97f43db5d7cc2ba0", + columns: [ + "hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32", + "284b83c9c7935002391129fd97f43db5d7cc2ba0", + ], + }, + split: "```", + rows: [], + }, + ), + Table( + Table { + header: TableRow { + raw: "`hashes` can contain multiple hashes separated by `|` or set to `all`", + columns: [ + "hashes can contain multiple hashes separated by", + "or set to all", + ], + }, + split: "", + rows: [], + }, + ), + Text( + "", + ), + Text( + "```http", + ), + Text( + "HTTP/1.1 200 OK", + ), + Text( + "content-type: application/json", + ), + Text( + "content-length: length", + ), + Text( + "", + ), + Text( + "{\"8c212779b4abde7c6bc608063a0d008b7e40ce32\":338944,\"284b83c9c7935002391129fd97f43db5d7cc2ba0\":123}", + ), + Text( + "```", + ), + Text( + "", + ), + Text( + "`8c212779b4abde7c6bc608063a0d008b7e40ce32` is the hash of the torrent in the request and `338944` its upload speed limit in bytes per second; this value will be zero if no limit is applied.", + ), + Text( + "", + ), + ], + children: [], + }, + TokenTree { + title: Some( + "Set torrent upload limit", + ), + content: [ + Text( + "", + ), + Text( + "Requires knowing the torrent hash. You can get it from [torrent list](#get-torrent-list).", + ), + Text( + "", + ), + Text( + "Name: `setUploadLimit`", + ), + Text( + "", + ), + Text( + "```http", + ), + Text( + "POST /api/v2/torrents/setUploadLimit HTTP/1.1", + ), + Text( + "User-Agent: Fiddler", + ), + Text( + "Host: 127.0.0.1", + ), + Text( + "Cookie: SID=your_sid", + ), + Text( + "Content-Type: application/x-www-form-urlencoded", + ), + Text( + "Content-Length: length", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32|284b83c9c7935002391129fd97f43db5d7cc2ba0&limit=131072", + columns: [ + "hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32", + "284b83c9c7935002391129fd97f43db5d7cc2ba0&limit=131072", + ], + }, + split: "```", + rows: [], + }, + ), + Table( + Table { + header: TableRow { + raw: "`hashes` can contain multiple hashes separated by `|` or set to `all`", + columns: [ + "hashes can contain multiple hashes separated by", + "or set to all", + ], + }, + split: "`limit` is the upload speed limit in bytes per second you want to set.", + rows: [], + }, + ), + Asterix( + "Returns:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "HTTP Status Code | Scenario", + columns: [ + "HTTP Status Code", + "Scenario", + ], + }, + split: "----------------------------------|---------------------", + rows: [ + TableRow { + raw: "200 | All scenarios", + columns: [ + "200", + "All scenarios", + ], + }, + ], + }, + ), + ], + children: [], + }, + TokenTree { + title: Some( + "Set torrent location", + ), + content: [ + Text( + "", + ), + Text( + "Requires knowing the torrent hash. You can get it from [torrent list](#get-torrent-list).", + ), + Text( + "", + ), + Text( + "Name: `setLocation`", + ), + Text( + "", + ), + Text( + "```http", + ), + Text( + "POST /api/v2/torrents/setLocation HTTP/1.1", + ), + Text( + "User-Agent: Fiddler", + ), + Text( + "Host: 127.0.0.1", + ), + Text( + "Cookie: SID=your_sid", + ), + Text( + "Content-Type: application/x-www-form-urlencoded", + ), + Text( + "Content-Length: length", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32|284b83c9c7935002391129fd97f43db5d7cc2ba0&location=/mnt/nfs/media", + columns: [ + "hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32", + "284b83c9c7935002391129fd97f43db5d7cc2ba0&location=/mnt/nfs/media", + ], + }, + split: "```", + rows: [], + }, + ), + Table( + Table { + header: TableRow { + raw: "`hashes` can contain multiple hashes separated by `|` or set to `all`", + columns: [ + "hashes can contain multiple hashes separated by", + "or set to all", + ], + }, + split: "`location` is the location to download the torrent to. If the location doesn't exist, the torrent's location is unchanged.", + rows: [], + }, + ), + Asterix( + "Returns:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "HTTP Status Code | Scenario", + columns: [ + "HTTP Status Code", + "Scenario", + ], + }, + split: "----------------------------------|---------------------", + rows: [ + TableRow { + raw: "400 | Save path is empty", + columns: [ + "400", + "Save path is empty", + ], + }, + TableRow { + raw: "403 | User does not have write access to directory", + columns: [ + "403", + "User does not have write access to directory", + ], + }, + TableRow { + raw: "409 | Unable to create save path directory", + columns: [ + "409", + "Unable to create save path directory", + ], + }, + TableRow { + raw: "200 | All other scenarios", + columns: [ + "200", + "All other scenarios", + ], + }, + ], + }, + ), + ], + children: [], + }, + TokenTree { + title: Some( + "Set torrent name", + ), + content: [ + Text( + "", + ), + Text( + "Requires knowing the torrent hash. You can get it from [torrent list](#get-torrent-list).", + ), + Text( + "", + ), + Text( + "Name: `rename`", + ), + Text( + "", + ), + Text( + "```http", + ), + Text( + "POST /api/v2/torrents/rename HTTP/1.1", + ), + Text( + "User-Agent: Fiddler", + ), + Text( + "Host: 127.0.0.1", + ), + Text( + "Cookie: SID=your_sid", + ), + Text( + "Content-Type: application/x-www-form-urlencoded", + ), + Text( + "Content-Length: length", + ), + Text( + "", + ), + Text( + "hash=8c212779b4abde7c6bc608063a0d008b7e40ce32&name=This%20is%20a%20test", + ), + Text( + "```", + ), + Text( + "", + ), + Asterix( + "Returns:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "HTTP Status Code | Scenario", + columns: [ + "HTTP Status Code", + "Scenario", + ], + }, + split: "----------------------------------|---------------------", + rows: [ + TableRow { + raw: "404 | Torrent hash is invalid", + columns: [ + "404", + "Torrent hash is invalid", + ], + }, + TableRow { + raw: "409 | Torrent name is empty", + columns: [ + "409", + "Torrent name is empty", + ], + }, + TableRow { + raw: "200 | All other scenarios", + columns: [ + "200", + "All other scenarios", + ], + }, + ], + }, + ), + ], + children: [], + }, + TokenTree { + title: Some( + "Set torrent category", + ), + content: [ + Text( + "", + ), + Text( + "Requires knowing the torrent hash. You can get it from [torrent list](#get-torrent-list).", + ), + Text( + "", + ), + Text( + "Name: `setCategory`", + ), + Text( + "", + ), + Text( + "```http", + ), + Text( + "POST /api/v2/torrents/setCategory HTTP/1.1", + ), + Text( + "User-Agent: Fiddler", + ), + Text( + "Host: 127.0.0.1", + ), + Text( + "Cookie: SID=your_sid", + ), + Text( + "Content-Type: application/x-www-form-urlencoded", + ), + Text( + "Content-Length: length", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32|284b83c9c7935002391129fd97f43db5d7cc2ba0&category=CategoryName", + columns: [ + "hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32", + "284b83c9c7935002391129fd97f43db5d7cc2ba0&category=CategoryName", + ], + }, + split: "```", + rows: [], + }, + ), + Table( + Table { + header: TableRow { + raw: "`hashes` can contain multiple hashes separated by `|` or set to `all`", + columns: [ + "hashes can contain multiple hashes separated by", + "or set to all", + ], + }, + split: "", + rows: [], + }, + ), + Text( + "", + ), + Asterix( + "Returns:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "HTTP Status Code | Scenario", + columns: [ + "HTTP Status Code", + "Scenario", + ], + }, + split: "----------------------------------|---------------------", + rows: [ + TableRow { + raw: "409 | Category name does not exist", + columns: [ + "409", + "Category name does not exist", + ], + }, + TableRow { + raw: "200 | All other scenarios", + columns: [ + "200", + "All other scenarios", + ], + }, + ], + }, + ), + ], + children: [], + }, + TokenTree { + title: Some( + "Get all categories", + ), + content: [ + Text( + "", + ), + Text( + "Name: `categories`", + ), + Text( + "", + ), + Text( + "Parameters:", + ), + Text( + "", + ), + Text( + "None", + ), + Text( + "", + ), + Text( + "Returns all categories in JSON format, e.g.:", + ), + Text( + "", + ), + Text( + "```JSON", + ), + Text( + "{", + ), + Text( + " \"Video\": {", + ), + Text( + " \"name\": \"Video\",", + ), + Text( + " \"savePath\": \"/home/user/torrents/video/\"", + ), + Text( + " },", + ), + Text( + " \"eBooks\": {", + ), + Text( + " \"name\": \"eBooks\",", + ), + Text( + " \"savePath\": \"/home/user/torrents/eBooks/\"", + ), + Text( + " }", + ), + Text( + "}", + ), + Text( + "```", + ), + Asterix( + "Returns:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "HTTP Status Code | Scenario", + columns: [ + "HTTP Status Code", + "Scenario", + ], + }, + split: "----------------------------------|---------------------", + rows: [ + TableRow { + raw: "200 | All scenarios", + columns: [ + "200", + "All scenarios", + ], + }, + ], + }, + ), + ], + children: [], + }, + TokenTree { + title: Some( + "Add new category", + ), + content: [ + Text( + "", + ), + Text( + "Name: `createCategory`", + ), + Text( + "", + ), + Text( + "```http", + ), + Text( + "POST /api/v2/torrents/createCategory HTTP/1.1", + ), + Text( + "User-Agent: Fiddler", + ), + Text( + "Host: 127.0.0.1", + ), + Text( + "Cookie: SID=your_sid", + ), + Text( + "Content-Type: application/x-www-form-urlencoded", + ), + Text( + "Content-Length: length", + ), + Text( + "", + ), + Text( + "category=CategoryName&savePath=/path/to/dir", + ), + Text( + "```", + ), + Text( + "", + ), + Text( + "`category` is the category you want to create.", + ), + Text( + "", + ), + Asterix( + "Returns:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "HTTP Status Code | Scenario", + columns: [ + "HTTP Status Code", + "Scenario", + ], + }, + split: "----------------------------------|---------------------", + rows: [ + TableRow { + raw: "400 | Category name is empty", + columns: [ + "400", + "Category name is empty", + ], + }, + TableRow { + raw: "409 | Category name is invalid", + columns: [ + "409", + "Category name is invalid", + ], + }, + TableRow { + raw: "200 | All other scenarios", + columns: [ + "200", + "All other scenarios", + ], + }, + ], + }, + ), + ], + children: [], + }, + TokenTree { + title: Some( + "Edit category", + ), + content: [ + Text( + "", + ), + Text( + "Name: `editCategory`", + ), + Text( + "", + ), + Text( + "```http", + ), + Text( + "POST /api/v2/torrents/editCategory HTTP/1.1", + ), + Text( + "User-Agent: Fiddler", + ), + Text( + "Host: 127.0.0.1", + ), + Text( + "Cookie: SID=your_sid", + ), + Text( + "Content-Type: application/x-www-form-urlencoded", + ), + Text( + "Content-Length: length", + ), + Text( + "", + ), + Text( + "category=CategoryName&savePath=/path/to/save/torrents/to", + ), + Text( + "```", + ), + Text( + "", + ), + Asterix( + "Returns:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "HTTP Status Code | Scenario", + columns: [ + "HTTP Status Code", + "Scenario", + ], + }, + split: "----------------------------------|---------------------", + rows: [ + TableRow { + raw: "400 | Category name is empty", + columns: [ + "400", + "Category name is empty", + ], + }, + TableRow { + raw: "409 | Category editing failed", + columns: [ + "409", + "Category editing failed", + ], + }, + TableRow { + raw: "200 | All other scenarios", + columns: [ + "200", + "All other scenarios", + ], + }, + ], + }, + ), + ], + children: [], + }, + TokenTree { + title: Some( + "Remove categories", + ), + content: [ + Text( + "", + ), + Text( + "Name: `removeCategories`", + ), + Text( + "", + ), + Text( + "```http", + ), + Text( + "POST /api/v2/torrents/removeCategories HTTP/1.1", + ), + Text( + "User-Agent: Fiddler", + ), + Text( + "Host: 127.0.0.1", + ), + Text( + "Cookie: SID=your_sid", + ), + Text( + "Content-Type: application/x-www-form-urlencoded", + ), + Text( + "Content-Length: length", + ), + Text( + "", + ), + Text( + "categories=Category1%0ACategory2", + ), + Text( + "```", + ), + Text( + "", + ), + Text( + "`categories` can contain multiple cateogies separated by `\\n` (%0A urlencoded)", + ), + Text( + "", + ), + Asterix( + "Returns:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "HTTP Status Code | Scenario", + columns: [ + "HTTP Status Code", + "Scenario", + ], + }, + split: "----------------------------------|---------------------", + rows: [ + TableRow { + raw: "200 | All scenarios", + columns: [ + "200", + "All scenarios", + ], + }, + ], + }, + ), + ], + children: [], + }, + TokenTree { + title: Some( + "Add torrent tags", + ), + content: [ + Text( + "", + ), + Text( + "Requires knowing the torrent hash. You can get it from [torrent list](#get-torrent-list).", + ), + Text( + "", + ), + Text( + "Name: `addTags`", + ), + Text( + "", + ), + Text( + "```http", + ), + Text( + "POST /api/v2/torrents/addTags HTTP/1.1", + ), + Text( + "User-Agent: Fiddler", + ), + Text( + "Host: 127.0.0.1", + ), + Text( + "Cookie: SID=your_sid", + ), + Text( + "Content-Type: application/x-www-form-urlencoded", + ), + Text( + "Content-Length: length", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32|284b83c9c7935002391129fd97f43db5d7cc2ba0&tags=TagName1,TagName2", + columns: [ + "hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32", + "284b83c9c7935002391129fd97f43db5d7cc2ba0&tags=TagName1,TagName2", + ], + }, + split: "```", + rows: [], + }, + ), + Table( + Table { + header: TableRow { + raw: "`hashes` can contain multiple hashes separated by `|` or set to `all`", + columns: [ + "hashes can contain multiple hashes separated by", + "or set to all", + ], + }, + split: "", + rows: [], + }, + ), + Text( + "", + ), + Asterix( + "Returns:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "HTTP Status Code | Scenario", + columns: [ + "HTTP Status Code", + "Scenario", + ], + }, + split: "----------------------------------|---------------------", + rows: [ + TableRow { + raw: "200 | All scenarios", + columns: [ + "200", + "All scenarios", + ], + }, + ], + }, + ), + ], + children: [], + }, + TokenTree { + title: Some( + "Remove torrent tags", + ), + content: [ + Text( + "", + ), + Text( + "Requires knowing the torrent hash. You can get it from [torrent list](#get-torrent-list).", + ), + Text( + "", + ), + Text( + "Name: `removeTags`", + ), + Text( + "", + ), + Text( + "```http", + ), + Text( + "POST /api/v2/torrents/removeTags HTTP/1.1", + ), + Text( + "User-Agent: Fiddler", + ), + Text( + "Host: 127.0.0.1", + ), + Text( + "Cookie: SID=your_sid", + ), + Text( + "Content-Type: application/x-www-form-urlencoded", + ), + Text( + "Content-Length: length", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32|284b83c9c7935002391129fd97f43db5d7cc2ba0&tags=TagName1,TagName2", + columns: [ + "hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32", + "284b83c9c7935002391129fd97f43db5d7cc2ba0&tags=TagName1,TagName2", + ], + }, + split: "```", + rows: [], + }, + ), + Table( + Table { + header: TableRow { + raw: "`hashes` can contain multiple hashes separated by `|` or set to `all`", + columns: [ + "hashes can contain multiple hashes separated by", + "or set to all", + ], + }, + split: "", + rows: [], + }, + ), + Text( + "Empty list removes all tags from relevant torrents.", + ), + Text( + "", + ), + Asterix( + "Returns:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "HTTP Status Code | Scenario", + columns: [ + "HTTP Status Code", + "Scenario", + ], + }, + split: "----------------------------------|---------------------", + rows: [ + TableRow { + raw: "200 | All scenarios", + columns: [ + "200", + "All scenarios", + ], + }, + ], + }, + ), + ], + children: [], + }, + TokenTree { + title: Some( + "Get all tags", + ), + content: [ + Text( + "", + ), + Text( + "Name: `tags`", + ), + Text( + "", + ), + Text( + "Parameters:", + ), + Text( + "", + ), + Text( + "None", + ), + Text( + "", + ), + Text( + "Returns all tags in JSON format, e.g.:", + ), + Text( + "", + ), + Text( + "```JSON", + ), + Text( + "[", + ), + Text( + " \"Tag 1\",", + ), + Text( + " \"Tag 2\"", + ), + Text( + "]", + ), + Text( + "```", + ), + Asterix( + "Returns:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "HTTP Status Code | Scenario", + columns: [ + "HTTP Status Code", + "Scenario", + ], + }, + split: "----------------------------------|---------------------", + rows: [ + TableRow { + raw: "200 | All scenarios", + columns: [ + "200", + "All scenarios", + ], + }, + ], + }, + ), + ], + children: [], + }, + TokenTree { + title: Some( + "Create tags", + ), + content: [ + Text( + "", + ), + Text( + "Name: `createTags`", + ), + Text( + "", + ), + Text( + "```http", + ), + Text( + "POST /api/v2/torrents/createTags HTTP/1.1", + ), + Text( + "User-Agent: Fiddler", + ), + Text( + "Host: 127.0.0.1", + ), + Text( + "Cookie: SID=your_sid", + ), + Text( + "Content-Type: application/x-www-form-urlencoded", + ), + Text( + "Content-Length: length", + ), + Text( + "", + ), + Text( + "tags=TagName1,TagName2", + ), + Text( + "```", + ), + Text( + "`tags` is a list of tags you want to create.", + ), + Text( + "Can contain multiple tags separated by `,`.", + ), + Text( + "", + ), + Asterix( + "Returns:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "HTTP Status Code | Scenario", + columns: [ + "HTTP Status Code", + "Scenario", + ], + }, + split: "----------------------------------|---------------------", + rows: [ + TableRow { + raw: "200 | All scenarios", + columns: [ + "200", + "All scenarios", + ], + }, + ], + }, + ), + ], + children: [], + }, + TokenTree { + title: Some( + "Delete tags", + ), + content: [ + Text( + "", + ), + Text( + "Name: `deleteTags`", + ), + Text( + "", + ), + Text( + "```http", + ), + Text( + "POST /api/v2/torrents/deleteTags HTTP/1.1", + ), + Text( + "User-Agent: Fiddler", + ), + Text( + "Host: 127.0.0.1", + ), + Text( + "Cookie: SID=your_sid", + ), + Text( + "Content-Type: application/x-www-form-urlencoded", + ), + Text( + "Content-Length: length", + ), + Text( + "", + ), + Text( + "tags=TagName1,TagName2", + ), + Text( + "```", + ), + Text( + "", + ), + Text( + "`tags` is a list of tags you want to delete.", + ), + Text( + "Can contain multiple tags separated by `,`.", + ), + Text( + "", + ), + Asterix( + "Returns:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "HTTP Status Code | Scenario", + columns: [ + "HTTP Status Code", + "Scenario", + ], + }, + split: "----------------------------------|---------------------", + rows: [ + TableRow { + raw: "200 | All scenarios", + columns: [ + "200", + "All scenarios", + ], + }, + ], + }, + ), + ], + children: [], + }, + TokenTree { + title: Some( + "Set automatic torrent management", + ), + content: [ + Text( + "", + ), + Text( + "Requires knowing the torrent hash. You can get it from [torrent list](#get-torrent-list).", + ), + Text( + "", + ), + Text( + "Name: `setAutoManagement`", + ), + Text( + "", + ), + Text( + "```http", + ), + Text( + "POST /api/v2/torrents/setAutoManagement HTTP/1.1", + ), + Text( + "User-Agent: Fiddler", + ), + Text( + "Host: 127.0.0.1", + ), + Text( + "Cookie: SID=your_sid", + ), + Text( + "Content-Type: application/x-www-form-urlencoded", + ), + Text( + "Content-Length: length", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32|284b83c9c7935002391129fd97f43db5d7cc2ba0&enable=true", + columns: [ + "hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32", + "284b83c9c7935002391129fd97f43db5d7cc2ba0&enable=true", + ], + }, + split: "```", + rows: [], + }, + ), + Table( + Table { + header: TableRow { + raw: "`hashes` can contain multiple hashes separated by `|` or set to `all`", + columns: [ + "hashes can contain multiple hashes separated by", + "or set to all", + ], + }, + split: "`enable` is a boolean, affects the torrents listed in `hashes`, default is `false`", + rows: [], + }, + ), + Asterix( + "Returns:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "HTTP Status Code | Scenario", + columns: [ + "HTTP Status Code", + "Scenario", + ], + }, + split: "----------------------------------|---------------------", + rows: [ + TableRow { + raw: "200 | All scenarios", + columns: [ + "200", + "All scenarios", + ], + }, + ], + }, + ), + ], + children: [], + }, + TokenTree { + title: Some( + "Toggle sequential download", + ), + content: [ + Text( + "", + ), + Text( + "Requires knowing the torrent hash. You can get it from [torrent list](#get-torrent-list).", + ), + Text( + "", + ), + Text( + "Name: `toggleSequentialDownload`", + ), + Text( + "", + ), + Asterix( + "Parameters:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "Parameter | Type | Description", + columns: [ + "Parameter", + "Type", + "Description", + ], + }, + split: "------------|----------|------------", + rows: [ + TableRow { + raw: "`hashes` | string | The hashes of the torrents you want to toggle sequential download for. `hashes` can contain multiple hashes separated by `\\|`, to toggle sequential download for multiple torrents, or set to `all`, to toggle sequential download for all torrents.", + columns: [ + "hashes", + "string", + "The hashes of the torrents you want to toggle sequential download for. hashes can contain multiple hashes separated by \\", + ", to toggle sequential download for multiple torrents, or set to all, to toggle sequential download for all torrents.", + ], + }, + ], + }, + ), + Text( + "Example:", + ), + Text( + "", + ), + Text( + "```http", + ), + Table( + Table { + header: TableRow { + raw: "/api/v2/torrents/toggleSequentialDownload?hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32|54eddd830a5b58480a6143d616a97e3a6c23c439", + columns: [ + "/api/v2/torrents/toggleSequentialDownload?hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32", + "54eddd830a5b58480a6143d616a97e3a6c23c439", + ], + }, + split: "```", + rows: [], + }, + ), + Asterix( + "Returns:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "HTTP Status Code | Scenario", + columns: [ + "HTTP Status Code", + "Scenario", + ], + }, + split: "----------------------------------|---------------------", + rows: [ + TableRow { + raw: "200 | All scenarios", + columns: [ + "200", + "All scenarios", + ], + }, + ], + }, + ), + ], + children: [], + }, + TokenTree { + title: Some( + "Set first/last piece priority", + ), + content: [ + Text( + "", + ), + Text( + "Requires knowing the torrent hash. You can get it from [torrent list](#get-torrent-list).", + ), + Text( + "", + ), + Text( + "Name: `toggleFirstLastPiecePrio`", + ), + Text( + "", + ), + Asterix( + "Parameters:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "Parameter | Type | Description", + columns: [ + "Parameter", + "Type", + "Description", + ], + }, + split: "------------|----------|------------", + rows: [ + TableRow { + raw: "`hashes` | string | The hashes of the torrents you want to toggle the first/last piece priority for. `hashes` can contain multiple hashes separated by `\\|`, to toggle the first/last piece priority for multiple torrents, or set to `all`, to toggle the first/last piece priority for all torrents.", + columns: [ + "hashes", + "string", + "The hashes of the torrents you want to toggle the first/last piece priority for. hashes can contain multiple hashes separated by \\", + ", to toggle the first/last piece priority for multiple torrents, or set to all, to toggle the first/last piece priority for all torrents.", + ], + }, + ], + }, + ), + Text( + "Example:", + ), + Text( + "", + ), + Text( + "```http", + ), + Table( + Table { + header: TableRow { + raw: "/api/v2/torrents/toggleFirstLastPiecePrio?hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32|54eddd830a5b58480a6143d616a97e3a6c23c439", + columns: [ + "/api/v2/torrents/toggleFirstLastPiecePrio?hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32", + "54eddd830a5b58480a6143d616a97e3a6c23c439", + ], + }, + split: "```", + rows: [], + }, + ), + Asterix( + "Returns:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "HTTP Status Code | Scenario", + columns: [ + "HTTP Status Code", + "Scenario", + ], + }, + split: "----------------------------------|---------------------", + rows: [ + TableRow { + raw: "200 | All scenarios", + columns: [ + "200", + "All scenarios", + ], + }, + ], + }, + ), + ], + children: [], + }, + TokenTree { + title: Some( + "Set force start", + ), + content: [ + Text( + "", + ), + Text( + "Requires knowing the torrent hash. You can get it from [torrent list](#get-torrent-list).", + ), + Text( + "", + ), + Text( + "Name: `setForceStart`", + ), + Text( + "", + ), + Text( + "```http", + ), + Text( + "POST /api/v2/torrents/setForceStart HTTP/1.1", + ), + Text( + "User-Agent: Fiddler", + ), + Text( + "Host: 127.0.0.1", + ), + Text( + "Cookie: SID=your_sid", + ), + Text( + "Content-Type: application/x-www-form-urlencoded", + ), + Text( + "Content-Length: length", + ), + Text( + "", + ), + Text( + "hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32?value=true", + ), + Text( + "```", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "`hashes` can contain multiple hashes separated by `|` or set to `all`", + columns: [ + "hashes can contain multiple hashes separated by", + "or set to all", + ], + }, + split: "`value` is a boolean, affects the torrents listed in `hashes`, default is `false`", + rows: [], + }, + ), + Asterix( + "Returns:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "HTTP Status Code | Scenario", + columns: [ + "HTTP Status Code", + "Scenario", + ], + }, + split: "----------------------------------|---------------------", + rows: [ + TableRow { + raw: "200 | All scenarios", + columns: [ + "200", + "All scenarios", + ], + }, + ], + }, + ), + ], + children: [], + }, + TokenTree { + title: Some( + "Set super seeding", + ), + content: [ + Text( + "", + ), + Text( + "Requires knowing the torrent hash. You can get it from [torrent list](#get-torrent-list).", + ), + Text( + "", + ), + Text( + "Name: `setSuperSeeding`", + ), + Text( + "", + ), + Text( + "```http", + ), + Text( + "POST /api/v2/torrents/setSuperSeeding HTTP/1.1", + ), + Text( + "User-Agent: Fiddler", + ), + Text( + "Host: 127.0.0.1", + ), + Text( + "Cookie: SID=your_sid", + ), + Text( + "Content-Type: application/x-www-form-urlencoded", + ), + Text( + "Content-Length: length", + ), + Text( + "", + ), + Text( + "hashes=8c212779b4abde7c6bc608063a0d008b7e40ce32?value=true", + ), + Text( + "```", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "`hashes` can contain multiple hashes separated by `|` or set to `all`", + columns: [ + "hashes can contain multiple hashes separated by", + "or set to all", + ], + }, + split: "`value` is a boolean, affects the torrents listed in `hashes`, default is `false`", + rows: [], + }, + ), + Asterix( + "Returns:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "HTTP Status Code | Scenario", + columns: [ + "HTTP Status Code", + "Scenario", + ], + }, + split: "----------------------------------|---------------------", + rows: [ + TableRow { + raw: "200 | All scenarios", + columns: [ + "200", + "All scenarios", + ], + }, + ], + }, + ), + ], + children: [], + }, + TokenTree { + title: Some( + "Rename file", + ), + content: [ + Text( + "", + ), + Text( + "Name: `renameFile`", + ), + Text( + "", + ), + Asterix( + "Parameters:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "Parameter | Type | Description", + columns: [ + "Parameter", + "Type", + "Description", + ], + }, + split: "----------------------------------|----------|------------", + rows: [ + TableRow { + raw: "`hash` | string | The hash of the torrent", + columns: [ + "hash", + "string", + "The hash of the torrent", + ], + }, + TableRow { + raw: "`oldPath` | string | The old path of the torrent", + columns: [ + "oldPath", + "string", + "The old path of the torrent", + ], + }, + TableRow { + raw: "`newPath` | string | The new path to use for the file", + columns: [ + "newPath", + "string", + "The new path to use for the file", + ], + }, + ], + }, + ), + Asterix( + "Returns:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "HTTP Status Code | Scenario", + columns: [ + "HTTP Status Code", + "Scenario", + ], + }, + split: "----------------------------------|---------------------", + rows: [ + TableRow { + raw: "400 | Missing `newPath` parameter", + columns: [ + "400", + "Missing newPath parameter", + ], + }, + TableRow { + raw: "409 | Invalid `newPath` or `oldPath`, or `newPath` already in use", + columns: [ + "409", + "Invalid newPath or oldPath, or newPath already in use", + ], + }, + TableRow { + raw: "200 | All other scenarios", + columns: [ + "200", + "All other scenarios", + ], + }, + ], + }, + ), + ], + children: [], + }, + TokenTree { + title: Some( + "Rename folder", + ), + content: [ + Text( + "", + ), + Text( + "Name: `renameFolder`", + ), + Text( + "", + ), + Asterix( + "Parameters:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "Parameter | Type | Description", + columns: [ + "Parameter", + "Type", + "Description", + ], + }, + split: "----------------------------------|----------|------------", + rows: [ + TableRow { + raw: "`hash` | string | The hash of the torrent", + columns: [ + "hash", + "string", + "The hash of the torrent", + ], + }, + TableRow { + raw: "`oldPath` | string | The old path of the torrent", + columns: [ + "oldPath", + "string", + "The old path of the torrent", + ], + }, + TableRow { + raw: "`newPath` | string | The new path to use for the file", + columns: [ + "newPath", + "string", + "The new path to use for the file", + ], + }, + ], + }, + ), + Asterix( + "Returns:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "HTTP Status Code | Scenario", + columns: [ + "HTTP Status Code", + "Scenario", + ], + }, + split: "----------------------------------|---------------------", + rows: [ + TableRow { + raw: "400 | Missing `newPath` parameter", + columns: [ + "400", + "Missing newPath parameter", + ], + }, + TableRow { + raw: "409 | Invalid `newPath` or `oldPath`, or `newPath` already in use", + columns: [ + "409", + "Invalid newPath or oldPath, or newPath already in use", + ], + }, + TableRow { + raw: "200 | All other scenarios", + columns: [ + "200", + "All other scenarios", + ], + }, + ], + }, + ), + ], + children: [], + }, + ], + }, + TokenTree { + title: Some( + "RSS (experimental)", + ), + content: [ + Text( + "", + ), + Text( + "All RSS API methods are under \"rss\", e.g.: `/api/v2/rss/methodName`.", + ), + Text( + "", + ), + ], + children: [ + TokenTree { + title: Some( + "Add folder", + ), + content: [ + Text( + "", + ), + Text( + "Name: `addFolder`", + ), + Text( + "", + ), + Text( + "Parameters:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "Parameter | Type | Description", + columns: [ + "Parameter", + "Type", + "Description", + ], + }, + split: "----------------------------------|---------|------------", + rows: [ + TableRow { + raw: "`path` | string | Full path of added folder (e.g. \"The Pirate Bay\\Top100\")", + columns: [ + "path", + "string", + "Full path of added folder (e.g. \"The Pirate Bay\\Top100\")", + ], + }, + ], + }, + ), + Asterix( + "Returns:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "HTTP Status Code | Scenario", + columns: [ + "HTTP Status Code", + "Scenario", + ], + }, + split: "----------------------------------|---------------------", + rows: [ + TableRow { + raw: "409 | Failure to add folder", + columns: [ + "409", + "Failure to add folder", + ], + }, + TableRow { + raw: "200 | All other scenarios", + columns: [ + "200", + "All other scenarios", + ], + }, + ], + }, + ), + ], + children: [], + }, + TokenTree { + title: Some( + "Add feed", + ), + content: [ + Text( + "", + ), + Text( + "Name: `addFeed`", + ), + Text( + "", + ), + Text( + "Parameters:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "Parameter | Type | Description", + columns: [ + "Parameter", + "Type", + "Description", + ], + }, + split: "----------------------------------|---------|------------", + rows: [ + TableRow { + raw: "`url` | string | URL of RSS feed (e.g. \"[http://thepiratebay.org/rss//top100/200](http://thepiratebay.org/rss//top100/200)\")", + columns: [ + "url", + "string", + "URL of RSS feed (e.g. \"[http://thepiratebay.org/rss//top100/200](http://thepiratebay.org/rss//top100/200)\")", + ], + }, + TableRow { + raw: "`path` _optional_ | string | Full path of added folder (e.g. \"The Pirate Bay\\Top100\\Video\")", + columns: [ + "path _optional_", + "string", + "Full path of added folder (e.g. \"The Pirate Bay\\Top100\\Video\")", + ], + }, + ], + }, + ), + Asterix( + "Returns:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "HTTP Status Code | Scenario", + columns: [ + "HTTP Status Code", + "Scenario", + ], + }, + split: "----------------------------------|---------------------", + rows: [ + TableRow { + raw: "409 | Failure to add feed", + columns: [ + "409", + "Failure to add feed", + ], + }, + TableRow { + raw: "200 | All other scenarios", + columns: [ + "200", + "All other scenarios", + ], + }, + ], + }, + ), + ], + children: [], + }, + TokenTree { + title: Some( + "Remove item", + ), + content: [ + Text( + "", + ), + Text( + "Removes folder or feed.", + ), + Text( + "", + ), + Text( + "Name: `removeItem`", + ), + Text( + "", + ), + Text( + "Parameters:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "Parameter | Type | Description", + columns: [ + "Parameter", + "Type", + "Description", + ], + }, + split: "----------------------------------|---------|------------", + rows: [ + TableRow { + raw: "`path` | string | Full path of removed item (e.g. \"The Pirate Bay\\Top100\")", + columns: [ + "path", + "string", + "Full path of removed item (e.g. \"The Pirate Bay\\Top100\")", + ], + }, + ], + }, + ), + Asterix( + "Returns:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "HTTP Status Code | Scenario", + columns: [ + "HTTP Status Code", + "Scenario", + ], + }, + split: "----------------------------------|---------------------", + rows: [ + TableRow { + raw: "409 | Failure to remove item", + columns: [ + "409", + "Failure to remove item", + ], + }, + TableRow { + raw: "200 | All other scenarios", + columns: [ + "200", + "All other scenarios", + ], + }, + ], + }, + ), + ], + children: [], + }, + TokenTree { + title: Some( + "Move item", + ), + content: [ + Text( + "", + ), + Text( + "Moves/renames folder or feed.", + ), + Text( + "", + ), + Text( + "Name: `moveItem`", + ), + Text( + "", + ), + Text( + "Parameters:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "Parameter | Type | Description", + columns: [ + "Parameter", + "Type", + "Description", + ], + }, + split: "----------------------------------|---------|------------", + rows: [ + TableRow { + raw: "`itemPath` | string | Current full path of item (e.g. \"The Pirate Bay\\Top100\")", + columns: [ + "itemPath", + "string", + "Current full path of item (e.g. \"The Pirate Bay\\Top100\")", + ], + }, + TableRow { + raw: "`destPath` | string | New full path of item (e.g. \"The Pirate Bay\")", + columns: [ + "destPath", + "string", + "New full path of item (e.g. \"The Pirate Bay\")", + ], + }, + ], + }, + ), + Asterix( + "Returns:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "HTTP Status Code | Scenario", + columns: [ + "HTTP Status Code", + "Scenario", + ], + }, + split: "----------------------------------|---------------------", + rows: [ + TableRow { + raw: "409 | Failure to move item", + columns: [ + "409", + "Failure to move item", + ], + }, + TableRow { + raw: "200 | All other scenarios", + columns: [ + "200", + "All other scenarios", + ], + }, + ], + }, + ), + ], + children: [], + }, + TokenTree { + title: Some( + "Get all items", + ), + content: [ + Text( + "", + ), + Text( + "Name: `items`", + ), + Text( + "", + ), + Text( + "Parameters:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "Parameter | Type | Description", + columns: [ + "Parameter", + "Type", + "Description", + ], + }, + split: "----------------------------------|---------|------------", + rows: [ + TableRow { + raw: "`withData` _optional_ | bool | True if you need current feed articles", + columns: [ + "withData _optional_", + "bool", + "True if you need current feed articles", + ], + }, + ], + }, + ), + Text( + "Returns all RSS items in JSON format, e.g.:", + ), + Text( + "", + ), + Text( + "```JSON", + ), + Text( + "{", + ), + Text( + " \"HD-Torrents.org\": \"https://hd-torrents.org/rss.php\",", + ), + Text( + " \"PowerfulJRE\": \"https://www.youtube.com/feeds/videos.xml?channel_id=UCzQUP1qoWDoEbmsQxvdjxgQ\",", + ), + Text( + " \"The Pirate Bay\": {", + ), + Text( + " \"Audio\": \"https://thepiratebay.org/rss//top100/100\",", + ), + Text( + " \"Video\": \"https://thepiratebay.org/rss//top100/200\"", + ), + Text( + " }", + ), + Text( + "}", + ), + Text( + "```", + ), + Text( + "", + ), + ], + children: [], + }, + TokenTree { + title: Some( + "Mark as read", + ), + content: [ + Text( + "", + ), + Text( + "If `articleId` is provided only the article is marked as read otherwise the whole feed is going to be marked as read.", + ), + Text( + "", + ), + Text( + "Name: `markAsRead`", + ), + Text( + "", + ), + Text( + "Parameters:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "Parameter | Type | Description", + columns: [ + "Parameter", + "Type", + "Description", + ], + }, + split: "----------------------------------|---------|------------", + rows: [ + TableRow { + raw: "`itemPath` | string | Current full path of item (e.g. \"The Pirate Bay\\Top100\")", + columns: [ + "itemPath", + "string", + "Current full path of item (e.g. \"The Pirate Bay\\Top100\")", + ], + }, + TableRow { + raw: "`articleId` _optional_ | string | ID of article", + columns: [ + "articleId _optional_", + "string", + "ID of article", + ], + }, + ], + }, + ), + Asterix( + "Returns:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "HTTP Status Code | Scenario", + columns: [ + "HTTP Status Code", + "Scenario", + ], + }, + split: "----------------------------------|---------------------", + rows: [ + TableRow { + raw: "200 | All scenarios", + columns: [ + "200", + "All scenarios", + ], + }, + ], + }, + ), + ], + children: [], + }, + TokenTree { + title: Some( + "Refresh item", + ), + content: [ + Text( + "", + ), + Text( + "Refreshes folder or feed.", + ), + Text( + "", + ), + Text( + "Name: `refreshItem`", + ), + Text( + "", + ), + Text( + "Parameters:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "Parameter | Type | Description", + columns: [ + "Parameter", + "Type", + "Description", + ], + }, + split: "----------------------------------|---------|------------", + rows: [ + TableRow { + raw: "`itemPath` | string | Current full path of item (e.g. \"The Pirate Bay\\Top100\")", + columns: [ + "itemPath", + "string", + "Current full path of item (e.g. \"The Pirate Bay\\Top100\")", + ], + }, + ], + }, + ), + Asterix( + "Returns:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "HTTP Status Code | Scenario", + columns: [ + "HTTP Status Code", + "Scenario", + ], + }, + split: "----------------------------------|---------------------", + rows: [ + TableRow { + raw: "200 | All scenarios", + columns: [ + "200", + "All scenarios", + ], + }, + ], + }, + ), + ], + children: [], + }, + TokenTree { + title: Some( + "Set auto-downloading rule", + ), + content: [ + Text( + "", + ), + Text( + "Name: `setRule`", + ), + Text( + "", + ), + Text( + "Parameters:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "Parameter | Type | Description", + columns: [ + "Parameter", + "Type", + "Description", + ], + }, + split: "----------------------------------|---------|------------", + rows: [ + TableRow { + raw: "`ruleName` | string | Rule name (e.g. \"Punisher\")", + columns: [ + "ruleName", + "string", + "Rule name (e.g. \"Punisher\")", + ], + }, + TableRow { + raw: "`ruleDef` | string | JSON encoded rule definition", + columns: [ + "ruleDef", + "string", + "JSON encoded rule definition", + ], + }, + ], + }, + ), + Text( + "Rule definition is JSON encoded dictionary with the following fields:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "Field | Type | Description", + columns: [ + "Field", + "Type", + "Description", + ], + }, + split: "----------------------------------|---------|------------", + rows: [ + TableRow { + raw: "`enabled` | bool | Whether the rule is enabled", + columns: [ + "enabled", + "bool", + "Whether the rule is enabled", + ], + }, + TableRow { + raw: "`mustContain` | string | The substring that the torrent name must contain", + columns: [ + "mustContain", + "string", + "The substring that the torrent name must contain", + ], + }, + TableRow { + raw: "`mustNotContain` | string | The substring that the torrent name must not contain", + columns: [ + "mustNotContain", + "string", + "The substring that the torrent name must not contain", + ], + }, + TableRow { + raw: "`useRegex` | bool | Enable regex mode in \"mustContain\" and \"mustNotContain\"", + columns: [ + "useRegex", + "bool", + "Enable regex mode in \"mustContain\" and \"mustNotContain\"", + ], + }, + TableRow { + raw: "`episodeFilter` | string | Episode filter definition", + columns: [ + "episodeFilter", + "string", + "Episode filter definition", + ], + }, + TableRow { + raw: "`smartFilter` | bool | Enable smart episode filter", + columns: [ + "smartFilter", + "bool", + "Enable smart episode filter", + ], + }, + TableRow { + raw: "`previouslyMatchedEpisodes` | list | The list of episode IDs already matched by smart filter", + columns: [ + "previouslyMatchedEpisodes", + "list", + "The list of episode IDs already matched by smart filter", + ], + }, + TableRow { + raw: "`affectedFeeds` | list | The feed URLs the rule applied to", + columns: [ + "affectedFeeds", + "list", + "The feed URLs the rule applied to", + ], + }, + TableRow { + raw: "`ignoreDays` | number | Ignore sunsequent rule matches", + columns: [ + "ignoreDays", + "number", + "Ignore sunsequent rule matches", + ], + }, + TableRow { + raw: "`lastMatch` | string | The rule last match time", + columns: [ + "lastMatch", + "string", + "The rule last match time", + ], + }, + TableRow { + raw: "`addPaused` | bool | Add matched torrent in paused mode", + columns: [ + "addPaused", + "bool", + "Add matched torrent in paused mode", + ], + }, + TableRow { + raw: "`assignedCategory` | string | Assign category to the torrent", + columns: [ + "assignedCategory", + "string", + "Assign category to the torrent", + ], + }, + TableRow { + raw: "`savePath` | string | Save torrent to the given directory", + columns: [ + "savePath", + "string", + "Save torrent to the given directory", + ], + }, + ], + }, + ), + Text( + "E.g.:", + ), + Text( + "", + ), + Text( + "```JSON", + ), + Text( + "{", + ), + Text( + " \"enabled\": false,", + ), + Text( + " \"mustContain\": \"The *Punisher*\",", + ), + Text( + " \"mustNotContain\": \"\",", + ), + Text( + " \"useRegex\": false,", + ), + Text( + " \"episodeFilter\": \"1x01-;\",", + ), + Text( + " \"smartFilter\": false,", + ), + Text( + " \"previouslyMatchedEpisodes\": [", + ), + Text( + " ],", + ), + Text( + " \"affectedFeeds\": [", + ), + Text( + " \"http://showrss.info/user/134567.rss?magnets=true\"", + ), + Text( + " ],", + ), + Text( + " \"ignoreDays\": 0,", + ), + Text( + " \"lastMatch\": \"20 Nov 2017 09:05:11\",", + ), + Text( + " \"addPaused\": true,", + ), + Text( + " \"assignedCategory\": \"\",", + ), + Text( + " \"savePath\": \"C:/Users/JohnDoe/Downloads/Punisher\"", + ), + Text( + "}", + ), + Text( + "```", + ), + Text( + "", + ), + ], + children: [], + }, + TokenTree { + title: Some( + "Rename auto-downloading rule", + ), + content: [ + Text( + "", + ), + Text( + "Name: `renameRule`", + ), + Text( + "", + ), + Text( + "Parameters:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "Parameter | Type | Description", + columns: [ + "Parameter", + "Type", + "Description", + ], + }, + split: "----------------------------------|---------|------------", + rows: [ + TableRow { + raw: "`ruleName` | string | Rule name (e.g. \"Punisher\")", + columns: [ + "ruleName", + "string", + "Rule name (e.g. \"Punisher\")", + ], + }, + TableRow { + raw: "`newRuleName` | string | New rule name (e.g. \"The Punisher\")", + columns: [ + "newRuleName", + "string", + "New rule name (e.g. \"The Punisher\")", + ], + }, + ], + }, + ), + Asterix( + "Returns:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "HTTP Status Code | Scenario", + columns: [ + "HTTP Status Code", + "Scenario", + ], + }, + split: "----------------------------------|---------------------", + rows: [ + TableRow { + raw: "200 | All scenarios", + columns: [ + "200", + "All scenarios", + ], + }, + ], + }, + ), + ], + children: [], + }, + TokenTree { + title: Some( + "Remove auto-downloading rule", + ), + content: [ + Text( + "", + ), + Text( + "Name: `removeRule`", + ), + Text( + "", + ), + Text( + "Parameters:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "Parameter | Type | Description", + columns: [ + "Parameter", + "Type", + "Description", + ], + }, + split: "----------------------------------|---------|------------", + rows: [ + TableRow { + raw: "`ruleName` | string | Rule name (e.g. \"Punisher\")", + columns: [ + "ruleName", + "string", + "Rule name (e.g. \"Punisher\")", + ], + }, + ], + }, + ), + Asterix( + "Returns:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "HTTP Status Code | Scenario", + columns: [ + "HTTP Status Code", + "Scenario", + ], + }, + split: "----------------------------------|---------------------", + rows: [ + TableRow { + raw: "200 | All scenarios", + columns: [ + "200", + "All scenarios", + ], + }, + ], + }, + ), + Text( + "", + ), + ], + children: [], + }, + TokenTree { + title: Some( + "Get all auto-downloading rules", + ), + content: [ + Text( + "", + ), + Text( + "Name: `rules`", + ), + Text( + "", + ), + Text( + "Returns all auto-downloading rules in JSON format, e.g.:", + ), + Text( + "", + ), + Text( + "```JSON", + ), + Text( + "{", + ), + Text( + " \"The Punisher\": {", + ), + Text( + " \"enabled\": false,", + ), + Text( + " \"mustContain\": \"The *Punisher*\",", + ), + Text( + " \"mustNotContain\": \"\",", + ), + Text( + " \"useRegex\": false,", + ), + Text( + " \"episodeFilter\": \"1x01-;\",", + ), + Text( + " \"smartFilter\": false,", + ), + Text( + " \"previouslyMatchedEpisodes\": [", + ), + Text( + " ],", + ), + Text( + " \"affectedFeeds\": [", + ), + Text( + " \"http://showrss.info/user/134567.rss?magnets=true\"", + ), + Text( + " ],", + ), + Text( + " \"ignoreDays\": 0,", + ), + Text( + " \"lastMatch\": \"20 Nov 2017 09:05:11\",", + ), + Text( + " \"addPaused\": true,", + ), + Text( + " \"assignedCategory\": \"\",", + ), + Text( + " \"savePath\": \"C:/Users/JohnDoe/Downloads/Punisher\"", + ), + Text( + " }", + ), + Text( + "}", + ), + Text( + "```", + ), + Asterix( + "Returns:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "HTTP Status Code | Scenario", + columns: [ + "HTTP Status Code", + "Scenario", + ], + }, + split: "----------------------------------|---------------------", + rows: [ + TableRow { + raw: "200 | All scenarios", + columns: [ + "200", + "All scenarios", + ], + }, + ], + }, + ), + ], + children: [], + }, + TokenTree { + title: Some( + "Get all articles matching a rule", + ), + content: [ + Text( + "", + ), + Text( + "Name: `matchingArticles`", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "Parameter | Type | Description", + columns: [ + "Parameter", + "Type", + "Description", + ], + }, + split: "----------------------------------|---------|------------", + rows: [ + TableRow { + raw: "`ruleName` | string | Rule name (e.g. \"Linux\")", + columns: [ + "ruleName", + "string", + "Rule name (e.g. \"Linux\")", + ], + }, + ], + }, + ), + Text( + "", + ), + Text( + "Returns all articles that match a rule by feed name in JSON format, e.g.:", + ), + Text( + "", + ), + Text( + "```JSON", + ), + Text( + "{", + ), + Text( + " \"DistroWatch\":[", + ), + Text( + " \"sparkylinux-5.11-i686-minimalgui.iso.torrent\",", + ), + Text( + " \"sparkylinux-5.11-x86_64-minimalgui.iso.torrent\",", + ), + Text( + " \"sparkylinux-5.11-i686-xfce.iso.torrent\",", + ), + Text( + " \"bluestar-linux-5.6.3-2020.04.09-x86_64.iso.torrent\",", + ), + Text( + " \"robolinux64-mate3d-v10.10.iso.torrent\",", + ), + Text( + " ],", + ), + Text( + " \"Linuxtracker\":[", + ), + Text( + " \"[Alpine Linux] alpine-extended-3.11.6\",", + ), + Text( + " \"[Alpine Linux] alpine-standard-3.11.6\",", + ), + Text( + " \"[Linuxfx] linuxfx10-wxs-lts-beta5.iso\",", + ), + Text( + " \"[Linux Lite] linux-lite-5.0-rc1-64bit.iso (MULTI)\",", + ), + Text( + " \"[Scientific Linux] SL-7.8-x86_64-Pack\",", + ), + Text( + " \"[NixOS] nixos-plasma5-20.03.1418.5272327b81e-x86_64-linux.iso\"", + ), + Text( + " ]", + ), + Text( + "}", + ), + Text( + "```", + ), + Text( + "", + ), + Asterix( + "Returns:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "HTTP Status Code | Scenario", + columns: [ + "HTTP Status Code", + "Scenario", + ], + }, + split: "----------------------------------|---------------------", + rows: [ + TableRow { + raw: "200 | All scenarios", + columns: [ + "200", + "All scenarios", + ], + }, + ], + }, + ), + Text( + "", + ), + ], + children: [], + }, + ], + }, + TokenTree { + title: Some( + "Search", + ), + content: [ + Text( + "", + ), + Text( + "All Search API methods are under \"search\", e.g.: `/api/v2/search/methodName`.", + ), + Text( + "", + ), + ], + children: [ + TokenTree { + title: Some( + "Start search", + ), + content: [ + Text( + "", + ), + Text( + "Name: `start`", + ), + Text( + "", + ), + Asterix( + "Parameters:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "Parameter | Type | Description", + columns: [ + "Parameter", + "Type", + "Description", + ], + }, + split: "----------------------------------|---------|------------", + rows: [ + TableRow { + raw: "`pattern` | string | Pattern to search for (e.g. \"Ubuntu 18.04\")", + columns: [ + "pattern", + "string", + "Pattern to search for (e.g. \"Ubuntu 18.04\")", + ], + }, + TableRow { + raw: "`plugins` | string | Plugins to use for searching (e.g. \"legittorrents\"). Supports multiple plugins separated by `\\|`. Also supports `all` and `enabled`", + columns: [ + "plugins", + "string", + "Plugins to use for searching (e.g. \"legittorrents\"). Supports multiple plugins separated by \\", + ". Also supports all and enabled", + ], + }, + TableRow { + raw: "`category` | string | Categories to limit your search to (e.g. \"legittorrents\"). Available categories depend on the specified `plugins`. Also supports `all`", + columns: [ + "category", + "string", + "Categories to limit your search to (e.g. \"legittorrents\"). Available categories depend on the specified plugins. Also supports all", + ], + }, + ], + }, + ), + Asterix( + "Returns:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "HTTP Status Code | Scenario", + columns: [ + "HTTP Status Code", + "Scenario", + ], + }, + split: "----------------------------------|---------------------", + rows: [ + TableRow { + raw: "409 | User has reached the limit of max `Running` searches (currently set to 5)", + columns: [ + "409", + "User has reached the limit of max Running searches (currently set to 5)", + ], + }, + TableRow { + raw: "200 | All other scenarios- see JSON below", + columns: [ + "200", + "All other scenarios- see JSON below", + ], + }, + ], + }, + ), + Text( + "The response is a JSON object with the following fields", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "Field | Type | Description", + columns: [ + "Field", + "Type", + "Description", + ], + }, + split: "----------------------------------|---------|------------", + rows: [ + TableRow { + raw: "`id` | number | ID of the search job", + columns: [ + "id", + "number", + "ID of the search job", + ], + }, + ], + }, + ), + Text( + "Example:", + ), + Text( + "", + ), + Text( + "```JSON", + ), + Text( + "{", + ), + Text( + " \"id\": 12345", + ), + Text( + "}", + ), + Text( + "```", + ), + Text( + "", + ), + ], + children: [], + }, + TokenTree { + title: Some( + "Stop search", + ), + content: [ + Text( + "", + ), + Text( + "Name: `stop`", + ), + Text( + "", + ), + Asterix( + "Parameters:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "Parameter | Type | Description", + columns: [ + "Parameter", + "Type", + "Description", + ], + }, + split: "----------------------------------|---------|------------", + rows: [ + TableRow { + raw: "`id` | number | ID of the search job", + columns: [ + "id", + "number", + "ID of the search job", + ], + }, + ], + }, + ), + Asterix( + "Returns:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "HTTP Status Code | Scenario", + columns: [ + "HTTP Status Code", + "Scenario", + ], + }, + split: "----------------------------------|---------------------", + rows: [ + TableRow { + raw: "404 | Search job was not found", + columns: [ + "404", + "Search job was not found", + ], + }, + TableRow { + raw: "200 | All other scenarios", + columns: [ + "200", + "All other scenarios", + ], + }, + ], + }, + ), + ], + children: [], + }, + TokenTree { + title: Some( + "Get search status", + ), + content: [ + Text( + "", + ), + Text( + "Name: `status`", + ), + Text( + "", + ), + Asterix( + "Parameters:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "Parameter | Type | Description", + columns: [ + "Parameter", + "Type", + "Description", + ], + }, + split: "----------------------------------|---------|------------", + rows: [ + TableRow { + raw: "`id` _optional_ | number | ID of the search job. If not specified, all search jobs are returned", + columns: [ + "id _optional_", + "number", + "ID of the search job. If not specified, all search jobs are returned", + ], + }, + ], + }, + ), + Asterix( + "Returns:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "HTTP Status Code | Scenario", + columns: [ + "HTTP Status Code", + "Scenario", + ], + }, + split: "----------------------------------|---------------------", + rows: [ + TableRow { + raw: "404 | Search job was not found", + columns: [ + "404", + "Search job was not found", + ], + }, + TableRow { + raw: "200 | All other scenarios- see JSON below", + columns: [ + "200", + "All other scenarios- see JSON below", + ], + }, + ], + }, + ), + Text( + "The response is a JSON array of objects containing the following fields", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "Field | Type | Description", + columns: [ + "Field", + "Type", + "Description", + ], + }, + split: "----------------------------------|---------|------------", + rows: [ + TableRow { + raw: "`id` | number | ID of the search job", + columns: [ + "id", + "number", + "ID of the search job", + ], + }, + TableRow { + raw: "`status` | string | Current status of the search job (either `Running` or `Stopped`)", + columns: [ + "status", + "string", + "Current status of the search job (either Running or Stopped)", + ], + }, + TableRow { + raw: "`total` | number | Total number of results. If the status is `Running` this number may contineu to increase", + columns: [ + "total", + "number", + "Total number of results. If the status is Running this number may contineu to increase", + ], + }, + ], + }, + ), + Text( + "Example:", + ), + Text( + "", + ), + Text( + "```JSON", + ), + Text( + "[", + ), + Text( + " {", + ), + Text( + " \"id\": 12345,", + ), + Text( + " \"status\": \"Running\",", + ), + Text( + " \"total\": 170", + ), + Text( + " }", + ), + Text( + "]", + ), + Text( + "```", + ), + Text( + "", + ), + ], + children: [], + }, + TokenTree { + title: Some( + "Get search results", + ), + content: [ + Text( + "", + ), + Text( + "Name: `results`", + ), + Text( + "", + ), + Asterix( + "Parameters:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "Parameter | Type | Description", + columns: [ + "Parameter", + "Type", + "Description", + ], + }, + split: "----------------------------------|---------|------------", + rows: [ + TableRow { + raw: "`id` | number | ID of the search job", + columns: [ + "id", + "number", + "ID of the search job", + ], + }, + TableRow { + raw: "`limit` _optional_ | number | max number of results to return. 0 or negative means no limit", + columns: [ + "limit _optional_", + "number", + "max number of results to return. 0 or negative means no limit", + ], + }, + TableRow { + raw: "`offset` _optional_ | number | result to start at. A negative number means count backwards (e.g. `-2` returns the 2 most recent results)", + columns: [ + "offset _optional_", + "number", + "result to start at. A negative number means count backwards (e.g. -2 returns the 2 most recent results)", + ], + }, + ], + }, + ), + Asterix( + "Returns:", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "HTTP Status Code | Scenario", + columns: [ + "HTTP Status Code", + "Scenario", + ], + }, + split: "----------------------------------|---------------------", + rows: [ + TableRow { + raw: "404 | Search job was not found", + columns: [ + "404", + "Search job was not found", + ], + }, + TableRow { + raw: "409 | Offset is too large, or too small (e.g. absolute value of negative number is greater than # results)", + columns: [ + "409", + "Offset is too large, or too small (e.g. absolute value of negative number is greater than # results)", + ], + }, + TableRow { + raw: "200 | All other scenarios- see JSON below", + columns: [ + "200", + "All other scenarios- see JSON below", + ], + }, + ], + }, + ), + Text( + "The response is a JSON object with the following fields", + ), + Text( + "", + ), + Table( + Table { + header: TableRow { + raw: "Field | Type | Description", + columns: [ + "Field", + "Type", + "Description", + ], + }, + split: "----------------------------------|---------|------------", + rows: [], + }, + ), + ], + children: [], + }, + ], + }, + ], +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..5cac0f1 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,4 @@ +use api_gen::QBittorrentApiGen; + +#[derive(QBittorrentApiGen)] +pub struct Api;