Skip to content
Merged
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
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ pkg-fmt = "txz" # Changed from "tgz" to match .tar.xz files
pkg-fmt = "zip"

[dependencies]
async-trait = "0.1"
chrono = "0.4"
librqbit = { version = "8.1.1", default-features = false, features = ["http-api", "tracing-subscriber-utils", "rust-tls"] }
ratatui = "0.30.0"
Expand Down
57 changes: 49 additions & 8 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ pub struct Config {
#[serde(default)]
pub extensions: ExtensionsConfig,
#[serde(default)]
pub providers: ProvidersConfig,
#[serde(default)]
pub scrobblers: ScrobblersConfig,
#[serde(default)]
pub subtitles: SubtitlesConfig,
#[serde(default)]
pub streaming: StreamingConfig,
Expand All @@ -37,8 +41,25 @@ pub struct Config {
pub struct ExtensionsConfig {
#[serde(default)]
pub discord: DiscordConfig,
}

/// Content discovery providers configuration
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
pub struct ProvidersConfig {
/// Which provider to use for discovery ("trakt", "simkl", or omit for none)
#[serde(default, skip_serializing_if = "Option::is_none")]
pub discovery: Option<String>,
#[serde(default)]
pub trakt: TraktConfig,
// Future: pub simkl: SimklConfig,
}

/// Watch history scrobblers configuration
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
pub struct ScrobblersConfig {
/// List of enabled scrobblers (e.g., ["trakt", "simkl"])
#[serde(default)]
pub enabled: Vec<String>,
}

#[derive(Debug, Clone, Default, Deserialize, Serialize)]
Expand All @@ -49,10 +70,9 @@ pub struct DiscordConfig {
pub app_id: Option<String>,
}

/// Trakt.tv configuration (used by both provider and scrobbler)
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
pub struct TraktConfig {
#[serde(default)]
pub enabled: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub client_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
Expand Down Expand Up @@ -277,6 +297,8 @@ impl Default for Config {
player: PlayerConfig::default(),
storage: StorageConfig::default(),
extensions: ExtensionsConfig::default(),
providers: ProvidersConfig::default(),
scrobblers: ScrobblersConfig::default(),
subtitles: SubtitlesConfig::default(),
streaming: StreamingConfig::default(),
}
Expand Down Expand Up @@ -459,8 +481,9 @@ mod tests {
assert_eq!(config.streaming.auto_race, 10);
assert!(config.extensions.discord.app_id.is_none());
assert!(!config.extensions.discord.enabled);
assert!(config.extensions.trakt.client_id.is_none());
assert!(!config.extensions.trakt.enabled);
assert!(config.providers.discovery.is_none());
assert!(config.providers.trakt.client_id.is_none());
assert!(config.scrobblers.enabled.is_empty());
}

#[test]
Expand Down Expand Up @@ -527,13 +550,18 @@ mod tests {
enabled: true,
app_id: Some("discord-id".to_string()),
},
},
providers: ProvidersConfig {
discovery: Some("trakt".to_string()),
trakt: TraktConfig {
enabled: true,
client_id: Some("trakt-id".to_string()),
access_token: Some("trakt-token".to_string()),
..Default::default()
},
},
scrobblers: ScrobblersConfig {
enabled: vec!["trakt".to_string()],
},
};

let toml_str = toml::to_string(&config).unwrap();
Expand All @@ -551,16 +579,29 @@ mod tests {
assert_eq!(parsed.subtitles.language, config.subtitles.language);
assert_eq!(parsed.streaming.auto_race, config.streaming.auto_race);
assert_eq!(parsed.storage.temp_dir, config.storage.temp_dir);
assert_eq!(parsed.providers.discovery, config.providers.discovery);
assert_eq!(parsed.scrobblers.enabled, config.scrobblers.enabled);
}

#[test]
fn test_extensions_config_default() {
let extensions = ExtensionsConfig::default();
assert!(!extensions.discord.enabled);
assert!(extensions.discord.app_id.is_none());
assert!(!extensions.trakt.enabled);
assert!(extensions.trakt.client_id.is_none());
assert!(extensions.trakt.access_token.is_none());
}

#[test]
fn test_providers_config_default() {
let providers = ProvidersConfig::default();
assert!(providers.discovery.is_none());
assert!(providers.trakt.client_id.is_none());
assert!(providers.trakt.access_token.is_none());
}

#[test]
fn test_scrobblers_config_default() {
let scrobblers = ScrobblersConfig::default();
assert!(scrobblers.enabled.is_empty());
}

#[test]
Expand Down
46 changes: 30 additions & 16 deletions src/doctor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,10 @@ pub async fn run_checks(config: &Config) -> Vec<CheckResult> {
results.push(check_discord(config));
}

if config.extensions.trakt.enabled {
// Check if Trakt is enabled (for discovery or scrobbling)
let trakt_enabled = config.providers.discovery.as_deref() == Some("trakt")
|| config.scrobblers.enabled.contains(&"trakt".to_string());
if trakt_enabled {
results.push(check_trakt(config).await);
}

Expand Down Expand Up @@ -131,9 +134,9 @@ fn check_discord(config: &Config) -> CheckResult {
}

async fn check_trakt(config: &Config) -> CheckResult {
let trakt = &config.extensions.trakt;
let trakt = &config.providers.trakt;

if trakt.client_id.is_none() {
if trakt.get_client_id().is_none() {
return CheckResult::error("Trakt", "Enabled but no client_id configured");
}

Expand Down Expand Up @@ -227,8 +230,8 @@ pub fn print_results(results: &[CheckResult]) {
mod tests {
use super::*;
use crate::config::{
DiscordConfig, ExtensionsConfig, PlayerConfig, ProwlarrConfig, StorageConfig,
StreamingConfig, SubtitlesConfig, TmdbConfig, TraktConfig,
DiscordConfig, ExtensionsConfig, PlayerConfig, ProvidersConfig, ProwlarrConfig,
ScrobblersConfig, StorageConfig, StreamingConfig, SubtitlesConfig, TmdbConfig, TraktConfig,
};

#[test]
Expand Down Expand Up @@ -296,8 +299,9 @@ mod tests {
enabled: true,
app_id: Some("123456".to_string()),
},
trakt: TraktConfig::default(),
},
providers: ProvidersConfig::default(),
scrobblers: ScrobblersConfig::default(),
};

let result = check_discord(&config);
Expand All @@ -322,8 +326,9 @@ mod tests {
enabled: true,
app_id: None,
},
trakt: TraktConfig::default(),
},
providers: ProvidersConfig::default(),
scrobblers: ScrobblersConfig::default(),
};

let result = check_discord(&config);
Expand All @@ -343,15 +348,16 @@ mod tests {
storage: StorageConfig::default(),
subtitles: SubtitlesConfig::default(),
streaming: StreamingConfig::default(),
extensions: ExtensionsConfig {
discord: DiscordConfig::default(),
extensions: ExtensionsConfig::default(),
providers: ProvidersConfig {
discovery: Some("trakt".to_string()),
trakt: TraktConfig {
enabled: true,
client_id: None,
access_token: None,
..Default::default()
},
},
scrobblers: ScrobblersConfig::default(),
};

let result = check_trakt(&config).await;
Expand All @@ -371,15 +377,16 @@ mod tests {
storage: StorageConfig::default(),
subtitles: SubtitlesConfig::default(),
streaming: StreamingConfig::default(),
extensions: ExtensionsConfig {
discord: DiscordConfig::default(),
extensions: ExtensionsConfig::default(),
providers: ProvidersConfig {
discovery: Some("trakt".to_string()),
trakt: TraktConfig {
enabled: true,
client_id: Some("client123".to_string()),
access_token: None,
..Default::default()
},
},
scrobblers: ScrobblersConfig::default(),
};

let result = check_trakt(&config).await;
Expand All @@ -399,15 +406,16 @@ mod tests {
storage: StorageConfig::default(),
subtitles: SubtitlesConfig::default(),
streaming: StreamingConfig::default(),
extensions: ExtensionsConfig {
discord: DiscordConfig::default(),
extensions: ExtensionsConfig::default(),
providers: ProvidersConfig {
discovery: Some("trakt".to_string()),
trakt: TraktConfig {
enabled: true,
client_id: Some("client123".to_string()),
access_token: Some("token456".to_string()),
..Default::default()
},
},
scrobblers: ScrobblersConfig::default(),
};

let result = check_trakt(&config).await;
Expand All @@ -431,6 +439,8 @@ mod tests {
subtitles: SubtitlesConfig::default(),
streaming: StreamingConfig::default(),
extensions: ExtensionsConfig::default(),
providers: ProvidersConfig::default(),
scrobblers: ScrobblersConfig::default(),
};

let result = check_player(&config);
Expand Down Expand Up @@ -461,6 +471,8 @@ mod tests {
subtitles: SubtitlesConfig::default(),
streaming: StreamingConfig::default(),
extensions: ExtensionsConfig::default(),
providers: ProvidersConfig::default(),
scrobblers: ScrobblersConfig::default(),
};

let result = check_storage(&config);
Expand Down Expand Up @@ -494,6 +506,8 @@ mod tests {
subtitles: SubtitlesConfig::default(),
streaming: StreamingConfig::default(),
extensions: ExtensionsConfig::default(),
providers: ProvidersConfig::default(),
scrobblers: ScrobblersConfig::default(),
};

let result = check_storage(&config);
Expand Down
2 changes: 0 additions & 2 deletions src/extensions/mod.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
pub mod discord;
pub mod trakt;

pub use discord::DiscordExtension;
pub use trakt::TraktExtension;

use once_cell::sync::Lazy;
use regex::Regex;
Expand Down
34 changes: 25 additions & 9 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,17 @@ mod doctor;
mod extensions;
mod history;
mod opensubtitles;
mod providers;
mod prowlarr;
mod scrobblers;
mod streaming;
mod tmdb;
mod torznab;
mod trakt;
mod tui;

use config::Config;
use extensions::{DiscordExtension, ExtensionManager, TraktExtension};
use extensions::{DiscordExtension, ExtensionManager};
use scrobblers::ScrobblerManager;
use std::fs::File;
use tracing_subscriber::EnvFilter;

Expand Down Expand Up @@ -66,7 +68,7 @@ async fn main() {
}
};

// Initialize extensions
// Initialize extensions (Discord RPC, etc.)
let mut ext_manager = ExtensionManager::new();

if config.extensions.discord.enabled {
Expand All @@ -75,14 +77,28 @@ async fn main() {
)));
}

if config.extensions.trakt.enabled {
ext_manager.register(Box::new(TraktExtension::new(
config.extensions.trakt.client_id.clone(),
config.extensions.trakt.access_token.clone(),
)));
// Initialize scrobblers (Trakt, SIMKL, etc.)
let mut scrobbler_manager = ScrobblerManager::new();

for scrobbler_name in &config.scrobblers.enabled {
match scrobbler_name.as_str() {
"trakt" => {
if let Some(scrobbler) = scrobblers::create_scrobbler(
"trakt",
config.providers.trakt.get_client_id().map(String::from),
config.providers.trakt.access_token.clone(),
) {
scrobbler_manager.register(scrobbler);
}
}
// Future: "simkl" => { ... }
other => {
tracing::warn!(scrobbler = other, "unknown scrobbler, skipping");
}
}
}

let result = tui::run(config, ext_manager, is_new).await;
let result = tui::run(config, ext_manager, scrobbler_manager, is_new).await;

if let Err(e) = result {
eprintln!("Error: {}", e);
Expand Down
Loading