Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "putioarr"
description = "put.io to sonarr/radarr/whisparr proxy"
description = "put.io to sonarr/radarr/whisparr/lidarr proxy"
authors = ["Wouter de Bie <wouter@evenflow.nl"]
repository = "https://github.com/wouterdebie/putioarr"
license = "MIT"
Expand Down
16 changes: 8 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# putioarr

Proxy that allows put.io to be used as a download client for sonarr/radarr/whisparr. The proxy uses the Transmission protocol.
Proxy that allows put.io to be used as a download client for sonarr/radarr/whisparr/lidarr. The proxy uses the Transmission protocol.

## Installation

Expand All @@ -14,10 +14,10 @@ Make sure you have a [proper rust installation](https://www.rust-lang.org/tools/

First, generate a config using `putio generate-config`. This will generate a config file in `~/.config/putioarr/config.toml`. Use `-c` to override the configuration file location.

Edit the configuration file and make sure you configure the username and password, as well as the sonarr/radarr/whisparr details.
Edit the configuration file and make sure you configure the username and password, as well as the sonarr/radarr/whisparr/lidarr details.

- Run the proxy:`putioarr run`
- Configure the Transmission download client in sonarr/radarr/whisparr:
- Configure the Transmission download client in sonarr/radarr/whisparr/lidarr:
- Url Base: /transmission
- Username: <configured username>
- Password: <configured password>
Expand All @@ -29,7 +29,7 @@ Docker images are based on [linuxserver.io](https://linuxserver.io) images.

#### Usage

The first time you run your docker container, run it without the `-d` option, since you'll need a put.io API key. When no configuration is found, it will present you a link and a code that will generate an API key. After the key is generated, putioarr will write a default config in your config volume (see `docker compose` and `docker cli` below). Modify the config (like username, password and sonarr/radarr/whisparr configuration) in order to properly use putioarr.
The first time you run your docker container, run it without the `-d` option, since you'll need a put.io API key. When no configuration is found, it will present you a link and a code that will generate an API key. After the key is generated, putioarr will write a default config in your config volume (see `docker compose` and `docker cli` below). Modify the config (like username, password and sonarr/radarr/whisparr/lidarr configuration) in order to properly use putioarr.

#### Supported Architectures

Expand Down Expand Up @@ -88,7 +88,7 @@ Container images are configured using parameters passed at runtime (such as thos


## Behavior
The proxy will upload torrents or magnet links to put.io. It will then continue to monitor transfers. When a transfer is completed, all files belonging to the transfer will be downloaded to the specified download directory. The proxy will remove the files after sonarr/radarr/whisparr has imported them and put.io is done seeding. The proxy will skip directories named "Sample".
The proxy will upload torrents or magnet links to put.io. It will then continue to monitor transfers. When a transfer is completed, all files belonging to the transfer will be downloaded to the specified download directory. The proxy will remove the files after sonarr/radarr/whisparr/lidarr has imported them and put.io is done seeding. The proxy will skip directories named "Sample".

## Configuration
A configuration file can be specified using `-c`, but the default configuration file location is:
Expand All @@ -97,12 +97,12 @@ A configuration file can be specified using `-c`, but the default configuration

TOML is used as the configuration format:
```
# Required. Username and password that sonarr/radarr/whisparr use to connect to the proxy
# Required. Username and password that sonarr/radarr/whisparr/lidarr use to connect to the proxy
username = "myusername"
password = "mypassword"

# Required. Directory where the proxy will download files to. This directory has to be readable by
# sonarr/radarr/whisparr in order to import downloads
# sonarr/radarr/whisparr/lidarr in order to import downloads
download_directory = "/path/to/downloads"

# Optional bind address, default "0.0.0.0"
Expand Down Expand Up @@ -150,7 +150,7 @@ api_key = "MYRADARRAPIKEY"
- Better Error handling and retry behavior
- The session ID provided is hard coded. Not sure if it matters.
- (Add option to not delete downloads)
- Figure out a better way to map a transfer to a completed import. Since a transfer can contain multiple files (e.g. a whole season) we currently check if all video files have been imported. Most of the time this is fine, except when there are sample videos. sonarr/radarr/whisparr will not import samples, but will make no mention of the fact that the sample was skipped. Right now we check against the `skip_directories` list, which works, but might be tedious.
- Figure out a better way to map a transfer to a completed import. Since a transfer can contain multiple files (e.g. a whole season) we currently check if all video files have been imported. Most of the time this is fine, except when there are sample videos. sonarr/radarr/whisparr/lidarr will not import samples, but will make no mention of the fact that the sample was skipped. Right now we check against the `skip_directories` list, which works, but might be tedious.
- Automatically pick the right putio proxy based on speed

## Thanks
Expand Down
5 changes: 5 additions & 0 deletions root/defaults/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,8 @@ api_key = ""
# url = "http://mywhisparrhost:6969/radarr"
# Can be found in Radarr: Settings -> General
# api_key = "MYWHISPARRAPIKEY"

# [lidarr]
# url = "http://mylidarrhost:6969/lidarr"
# Can be found in Lidarr: Settings -> General
# api_key = "MYLIDARRAPIKEY"
57 changes: 34 additions & 23 deletions src/download_system/transfer.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::{
services::{
arr,
arr::ArrApp,
putio::{self, PutIOTransfer},
},
AppData,
Expand All @@ -10,7 +10,7 @@ use anyhow::Result;
use async_channel::Sender;
use async_recursion::async_recursion;
use colored::*;
use log::{error, info, warn};
use log::{debug, error, info, warn};
use serde::{Deserialize, Serialize};
use std::{fmt::Display, path::Path};
use tokio::time::sleep;
Expand All @@ -28,16 +28,7 @@ pub struct Transfer {
impl Transfer {
pub async fn is_imported(&self) -> bool {
let targets = self.targets.as_ref().unwrap().clone();
let mut check_services = Vec::<(&str, String, String)>::new();
if let Some(a) = &self.app_data.config.sonarr {
check_services.push(("Sonarr", a.url.clone(), a.api_key.clone()))
}
if let Some(a) = &self.app_data.config.radarr {
check_services.push(("Radarr", a.url.clone(), a.api_key.clone()))
}
if let Some(a) = &self.app_data.config.whisparr {
check_services.push(("Whisparr", a.url.clone(), a.api_key.clone()))
}
let apps = ArrApp::from_config(&self.app_data.config);

let targets = targets
.into_iter()
Expand All @@ -49,24 +40,19 @@ impl Transfer {
let mut results = Vec::<bool>::new();
for target in targets {
let mut service_results = vec![];
for (service_name, url, key) in &check_services {
let service_result = match arr::check_imported(&target.to, key, url).await {
for app in &apps {
let service_result = match app.check_imported(&target).await {
Ok(r) => r,
Err(e) => {
error!("Error retrieving history from {}: {}", service_name, e);
error!("Error retrieving history from {}: {}", app, e);
false
}
};
if service_result {
info!(
"{}: found imported by {}",
&target,
service_name.bright_blue()
);
info!("{}: found imported by {}", &target, app);
}
service_results.push(service_result)
}
// Check if ANY of the service_results are true and put the outcome in results
results.push(service_results.into_iter().any(|x| x));
}
// Check if all targets have been imported
Expand Down Expand Up @@ -143,6 +129,7 @@ async fn recurse_download_targets(
to,
top_level,
transfer_hash: hash.to_string(),
media_type: None,
});

for file in response.files {
Expand All @@ -159,7 +146,7 @@ async fn recurse_download_targets(
}
}
}
"VIDEO" => {
"VIDEO" | "AUDIO" => {
// Get download URL for file
let url = putio::url(&app_data.config.putio.api_key, response.parent.id).await?;
targets.push(DownloadTarget {
Expand All @@ -168,9 +155,16 @@ async fn recurse_download_targets(
to,
top_level,
transfer_hash: hash.to_string(),
media_type: MediaType::from_file_type_str(response.parent.file_type.as_str()),
});
}
_ => {}
_ => {
debug!(
"{}: skipping filetype {}",
response.parent.name,
response.parent.file_type.as_str()
);
}
}

Ok(targets)
Expand All @@ -183,13 +177,30 @@ pub enum TransferMessage {
Imported(Transfer),
}

#[derive(PartialEq, Debug, Serialize, Deserialize, Clone)]
pub enum MediaType {
Audio,
Video,
}

impl MediaType {
pub fn from_file_type_str(file_type: &str) -> Option<Self> {
match file_type {
"AUDIO" => Some(Self::Audio),
"VIDEO" => Some(Self::Video),
_ => None,
}
}
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct DownloadTarget {
pub from: Option<String>,
pub to: String,
pub target_type: TargetType,
pub top_level: bool,
pub transfer_hash: String,
pub media_type: Option<MediaType>,
}

impl Display for DownloadTarget {
Expand Down
15 changes: 5 additions & 10 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{http::routes, services::putio};
use crate::{http::routes, services::arr, services::putio};
use actix_web::{web, App, HttpServer};
use anyhow::{bail, Context, Result};
use clap::{Parser, Subcommand};
Expand Down Expand Up @@ -55,22 +55,17 @@ pub struct Config {
uid: u32,
username: String,
putio: PutioConfig,
sonarr: Option<ArrConfig>,
radarr: Option<ArrConfig>,
whisparr: Option<ArrConfig>,
sonarr: Option<arr::ArrConfig>,
radarr: Option<arr::ArrConfig>,
whisparr: Option<arr::ArrConfig>,
lidarr: Option<arr::ArrConfig>,
}

#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct PutioConfig {
api_key: String,
}

#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct ArrConfig {
url: String,
api_key: String,
}

pub struct AppData {
pub config: Config,
}
Expand Down
Loading