mod cli; mod episodes; mod files; mod history; mod list; mod metadata; mod presets; mod settings; mod sfv; mod subtitles; use aurora_curator_core::format::ExpressionFormat; use aurora_curator_core::matching::{self, EpisodeGuess, MovieGuess, ReleaseInfo}; use aurora_curator_core::rename::{self, ConflictPolicy, RenameAction, RenamePlanItem, RenameResult}; use aurora_curator_core::string_bindings; use std::collections::HashMap; use tauri::AppHandle; /// Renders a naming-scheme expression against a set of binding values. /// This is the Tauri-facing entry point into the core format engine, used by the /// rename-preview panel in the UI so users see live results as they edit a pattern. #[tauri::command] fn render_expression(expression: String, bindings: HashMap) -> Result { let compiled = ExpressionFormat::compile(&expression).map_err(|e| e.to_string())?; compiled .format(&string_bindings(bindings)) .map_err(|e| e.to_string()) } /// Guesses a season/episode number pair (and series name) from a filename using local /// regex patterns only - no network calls, no metadata lookup. Returns `None` when the /// filename doesn't carry a recognizable season/episode marker (movies, already-clean /// names, etc). #[tauri::command] fn guess_episode(filename: String) -> Option { matching::guess_episode(&filename) } /// Guesses a movie title (and release year, if present) from a filename using local /// regex/heuristic patterns only - no network calls. Callers should try /// `guess_episode` first and only fall back to this when it returns `None`; see /// `matching::guess_movie` for why. #[tauri::command] fn guess_movie(filename: String) -> Option { matching::guess_movie(&filename) } /// Guesses release-tag metadata (resolution, source, video/audio codec, release /// group) from a filename's own text using local regex patterns only - no network /// calls, no real media probing (Aurora Curator never ships an ffprobe/MediaInfo dependency, /// so filename tags are the only source for these fields). Independent of /// `guess_episode`/`guess_movie`: called alongside whichever of those two applies, /// since a release name carries (or doesn't carry) these tags regardless of whether /// it's an episode or a movie. #[tauri::command] fn guess_release_info(filename: String) -> Option { matching::guess_release_info(&filename) } /// Executes a batch of planned on-disk rename/copy/symlink operations (optionally /// moving/copying files into subfolders - see `aurora_curator_core::rename`). `action` picks /// which of the three happens; `conflict` picks what happens when a computed target /// already exists (skip it, save alongside it with an auto-numbered name, or replace /// it) - the UI is expected to have already shown the user the full old-path/new-path /// plan and both choices, and gotten explicit confirmation before calling this. Runs /// off the main thread since real filesystem I/O shouldn't block the UI. /// /// Every successfully-produced item is recorded to the rename history log afterward /// (see `history::record_batch`), tagged with `action`, so it can be undone later from /// the History panel - a Move undo moves the file back, a Copy/Symlink undo just /// removes what was created. `conflict` is NOT recorded - undoing always uses its own /// fixed skip-on-conflict policy regardless of what the original rename used. #[tauri::command] async fn execute_rename( app: AppHandle, items: Vec, action: RenameAction, conflict: ConflictPolicy, ) -> Vec { let results = tauri::async_runtime::spawn_blocking(move || rename::execute_renames(&items, action, conflict)) .await .unwrap_or_default(); history::record_batch(&app, &results, action); results } /// Entry point for the `-rename` command-line mode - see `cli.rs`'s module doc. Never /// builds a Tauri `App`/`Builder` at all, unlike [`run`] below. Returns the process /// exit code for `main.rs` to pass to `std::process::exit`. pub fn run_cli(args: Vec) -> i32 { cli::run(args) } #[cfg_attr(mobile, tauri::mobile_entry_point)] pub fn run() { tauri::Builder::default() .plugin(tauri_plugin_dialog::init()) .invoke_handler(tauri::generate_handler![ render_expression, guess_episode, guess_movie, guess_release_info, execute_rename, metadata::fetch_metadata, metadata::fetch_movie_metadata, settings::get_tmdb_token, settings::save_tmdb_token, settings::get_omdb_api_key, settings::save_omdb_api_key, settings::get_tv_metadata_source, settings::save_tv_metadata_source, settings::get_movie_metadata_source, settings::save_movie_metadata_source, settings::get_opensubtitles_api_key, settings::save_opensubtitles_api_key, settings::get_opensubtitles_username, settings::has_opensubtitles_password, settings::save_opensubtitles_login, subtitles::search_subtitles, subtitles::download_subtitle, episodes::browse_episodes, sfv::compute_checksums, sfv::save_checksum_file, sfv::verify_checksum_file, list::export_text_list, history::list_rename_history, history::undo_rename_batch, presets::list_presets, presets::save_preset, presets::delete_preset, files::pick_files, files::pick_folder, files::expand_dropped_paths, ]) .run(tauri::generate_context!()) .expect("error while running Aurora Curator"); }