From 6178c022d1db0735ab4aeceb0e4f7212d0e3fecf Mon Sep 17 00:00:00 2001 From: SinTan1729 Date: Sun, 28 May 2023 20:22:23 -0500 Subject: [PATCH] change: Improved filename sanitization and some logic --- src/functions.rs | 20 +++++++++------- src/main.rs | 40 +++++++++++++++++-------------- src/structs.rs | 61 ++++++++++++++++++++++++++++-------------------- 3 files changed, 69 insertions(+), 52 deletions(-) diff --git a/src/functions.rs b/src/functions.rs index 849bc4f..8880a54 100644 --- a/src/functions.rs +++ b/src/functions.rs @@ -21,9 +21,11 @@ pub async fn process_file( tmdb: &Client, pattern: &str, dry_run: bool, - movie_list: Option<&HashMap>, -) -> (String, String, bool) { + movie_list: Option<&HashMap>>, // The last bool tells whether the entry should be added to the movie_list or not + // The first String is filename without extension, and the second String is + // new basename, if any. +) -> (String, Option, bool) { // Set RenderConfig for the menu items inquire::set_global_render_config(get_render_config()); @@ -53,7 +55,7 @@ pub async fn process_file( Some(list) => { if list.contains_key(&filename_without_ext) { preprocessed = true; - list[&filename_without_ext].clone() + list[&filename_without_ext].clone().unwrap_or_default() } else { String::new() } @@ -63,7 +65,7 @@ pub async fn process_file( // Check if it should be ignored if preprocessed && new_name_base.is_empty() { eprintln!(" Ignoring {file_base} as per previous choice for related files..."); - return (filename_without_ext, String::new(), false); + return (filename_without_ext, None, false); } // Parse the filename for metadata @@ -75,7 +77,7 @@ pub async fn process_file( println!(" Processing {file_base}..."); } else { println!(" Ignoring {file_base}..."); - return (filename_without_ext, String::new(), false); + return (filename_without_ext, None, false); } // Only do the TMDb API stuff if it's not preprocessed @@ -113,7 +115,7 @@ pub async fn process_file( if let Some(pos) = directors_text.rfind(',') { directors_text.replace_range(pos..pos + 2, " and "); } - movie_details.director = directors_text; + movie_details.director = Some(directors_text); } } } @@ -124,7 +126,7 @@ pub async fn process_file( // If nothing is found, skip if movie_list.is_empty() { eprintln!(" Could not find any entries matching {file_base}!"); - return (filename_without_ext, String::new(), true); + return (filename_without_ext, None, true); } // Choose from the possible entries @@ -141,7 +143,7 @@ pub async fn process_file( error, InquireError::OperationCanceled | InquireError::OperationInterrupted ); - return (filename_without_ext, String::new(), flag); + return (filename_without_ext, None, flag); } }; @@ -196,7 +198,7 @@ pub async fn process_file( } } } - (filename_without_ext, new_name_base, true) + (filename_without_ext, Some(new_name_base), true) } // Function to process the passed arguments diff --git a/src/main.rs b/src/main.rs index e6de7f6..3b70013 100644 --- a/src/main.rs +++ b/src/main.rs @@ -74,10 +74,6 @@ async fn main() { ) .await; - // if movie_name_temp.is_empty() { - // continue; - // } - if add_to_list { movie_list.insert(filename_without_ext, movie_name_temp); } @@ -90,22 +86,30 @@ async fn main() { if movie_list.len() == 1 { let entry_clean = entry.trim_end_matches('/'); let movie_name = movie_list.into_values().next().unwrap(); - // If the file was ignored, exit - if movie_name.is_empty() { - eprintln!("Not renaming directory as only movie was skipped."); - continue; - } - if entry_clean == movie_name { - println!("[directory] '{entry_clean}' already has correct name."); - } else { - println!("[directory] '{entry_clean}' -> '{movie_name}'"); - if !settings["dry_run"] { - if !Path::new(movie_name.as_str()).is_dir() { - fs::rename(entry, movie_name) - .expect("Unable to rename directory!"); + // If the file was ignored, exit + match movie_name { + None => { + eprintln!("Not renaming directory as only movie was skipped."); + } + + Some(name) => { + if entry_clean == name { + println!( + "[directory] '{entry_clean}' already has correct name." + ); } else { - eprintln!("Destination directory already exists, skipping..."); + println!("[directory] '{entry_clean}' -> '{name}'",); + if !settings["dry_run"] { + if !Path::new(name.as_str()).is_dir() { + fs::rename(entry, name) + .expect("Unable to rename directory!"); + } else { + eprintln!( + "Destination directory already exists, skipping..." + ); + } + } } } } diff --git a/src/structs.rs b/src/structs.rs index 64eda67..e1a6b5e 100644 --- a/src/structs.rs +++ b/src/structs.rs @@ -5,8 +5,8 @@ use tmdb_api::movie::MovieShort; pub struct MovieEntry { pub title: String, pub id: u64, - pub director: String, - pub year: String, + pub director: Option, + pub year: Option, pub language: String, } @@ -16,39 +16,38 @@ impl MovieEntry { MovieEntry { title: movie.inner.title, id: movie.inner.id, - director: String::from("N/A"), - year: match movie.inner.release_date { - Some(date) => date.format("%Y").to_string(), - _ => String::from("N/A"), - }, + director: None, + year: movie + .inner + .release_date + .map(|date| date.format("%Y").to_string()), language: get_long_lang(movie.inner.original_language.as_str()), } } // Generate desired filename from movie entry pub fn rename_format(&self, mut format: String) -> String { - const PATTERN: &str = "^~#%$*+={}?@'`/\\\"><|:&!"; // Try to sanitize the title to avoid some characters let mut title = self.title.clone(); - title.retain(|c| !PATTERN.contains(c)); + title = sanitize(title); title.truncate(159); format = format.replace("{title}", title.as_str()); - if self.year != "N/A" { - format = format.replace("{year}", self.year.as_str()); - } else { - format = format.replace("{year}", ""); - } + format = match &self.year { + Some(year) => format.replace("{year}", year.as_str()), + None => format.replace("{year}", ""), + }; - if self.director.as_str() != "N/A" { - // Try to sanitize the director's name to avoid some characters - let mut director = self.director.clone(); - director.retain(|c| !PATTERN.contains(c)); - director.truncate(63); - format = format.replace("{director}", director.as_str()); - } else { - format = format.replace("{director}", ""); - } + format = match &self.director { + Some(name) => { + // Try to sanitize the director's name to avoid some characters + let mut director = name.clone(); + director = sanitize(director); + director.truncate(63); + format.replace("{director}", director.as_str()) + } + None => format.replace("{director}", ""), + }; // Try to clean extra spaces and such format = format.trim_matches(|c| "- ".contains(c)).to_string(); @@ -65,12 +64,12 @@ impl fmt::Display for MovieEntry { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let mut buffer = String::new(); buffer.push_str(&format!("{} ", self.title)); - buffer.push_str(&format!("({}), ", self.year)); + buffer.push_str(&format!("({:?}), ", self.year)); buffer.push_str(&format!( "Language: {}, ", get_long_lang(self.language.as_str()) )); - buffer.push_str(&format!("Directed by: {}, ", self.director)); + buffer.push_str(&format!("Directed by: {:?}, ", self.director)); buffer.push_str(&format!("TMDB ID: {}", self.id)); // buffer.push_str(&format!("Synopsis: {}", self.overview)); write!(f, "{buffer}") @@ -118,3 +117,15 @@ pub fn get_long_lang(short: &str) -> String { }; String::from(long) } + +// Sanitize filename so that there are no errors while +// creating a file/directory +fn sanitize(input: String) -> String { + const AVOID: &str = "^~*+=`/\\\"><|"; + + let mut out = input; + out.retain(|c| !AVOID.contains(c)); + out = out.replace(':', "∶"); + out = out.replace('?', "﹖"); + out +}