Merge branch 'report' into 'main'
Clean up output See merge request kernald/reddit-magnet!8
This commit is contained in:
commit
90c3fc5ee3
7 changed files with 224 additions and 77 deletions
38
Cargo.lock
generated
38
Cargo.lock
generated
|
|
@ -91,6 +91,17 @@ dependencies = [
|
||||||
"windows-sys 0.59.0",
|
"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]]
|
[[package]]
|
||||||
name = "atomic"
|
name = "atomic"
|
||||||
version = "0.6.0"
|
version = "0.6.0"
|
||||||
|
|
@ -282,6 +293,19 @@ version = "1.0.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
|
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]]
|
[[package]]
|
||||||
name = "core-foundation"
|
name = "core-foundation"
|
||||||
version = "0.9.4"
|
version = "0.9.4"
|
||||||
|
|
@ -439,6 +463,12 @@ version = "1.15.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
|
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "encode_unicode"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "encoding_rs"
|
name = "encoding_rs"
|
||||||
version = "0.8.35"
|
version = "0.8.35"
|
||||||
|
|
@ -1496,10 +1526,12 @@ dependencies = [
|
||||||
name = "reddit-magnet"
|
name = "reddit-magnet"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"async-trait",
|
||||||
"chrono",
|
"chrono",
|
||||||
"clap",
|
"clap",
|
||||||
"clap-verbosity-flag",
|
"clap-verbosity-flag",
|
||||||
"color-eyre",
|
"color-eyre",
|
||||||
|
"console",
|
||||||
"diesel",
|
"diesel",
|
||||||
"diesel_migrations",
|
"diesel_migrations",
|
||||||
"directories",
|
"directories",
|
||||||
|
|
@ -2281,6 +2313,12 @@ version = "1.0.18"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
|
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-width"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "untrusted"
|
name = "untrusted"
|
||||||
version = "0.9.0"
|
version = "0.9.0"
|
||||||
|
|
|
||||||
|
|
@ -27,3 +27,5 @@ transmission-rpc = "0.5.0"
|
||||||
url = "2.5.4"
|
url = "2.5.4"
|
||||||
clap-verbosity-flag = "3.0.2"
|
clap-verbosity-flag = "3.0.2"
|
||||||
pretty_env_logger = "0.5.0"
|
pretty_env_logger = "0.5.0"
|
||||||
|
async-trait = "0.1.77"
|
||||||
|
console = "0.15.8"
|
||||||
|
|
|
||||||
22
src/actions/action.rs
Normal file
22
src/actions/action.rs
Normal 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>;
|
||||||
|
}
|
||||||
|
|
@ -1 +1,2 @@
|
||||||
|
pub mod action;
|
||||||
pub mod transmission;
|
pub mod transmission;
|
||||||
|
|
|
||||||
|
|
@ -1,51 +1,63 @@
|
||||||
|
use crate::actions::action::{Action, ProcessedMagnets};
|
||||||
use crate::actions::transmission::client::TransmissionClient;
|
use crate::actions::transmission::client::TransmissionClient;
|
||||||
use crate::actions::transmission::config::TransmissionConfig;
|
use crate::actions::transmission::config::TransmissionConfig;
|
||||||
use crate::db::{Database, TransmissionProcessedTable};
|
use crate::db::{Database, TransmissionProcessedTable};
|
||||||
use color_eyre::eyre::Result;
|
use color_eyre::eyre::Result;
|
||||||
use log::{debug, info, warn};
|
use log::{debug, warn};
|
||||||
|
|
||||||
/// Action for submitting magnet links to Transmission
|
/// Action for submitting magnet links to Transmission
|
||||||
pub struct TransmissionAction {
|
pub struct TransmissionAction {
|
||||||
client: TransmissionClient,
|
client: TransmissionClient,
|
||||||
db: Database,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TransmissionAction {
|
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)?;
|
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
|
/// Process all unprocessed magnet links and return the list of processed magnets
|
||||||
pub async fn process_unprocessed_magnets(&mut self) -> Result<usize> {
|
async fn process_unprocessed_magnets(&mut self, db: &mut Database) -> Result<ProcessedMagnets> {
|
||||||
let unprocessed_magnets = self
|
let unprocessed_magnets =
|
||||||
.db
|
db.get_unprocessed_magnets_for_table::<TransmissionProcessedTable>()?;
|
||||||
.get_unprocessed_magnets_for_table::<TransmissionProcessedTable>()?;
|
let mut processed_magnets = Vec::new();
|
||||||
let mut processed_count = 0;
|
let mut failed_magnets = Vec::new();
|
||||||
|
|
||||||
for magnet in unprocessed_magnets {
|
for magnet in unprocessed_magnets {
|
||||||
if let Some(id) = magnet.id {
|
if let Some(id) = magnet.id {
|
||||||
match self.client.submit_magnet(&magnet.link).await {
|
match self.client.submit_magnet(&magnet.link).await {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
info!(
|
debug!(
|
||||||
"Successfully submitted magnet link to Transmission: {}",
|
"Successfully submitted magnet link to Transmission: {}",
|
||||||
magnet.title
|
magnet.title
|
||||||
);
|
);
|
||||||
debug!("Magnet link: {}", magnet.link);
|
debug!("Magnet link: {}", magnet.link);
|
||||||
self.db
|
db.mark_magnet_processed_for_table::<TransmissionProcessedTable>(id)?;
|
||||||
.mark_magnet_processed_for_table::<TransmissionProcessedTable>(id)?;
|
processed_magnets.push(magnet);
|
||||||
processed_count += 1;
|
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
warn!("Failed to submit magnet link to Transmission: {}", e);
|
warn!("Failed to submit magnet link to Transmission: {}", e);
|
||||||
|
failed_magnets.push(magnet);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
warn!("Skipping magnet with null ID: {}", magnet.link);
|
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,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
109
src/main.rs
109
src/main.rs
|
|
@ -1,3 +1,4 @@
|
||||||
|
use crate::actions::action::Action;
|
||||||
use crate::actions::transmission::TransmissionAction;
|
use crate::actions::transmission::TransmissionAction;
|
||||||
use crate::args::Args;
|
use crate::args::Args;
|
||||||
use crate::config::{get_db_path, load_config};
|
use crate::config::{get_db_path, load_config};
|
||||||
|
|
@ -10,7 +11,7 @@ use log::{debug, info, warn};
|
||||||
use multimap::MultiMap;
|
use multimap::MultiMap;
|
||||||
use reddit_client::RedditClient;
|
use reddit_client::RedditClient;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use std::collections::HashSet;
|
use std::collections::{HashMap, HashSet};
|
||||||
use std::fs::create_dir_all;
|
use std::fs::create_dir_all;
|
||||||
|
|
||||||
mod actions;
|
mod actions;
|
||||||
|
|
@ -20,6 +21,7 @@ mod db;
|
||||||
mod magnet;
|
mod magnet;
|
||||||
mod models;
|
mod models;
|
||||||
mod reddit_client;
|
mod reddit_client;
|
||||||
|
mod report;
|
||||||
mod schema;
|
mod schema;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[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]
|
#[tokio::main]
|
||||||
async fn main() -> Result<()> {
|
async fn main() -> Result<()> {
|
||||||
color_eyre::install()?;
|
color_eyre::install()?;
|
||||||
|
|
@ -117,8 +82,9 @@ async fn main() -> Result<()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process sources and store magnet links
|
// Process sources and store magnet links
|
||||||
|
let mut total_new_links = 0;
|
||||||
for (source_name, source_config) in conf.sources {
|
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 username = source_config.username.clone();
|
||||||
let title_filter = match source_config.title_filter {
|
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) {
|
if let Some(submissions) = user_posts.get_vec(&username) {
|
||||||
let mut filtered_posts = Vec::new();
|
|
||||||
|
|
||||||
for post in submissions
|
for post in submissions
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|s| filter_posts(&*s.title, title_filter.clone()))
|
.filter(|s| filter_posts(&*s.title, title_filter.clone()))
|
||||||
|
|
@ -151,35 +115,29 @@ async fn main() -> Result<()> {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Store the post info in the database
|
// Store the post info in the database
|
||||||
if let Err(e) = db.store_magnets(&post_info) {
|
match db.store_magnets(&post_info) {
|
||||||
warn!("Failed to store post info in database: {}", e);
|
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 let Some(transmission_config) = conf.transmission {
|
||||||
if transmission_config.enable {
|
if transmission_config.enable {
|
||||||
info!("Processing magnet links with Transmission");
|
info!("Initializing Transmission action");
|
||||||
match TransmissionAction::new(&transmission_config, db).await {
|
match TransmissionAction::new(&transmission_config).await {
|
||||||
Ok(mut transmission_action) => {
|
Ok(transmission_action) => {
|
||||||
match transmission_action.process_unprocessed_magnets().await {
|
actions.push(Box::new(transmission_action));
|
||||||
Ok(count) => {
|
|
||||||
info!(
|
|
||||||
"Successfully processed {} magnet links with Transmission",
|
|
||||||
count
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
warn!("Failed to process magnet links with Transmission: {}", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
warn!("Failed to initialize Transmission action: {}", e);
|
warn!("Failed to initialize Transmission action: {}", e);
|
||||||
|
|
@ -192,5 +150,32 @@ async fn main() -> Result<()> {
|
||||||
debug!("No Transmission configuration found");
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
87
src/report.rs
Normal file
87
src/report.rs
Normal 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(())
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue