This commit is contained in:
red 2020-04-20 10:38:22 +02:00
parent ac3cefea36
commit baad21f277
8 changed files with 191 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
/target
Cargo.lock
.env

24
Cargo.toml Normal file
View File

@ -0,0 +1,24 @@
[package]
name = "transmission-rpc"
version = "0.1.0"
authors = ["red <red.avtovo@gmail.com>"]
edition = "2018"
[dependencies]
dotenv = "0.15.0"
reqwest = { version = "0.10.4", features = ["json", "rustls-tls"] }
tokio = { version = "0.2", features = ["full"] }
tokio-postgres = "0.5.2"
serde = { version = "1.0", features = ["derive"] }
futures = "0.3.3"
serde_json = "1.0"
ajson = "0.2"
log = "0.4.8"
env_logger = "0.7.1"
bb8-postgres = "0.4.0"
bb8 = "0.4.1"
rustc-serialize = "0.3.24"

19
README.md Normal file
View File

@ -0,0 +1,19 @@
Library to communicate with transmission rpc
spec: https://github.com/transmission/transmission/blob/master/extras/rpc-spec.txt
Supported Methods:
- [ ] torrent-set
- [ ] torrent-get
- [ ] torrent-add
- [ ] torrent-remove
- [ ] torrent-set-location
- [ ] torrent-rename-path
- [ ] session-set
- [X] session-get
- [ ] session-stats
- [ ] blocklist-update
- [ ] port-test
- [ ] session-close
- [ ] free-space

109
src/main.rs Normal file
View File

@ -0,0 +1,109 @@
#[allow(unused_imports)]
#[allow(dead_code)]
extern crate ajson;
extern crate bb8;
extern crate bb8_postgres;
extern crate env_logger;
#[macro_use]
extern crate log;
extern crate reqwest;
extern crate tokio_postgres;
use std::env;
use dotenv::dotenv;
use std::collections::HashMap;
use reqwest::header::CONTENT_TYPE;
pub type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
#[derive(Debug)]
struct BasicAuth {
user: String,
password: String,
}
mod models;
use models::request::SessionGet;
use models::response::RpcResponse;
use models::entity::SessionInfo;
#[tokio::main]
async fn main() -> Result<()> {
not_main().await
}
async fn not_main() -> Result<()> {
dotenv().ok();
env_logger::init();
let url= env::var("URL")?;
let basic_auth = Some(BasicAuth{user: env::var("TUSER")?, password: env::var("TPWD")?});
info!("Loaded auth: {:?}", &basic_auth);
let rq: reqwest::RequestBuilder = rpc_request(url.as_ref(), &basic_auth)
.header("X-Transmission-Session-Id", get_session_id(url.as_ref(), &basic_auth).await)
.json(&SessionGet::default());
debug!("Request body: {:?}", rq.try_clone().unwrap().body_string()?);
// let p2 = Point{ x: 34, ..Default::default() };
let resp: reqwest::Response = rq.send().await?;
// print!("{:?}", resp.text().await);
let rpc_response: RpcResponse<SessionInfo> = resp.json().await?;
info!("{:#?}", rpc_response);
Ok(())
}
trait BodyString {
fn body_string(self) -> Result<String>;
}
impl BodyString for reqwest::RequestBuilder {
fn body_string(self) -> Result<String> {
let rq = self.build()?;
let body = rq.body().unwrap().as_bytes().unwrap();
Ok(std::str::from_utf8(body)?.to_string())
}
}
fn rpc_request(url: &str, basic_auth: &Option<BasicAuth>) -> reqwest::RequestBuilder {
let client = reqwest::Client::new();
if let Some(auth) = basic_auth {
client.post(url.clone())
.basic_auth(&auth.user, Some(&auth.password))
} else {
client.post(url.clone())
}
.header(CONTENT_TYPE, "application/json")
}
async fn get_session_id(url: &str, basic_auth: &Option<BasicAuth>) -> String {
let mut map = HashMap::new();
map.insert("method", "session-get");
info!("Requesting session id info");
let response: reqwest::Response = rpc_request(url, basic_auth)
.json(&map)
.send()
.await
.unwrap();
let session_id = response.headers()
.get("x-transmission-session-id")
.expect("Unable to get session id")
.to_str()
.unwrap()
.to_owned();
info!("Received session id: {}", session_id);
session_id
}
#[cfg(test)]
mod tests {
use crate::not_main;
use crate::Result;
#[tokio::test]
async fn it_works() -> Result<()> {
dotenv().ok();
not_main().await
}
}

14
src/models/entity.rs Normal file
View File

@ -0,0 +1,14 @@
use serde::Deserialize;
#[derive(Deserialize, Debug)]
pub struct SessionInfo {
#[serde(rename="blocklist-enabled")]
blocklist_enabled: bool,
#[serde(rename="download-dir")]
download_dir: String,
encryption: String,
#[serde(rename="rpc-version")]
rpc_version: i32,
#[serde(rename="rpc-version-minimum")]
rpc_version_minimum: i32,
version: String,
}

4
src/models/mod.rs Normal file
View File

@ -0,0 +1,4 @@
pub mod request;
pub mod response;
pub mod entity;

12
src/models/request.rs Normal file
View File

@ -0,0 +1,12 @@
use serde::Serialize;
#[derive(Serialize, Debug, RustcEncodable)]
pub struct SessionGet {
method: String
}
impl Default for SessionGet{
fn default() -> SessionGet {
SessionGet { method: String::from("session-get") }
}
}

6
src/models/response.rs Normal file
View File

@ -0,0 +1,6 @@
use serde::Deserialize;
#[derive(Deserialize, Debug)]
pub struct RpcResponse<T> {
arguments: T,
result: String
}