change: Improved filename sanitization and some logic

This commit is contained in:
Sayantan Santra 2023-05-28 20:22:23 -05:00
parent 5e5ba7ea0a
commit 6178c022d1
Signed by: SinTan1729
GPG Key ID: EB3E68BFBA25C85F
3 changed files with 69 additions and 52 deletions

View File

@ -21,9 +21,11 @@ pub async fn process_file(
tmdb: &Client, tmdb: &Client,
pattern: &str, pattern: &str,
dry_run: bool, dry_run: bool,
movie_list: Option<&HashMap<String, String>>, movie_list: Option<&HashMap<String, Option<String>>>,
) -> (String, String, bool) {
// The last bool tells whether the entry should be added to the movie_list or not // 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<String>, bool) {
// Set RenderConfig for the menu items // Set RenderConfig for the menu items
inquire::set_global_render_config(get_render_config()); inquire::set_global_render_config(get_render_config());
@ -53,7 +55,7 @@ pub async fn process_file(
Some(list) => { Some(list) => {
if list.contains_key(&filename_without_ext) { if list.contains_key(&filename_without_ext) {
preprocessed = true; preprocessed = true;
list[&filename_without_ext].clone() list[&filename_without_ext].clone().unwrap_or_default()
} else { } else {
String::new() String::new()
} }
@ -63,7 +65,7 @@ pub async fn process_file(
// Check if it should be ignored // Check if it should be ignored
if preprocessed && new_name_base.is_empty() { if preprocessed && new_name_base.is_empty() {
eprintln!(" Ignoring {file_base} as per previous choice for related files..."); 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 // Parse the filename for metadata
@ -75,7 +77,7 @@ pub async fn process_file(
println!(" Processing {file_base}..."); println!(" Processing {file_base}...");
} else { } else {
println!(" Ignoring {file_base}..."); 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 // 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(',') { if let Some(pos) = directors_text.rfind(',') {
directors_text.replace_range(pos..pos + 2, " and "); 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 nothing is found, skip
if movie_list.is_empty() { if movie_list.is_empty() {
eprintln!(" Could not find any entries matching {file_base}!"); 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 // Choose from the possible entries
@ -141,7 +143,7 @@ pub async fn process_file(
error, error,
InquireError::OperationCanceled | InquireError::OperationInterrupted 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 // Function to process the passed arguments

View File

@ -74,10 +74,6 @@ async fn main() {
) )
.await; .await;
// if movie_name_temp.is_empty() {
// continue;
// }
if add_to_list { if add_to_list {
movie_list.insert(filename_without_ext, movie_name_temp); movie_list.insert(filename_without_ext, movie_name_temp);
} }
@ -90,22 +86,30 @@ async fn main() {
if movie_list.len() == 1 { if movie_list.len() == 1 {
let entry_clean = entry.trim_end_matches('/'); let entry_clean = entry.trim_end_matches('/');
let movie_name = movie_list.into_values().next().unwrap(); 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 { // If the file was ignored, exit
println!("[directory] '{entry_clean}' already has correct name."); match movie_name {
} else { None => {
println!("[directory] '{entry_clean}' -> '{movie_name}'"); eprintln!("Not renaming directory as only movie was skipped.");
if !settings["dry_run"] { }
if !Path::new(movie_name.as_str()).is_dir() {
fs::rename(entry, movie_name) Some(name) => {
.expect("Unable to rename directory!"); if entry_clean == name {
println!(
"[directory] '{entry_clean}' already has correct name."
);
} else { } 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..."
);
}
}
} }
} }
} }

View File

@ -5,8 +5,8 @@ use tmdb_api::movie::MovieShort;
pub struct MovieEntry { pub struct MovieEntry {
pub title: String, pub title: String,
pub id: u64, pub id: u64,
pub director: String, pub director: Option<String>,
pub year: String, pub year: Option<String>,
pub language: String, pub language: String,
} }
@ -16,39 +16,38 @@ impl MovieEntry {
MovieEntry { MovieEntry {
title: movie.inner.title, title: movie.inner.title,
id: movie.inner.id, id: movie.inner.id,
director: String::from("N/A"), director: None,
year: match movie.inner.release_date { year: movie
Some(date) => date.format("%Y").to_string(), .inner
_ => String::from("N/A"), .release_date
}, .map(|date| date.format("%Y").to_string()),
language: get_long_lang(movie.inner.original_language.as_str()), language: get_long_lang(movie.inner.original_language.as_str()),
} }
} }
// Generate desired filename from movie entry // Generate desired filename from movie entry
pub fn rename_format(&self, mut format: String) -> String { pub fn rename_format(&self, mut format: String) -> String {
const PATTERN: &str = "^~#%$*+={}?@'`/\\\"><|:&!";
// Try to sanitize the title to avoid some characters // Try to sanitize the title to avoid some characters
let mut title = self.title.clone(); let mut title = self.title.clone();
title.retain(|c| !PATTERN.contains(c)); title = sanitize(title);
title.truncate(159); title.truncate(159);
format = format.replace("{title}", title.as_str()); format = format.replace("{title}", title.as_str());
if self.year != "N/A" { format = match &self.year {
format = format.replace("{year}", self.year.as_str()); Some(year) => format.replace("{year}", year.as_str()),
} else { None => format.replace("{year}", ""),
format = format.replace("{year}", ""); };
}
if self.director.as_str() != "N/A" { format = match &self.director {
// Try to sanitize the director's name to avoid some characters Some(name) => {
let mut director = self.director.clone(); // Try to sanitize the director's name to avoid some characters
director.retain(|c| !PATTERN.contains(c)); let mut director = name.clone();
director.truncate(63); director = sanitize(director);
format = format.replace("{director}", director.as_str()); director.truncate(63);
} else { format.replace("{director}", director.as_str())
format = format.replace("{director}", ""); }
} None => format.replace("{director}", ""),
};
// Try to clean extra spaces and such // Try to clean extra spaces and such
format = format.trim_matches(|c| "- ".contains(c)).to_string(); 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 { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut buffer = String::new(); let mut buffer = String::new();
buffer.push_str(&format!("{} ", self.title)); buffer.push_str(&format!("{} ", self.title));
buffer.push_str(&format!("({}), ", self.year)); buffer.push_str(&format!("({:?}), ", self.year));
buffer.push_str(&format!( buffer.push_str(&format!(
"Language: {}, ", "Language: {}, ",
get_long_lang(self.language.as_str()) 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!("TMDB ID: {}", self.id));
// buffer.push_str(&format!("Synopsis: {}", self.overview)); // buffer.push_str(&format!("Synopsis: {}", self.overview));
write!(f, "{buffer}") write!(f, "{buffer}")
@ -118,3 +117,15 @@ pub fn get_long_lang(short: &str) -> String {
}; };
String::from(long) 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
}