From e9314ccddab9108de4454865f016f07d8c706ead Mon Sep 17 00:00:00 2001 From: SinTan1729 Date: Sun, 21 May 2023 20:39:38 -0500 Subject: [PATCH] new: Reuse same name for subtitle files and such --- src/functions.rs | 194 +++++++++++++++++++++++++++-------------------- src/main.rs | 41 +++++----- 2 files changed, 135 insertions(+), 100 deletions(-) diff --git a/src/functions.rs b/src/functions.rs index ef20373..e11e6e7 100644 --- a/src/functions.rs +++ b/src/functions.rs @@ -21,7 +21,9 @@ pub async fn process_file( tmdb: &Client, pattern: &str, dry_run: bool, -) -> (String, bool) { + movie_list: Option<&HashMap>, +) -> (String, String, bool) { + // The last bool tells whether the entry should be added to the movie_list or not // Set RenderConfig for the menu items inquire::set_global_render_config(get_render_config()); @@ -35,6 +37,34 @@ pub async fn process_file( } } + // Split the filename into parts for a couple of checks and some later use + let filename_parts: Vec<&str> = filename.rsplit('.').collect(); + let filename_without_ext = if filename_parts.len() >= 3 && filename_parts[1].len() == 2 { + filename.rsplitn(3, '.').last().unwrap().to_string() + } else { + filename.rsplit_once('.').unwrap().0.to_string() + }; + + // Check if the filename (without extension) has already been processed + // If yes, we'll use the older results + let mut preprocessed = false; + let mut new_name_base = match movie_list { + None => String::new(), + Some(list) => { + if list.contains_key(&filename_without_ext) { + preprocessed = true; + list[&filename_without_ext].clone() + } else { + String::new() + } + } + }; + + // Check if it should be ignored + if preprocessed && new_name_base.is_empty() { + return (filename_without_ext, "".to_string(), false); + } + // Parse the filename for metadata let metadata = Metadata::from(file_base.as_str()).expect(" Could not parse filename!"); @@ -44,96 +74,98 @@ pub async fn process_file( println!(" Processing {}...", file_base); } else { println!(" Ignoring {}...", file_base); - return ("n/a".to_string(), false); + return (filename_without_ext, "".to_string(), false); } - // Search using the TMDb API - let year = metadata.year().map(|y| y as u16); - let search = MovieSearch::new(metadata.title().to_string()).with_year(year); - let reply = search.execute(tmdb).await; + // Only do the TMDb API stuff if it's not preprocessed + if !preprocessed { + // Search using the TMDb API + let year = metadata.year().map(|y| y as u16); + let search = MovieSearch::new(metadata.title().to_string()).with_year(year); + let reply = search.execute(tmdb).await; - let results = match reply { - Ok(res) => Ok(res.results), - Err(e) => { - eprintln!(" There was an error while searching {}!", file_base); - Err(e) - } - }; + let results = match reply { + Ok(res) => Ok(res.results), + Err(e) => { + eprintln!(" There was an error while searching {}!", file_base); + Err(e) + } + }; - let mut movie_list: Vec = Vec::new(); - // Create movie entry from the result - if results.is_ok() { - for result in results.unwrap() { - let mut movie_details = MovieEntry::from(result); - // Get director's name, if needed - if pattern.contains("{director}") { - let credits_search = MovieCredits::new(movie_details.id); - let credits_reply = credits_search.execute(tmdb).await; - if credits_reply.is_ok() { - let mut crew = credits_reply.unwrap().crew; - // Only keep the director(s) - crew.retain(|x| x.job == *"Director"); - if !crew.is_empty() { - let directors: Vec = - crew.iter().map(|x| x.person.name.clone()).collect(); - let mut directors_text = directors.join(", "); - if let Some(pos) = directors_text.rfind(',') { - directors_text.replace_range(pos..pos + 2, " and "); + let mut movie_list: Vec = Vec::new(); + // Create movie entry from the result + if results.is_ok() { + for result in results.unwrap() { + let mut movie_details = MovieEntry::from(result); + // Get director's name, if needed + if pattern.contains("{director}") { + let credits_search = MovieCredits::new(movie_details.id); + let credits_reply = credits_search.execute(tmdb).await; + if credits_reply.is_ok() { + let mut crew = credits_reply.unwrap().crew; + // Only keep the director(s) + crew.retain(|x| x.job == *"Director"); + if !crew.is_empty() { + let directors: Vec = + crew.iter().map(|x| x.person.name.clone()).collect(); + let mut directors_text = directors.join(", "); + if let Some(pos) = directors_text.rfind(',') { + directors_text.replace_range(pos..pos + 2, " and "); + } + movie_details.director = directors_text; } - movie_details.director = directors_text; } } - } - movie_list.push(movie_details); - } - } - - // If nothing is found, skip - if movie_list.is_empty() { - eprintln!(" Could not find any entries matching {}!", file_base); - return ("".to_string(), true); - } - - // Choose from the possible entries - let choice = match Select::new( - format!(" Possible choices for {}:", file_base).as_str(), - movie_list, - ) - .prompt() - { - Ok(movie) => movie, - Err(error) => { - println!(" {error}"); - return ("".to_string(), true); - } - }; - - // Handle the case for subtitle files - let mut is_subtitle = false; - if ["srt", "ssa"].contains(&extension.as_str()) { - // Try to detect if there's already language info in the filename, else ask user to choose - let filename_parts: Vec<&str> = filename.rsplit('.').collect(); - if filename_parts.len() >= 3 && filename_parts[1].len() == 2 { - println!( - " Keeping language {} as detected in the subtitle file's extension...", - get_long_lang(filename_parts[1]) - ); - extension = format!("{}.{}", filename_parts[1], extension); - } else { - let lang_list = Language::generate_list(); - let lang_choice = - Select::new(" Choose the language for the subtitle file:", lang_list) - .prompt() - .expect(" Invalid choice!"); - if lang_choice.short != *"none" { - extension = format!("{}.{}", lang_choice.short, extension); + movie_list.push(movie_details); } } - is_subtitle = true; + + // If nothing is found, skip + if movie_list.is_empty() { + eprintln!(" Could not find any entries matching {}!", file_base); + return (filename_without_ext, "".to_string(), true); + } + + // Choose from the possible entries + let choice = match Select::new( + format!(" Possible choices for {}:", file_base).as_str(), + movie_list, + ) + .prompt() + { + Ok(movie) => movie, + Err(error) => { + println!(" {error}"); + return (filename_without_ext, "".to_string(), false); + } + }; + + // Handle the case for subtitle files + if ["srt", "ssa"].contains(&extension.as_str()) { + // Try to detect if there's already language info in the filename, else ask user to choose + if filename_parts.len() >= 3 && filename_parts[1].len() == 2 { + println!( + " Keeping language {} as detected in the subtitle file's extension...", + get_long_lang(filename_parts[1]) + ); + extension = format!("{}.{}", filename_parts[1], extension); + } else { + let lang_list = Language::generate_list(); + let lang_choice = + Select::new(" Choose the language for the subtitle file:", lang_list) + .prompt() + .expect(" Invalid choice!"); + if lang_choice.short != *"none" { + extension = format!("{}.{}", lang_choice.short, extension); + } + } + } + + // Create the new name + new_name_base = choice.rename_format(pattern.to_string()); } - // Create the new name - let new_name_base = choice.rename_format(pattern.to_string()); + // Add extension and stuff to the new name let mut new_name_with_ext = new_name_base.clone(); if !extension.is_empty() { new_name_with_ext = format!("{}.{}", new_name_with_ext, extension); @@ -157,7 +189,7 @@ pub async fn process_file( } } } - (new_name_base, is_subtitle) + (filename_without_ext, new_name_base, true) } // Function to process the passed arguments diff --git a/src/main.rs b/src/main.rs index 94e3ad9..2deb426 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,5 @@ use load_file::{self, load_str}; -use std::{env, fs, path::Path, process::exit}; +use std::{collections::HashMap, env, fs, path::Path, process::exit}; use tmdb_api::Client; // Import all the modules @@ -43,13 +43,12 @@ async fn main() { // Iterate over entries for entry in entries { // Check if the file/directory exists on disk and run necessary commands - // TODO: Detect subtitle files with same name/metadata and process them automatically without repeated input match settings["directory"] { // Normal file false => { if Path::new(entry.as_str()).is_file() { // Process the filename for movie entries - process_file(&entry, &tmdb, pattern, settings["dry_run"]).await; + process_file(&entry, &tmdb, pattern, settings["dry_run"], None).await; } else { eprintln!("The file {} wasn't found on disk, skipping...", entry); continue; @@ -59,26 +58,28 @@ async fn main() { true => { if Path::new(entry.as_str()).is_dir() { println!("Processing files inside the directory {}...", entry); - let mut movie_count = 0; - let mut movie_name = String::new(); + let mut movie_list = HashMap::new(); + if let Ok(files_in_dir) = fs::read_dir(entry.as_str()) { for file in files_in_dir { if file.is_ok() { - let (movie_name_temp, is_subtitle) = process_file( - &format!("{}", file.unwrap().path().display()), - &tmdb, - pattern, - settings["dry_run"], - ) - .await; + let filename = file.unwrap().path().display().to_string(); + let (filename_without_ext, movie_name_temp, add_to_list) = + process_file( + &filename, + &tmdb, + pattern, + settings["dry_run"], + Some(&movie_list), + ) + .await; - if movie_name_temp == *"n/a" { - continue; - } + // if movie_name_temp.is_empty() { + // continue; + // } - if !is_subtitle { - movie_count += 1; - movie_name = movie_name_temp; + if add_to_list { + movie_list.insert(filename_without_ext, movie_name_temp); } } } @@ -86,8 +87,10 @@ async fn main() { eprintln!("There was an error accessing the directory {}!", entry); continue; } - if movie_count == 1 { + if movie_list.len() == 1 { let entry_clean = entry.trim_end_matches('/'); + let movie_name = movie_list.into_values().next().unwrap(); + if entry_clean == movie_name { println!("[directory] '{}' already has correct name.", entry_clean); } else {