Clean up output

This commit is contained in:
Marc Plano-Lesay 2025-05-01 16:08:49 +10:00
parent 774a5ed4ac
commit 1a11a18d05
Signed by: kernald
GPG key ID: 66A41B08CC62A6CF
7 changed files with 224 additions and 77 deletions

38
Cargo.lock generated
View file

@ -91,6 +91,17 @@ dependencies = [
"windows-sys 0.59.0",
]
[[package]]
name = "async-trait"
version = "0.1.88"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "atomic"
version = "0.6.0"
@ -282,6 +293,19 @@ version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
[[package]]
name = "console"
version = "0.15.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "054ccb5b10f9f2cbf51eb355ca1d05c2d279ce1804688d0db74b4733a5aeafd8"
dependencies = [
"encode_unicode",
"libc",
"once_cell",
"unicode-width",
"windows-sys 0.59.0",
]
[[package]]
name = "core-foundation"
version = "0.9.4"
@ -439,6 +463,12 @@ version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
[[package]]
name = "encode_unicode"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0"
[[package]]
name = "encoding_rs"
version = "0.8.35"
@ -1496,10 +1526,12 @@ dependencies = [
name = "reddit-magnet"
version = "0.1.0"
dependencies = [
"async-trait",
"chrono",
"clap",
"clap-verbosity-flag",
"color-eyre",
"console",
"diesel",
"diesel_migrations",
"directories",
@ -2281,6 +2313,12 @@ version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
[[package]]
name = "unicode-width"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd"
[[package]]
name = "untrusted"
version = "0.9.0"

View file

@ -27,3 +27,5 @@ transmission-rpc = "0.5.0"
url = "2.5.4"
clap-verbosity-flag = "3.0.2"
pretty_env_logger = "0.5.0"
async-trait = "0.1.77"
console = "0.15.8"

22
src/actions/action.rs Normal file
View file

@ -0,0 +1,22 @@
use crate::models::Magnet;
use async_trait::async_trait;
use color_eyre::eyre::Result;
/// Struct to hold the list of processed and failed magnets
pub struct ProcessedMagnets {
pub success: Vec<Magnet>,
pub failed: Vec<Magnet>,
}
/// Trait for actions that process magnet links
#[async_trait]
pub trait Action {
/// Return the name of the action
fn name(&self) -> &str;
/// Process all unprocessed magnet links and return the list of processed magnets
async fn process_unprocessed_magnets(
&mut self,
db: &mut crate::db::Database,
) -> Result<ProcessedMagnets>;
}

View file

@ -1 +1,2 @@
pub mod action;
pub mod transmission;

View file

@ -1,51 +1,63 @@
use crate::actions::action::{Action, ProcessedMagnets};
use crate::actions::transmission::client::TransmissionClient;
use crate::actions::transmission::config::TransmissionConfig;
use crate::db::{Database, TransmissionProcessedTable};
use color_eyre::eyre::Result;
use log::{debug, info, warn};
use log::{debug, warn};
/// Action for submitting magnet links to Transmission
pub struct TransmissionAction {
client: TransmissionClient,
db: Database,
}
impl TransmissionAction {
pub async fn new(config: &TransmissionConfig, db: Database) -> Result<Self> {
pub async fn new(config: &TransmissionConfig) -> Result<Self> {
let client = TransmissionClient::new(config)?;
Ok(TransmissionAction { client, db })
Ok(TransmissionAction { client })
}
}
#[async_trait::async_trait]
impl Action for TransmissionAction {
/// Return the name of the action
fn name(&self) -> &str {
"Transmission"
}
/// Process all unprocessed magnet links
pub async fn process_unprocessed_magnets(&mut self) -> Result<usize> {
let unprocessed_magnets = self
.db
.get_unprocessed_magnets_for_table::<TransmissionProcessedTable>()?;
let mut processed_count = 0;
/// Process all unprocessed magnet links and return the list of processed magnets
async fn process_unprocessed_magnets(&mut self, db: &mut Database) -> Result<ProcessedMagnets> {
let unprocessed_magnets =
db.get_unprocessed_magnets_for_table::<TransmissionProcessedTable>()?;
let mut processed_magnets = Vec::new();
let mut failed_magnets = Vec::new();
for magnet in unprocessed_magnets {
if let Some(id) = magnet.id {
match self.client.submit_magnet(&magnet.link).await {
Ok(_) => {
info!(
debug!(
"Successfully submitted magnet link to Transmission: {}",
magnet.title
);
debug!("Magnet link: {}", magnet.link);
self.db
.mark_magnet_processed_for_table::<TransmissionProcessedTable>(id)?;
processed_count += 1;
db.mark_magnet_processed_for_table::<TransmissionProcessedTable>(id)?;
processed_magnets.push(magnet);
}
Err(e) => {
warn!("Failed to submit magnet link to Transmission: {}", e);
failed_magnets.push(magnet);
}
}
} else {
warn!("Skipping magnet with null ID: {}", magnet.link);
// Consider adding to failed_magnets if we want to report these as well
}
}
Ok(processed_count)
Ok(ProcessedMagnets {
success: processed_magnets,
failed: failed_magnets,
})
}
}

View file

@ -1,3 +1,4 @@
use crate::actions::action::Action;
use crate::actions::transmission::TransmissionAction;
use crate::args::Args;
use crate::config::{get_db_path, load_config};
@ -10,7 +11,7 @@ use log::{debug, info, warn};
use multimap::MultiMap;
use reddit_client::RedditClient;
use regex::Regex;
use std::collections::HashSet;
use std::collections::{HashMap, HashSet};
use std::fs::create_dir_all;
mod actions;
@ -20,6 +21,7 @@ mod db;
mod magnet;
mod models;
mod reddit_client;
mod report;
mod schema;
#[derive(Debug)]
@ -39,43 +41,6 @@ fn filter_posts(title: &str, title_filter: Option<Regex>) -> bool {
}
}
/// Prints the posts with their magnet links
fn print_posts(posts: &[PostInfo], username: &str, title_filter: Option<Regex>) -> usize {
println!("Magnet links from u/{}:", username);
if let Some(pattern) = title_filter {
println!("Filtering titles by pattern: {}", pattern);
}
println!("----------------------------");
let mut post_count = 0;
for post in posts {
// Only display posts with magnet links
if !post.magnet_links.is_empty() {
post_count += 1;
println!(
"{}. [r/{}] {} (Posted: {})",
post_count,
post.subreddit,
post.title,
post.timestamp.format("%Y-%m-%d %H:%M:%S")
);
for (i, link) in post.magnet_links.iter().enumerate() {
println!(" Link {}: {}", i + 1, link);
}
println!();
}
}
if post_count == 0 {
println!("No posts with magnet links found.");
}
post_count
}
#[tokio::main]
async fn main() -> Result<()> {
color_eyre::install()?;
@ -117,8 +82,9 @@ async fn main() -> Result<()> {
}
// Process sources and store magnet links
let mut total_new_links = 0;
for (source_name, source_config) in conf.sources {
println!("\nProcessing source [{}]", source_name);
info!("Processing source [{}]", source_name);
let username = source_config.username.clone();
let title_filter = match source_config.title_filter {
@ -130,8 +96,6 @@ async fn main() -> Result<()> {
};
if let Some(submissions) = user_posts.get_vec(&username) {
let mut filtered_posts = Vec::new();
for post in submissions
.iter()
.filter(|s| filter_posts(&*s.title, title_filter.clone()))
@ -151,35 +115,29 @@ async fn main() -> Result<()> {
};
// Store the post info in the database
if let Err(e) = db.store_magnets(&post_info) {
warn!("Failed to store post info in database: {}", e);
match db.store_magnets(&post_info) {
Ok(count) => {
total_new_links += count;
}
Err(e) => {
warn!("Failed to store post info in database: {}", e);
}
}
filtered_posts.push(post_info);
}
}
print_posts(&filtered_posts, &username, title_filter);
}
}
// Process magnet links with Transmission if enabled
// Initialize actions
let mut actions: Vec<Box<dyn Action>> = Vec::new();
// Add Transmission action if enabled
if let Some(transmission_config) = conf.transmission {
if transmission_config.enable {
info!("Processing magnet links with Transmission");
match TransmissionAction::new(&transmission_config, db).await {
Ok(mut transmission_action) => {
match transmission_action.process_unprocessed_magnets().await {
Ok(count) => {
info!(
"Successfully processed {} magnet links with Transmission",
count
);
}
Err(e) => {
warn!("Failed to process magnet links with Transmission: {}", e);
}
}
info!("Initializing Transmission action");
match TransmissionAction::new(&transmission_config).await {
Ok(transmission_action) => {
actions.push(Box::new(transmission_action));
}
Err(e) => {
warn!("Failed to initialize Transmission action: {}", e);
@ -192,5 +150,32 @@ async fn main() -> Result<()> {
debug!("No Transmission configuration found");
}
// Process all actions and collect results
let mut action_results = HashMap::new();
for action in &mut actions {
let action_name = action.name().to_string();
debug!("Processing magnet links with {} action", action_name);
match action.process_unprocessed_magnets(&mut db).await {
Ok(processed_magnets) => {
let count = processed_magnets.success.len();
debug!(
"Successfully processed {} magnet links with {}",
count, action_name
);
action_results.insert(action_name, processed_magnets);
}
Err(e) => {
warn!("Failed to process magnet links with {}: {}", action_name, e);
}
}
}
// Generate report
if let Err(e) = report::generate_report(&action_results, total_new_links) {
warn!("Failed to generate report: {}", e);
}
Ok(())
}

87
src/report.rs Normal file
View file

@ -0,0 +1,87 @@
use crate::actions::action::ProcessedMagnets;
use color_eyre::eyre::Result;
use console::{style, Emoji};
use log::info;
use std::collections::HashMap;
static SPARKLES: Emoji = Emoji("", ":sparkles:");
static ROCKET: Emoji = Emoji("🚀", ":rocket:");
static LINK: Emoji = Emoji("🔗", ":link:");
static WARNING: Emoji = Emoji("⚠️", ":warning:");
/// Generate a report of processed magnets
pub fn generate_report(
action_results: &HashMap<String, ProcessedMagnets>,
total_new_links: usize,
) -> Result<()> {
info!("");
info!(
"{} {} {}",
SPARKLES,
style("Report Summary").bold().underlined(),
style(format!(
"{} new links added to the database",
total_new_links
))
.bold()
.green(),
);
info!("");
for (action_name, processed_magnets) in action_results {
let success_count = processed_magnets.success.len();
let failed_count = processed_magnets.failed.len();
let total_count = success_count + failed_count;
// Section header for each action
info!("");
info!(
"{} {} {}",
ROCKET,
style(format!("{}", action_name)).bold().underlined().cyan(),
style(format!("({} new links)", total_count)).bold()
);
// Success/failure summary
if failed_count > 0 {
info!(
" {} Success: {}, {} Failure: {}",
style("").green(),
style(success_count).bold().green(),
style("").red(),
style(failed_count).bold().red()
);
} else {
info!(
" {} Success: {}",
style("").green(),
style(success_count).bold().green()
);
}
info!("");
// List successful magnets
if success_count > 0 {
info!(
" {} {}",
style("").green(),
style("Added:").bold().green()
);
for magnet in &processed_magnets.success {
info!(" {} {}", LINK, style(&magnet.title).italic());
}
info!("");
}
// List failed magnets
if failed_count > 0 {
info!(" {} {}", style("").red(), style("Failed:").bold().red());
for magnet in &processed_magnets.failed {
info!(" {} {}", WARNING, style(&magnet.title).italic());
}
info!("");
}
}
Ok(())
}