/* This file is part of jellything (https://codeberg.org/metamuffin/jellything) which is licensed under the GNU Affero General Public License (version 3); see /COPYING. Copyright (C) 2025 metamuffin */ use crate::cli::Action; use anyhow::anyhow; use dialoguer::{theme::ColorfulTheme, Confirm, FuzzySelect, Input}; use jellybase::SECRETS; use jellycommon::TraktKind; use jellyimport::trakt::Trakt; use log::warn; use std::{ fmt::Display, path::{Path, PathBuf}, }; use tokio::fs::rename; pub async fn add(action: Action) -> anyhow::Result<()> { match action { Action::Add { media } => { let theme = ColorfulTheme::default(); // let possible_kinds = [ // TraktKind::Movie, // TraktKind::Season, // TraktKind::Show, // TraktKind::Episode, // ]; // let trakt_kind: Vec = MultiSelect::with_theme(&theme) // .with_prompt("Media Kind") // .items(&possible_kinds) // .defaults(&[true, false, false, false]) // .interact() // .unwrap(); // let search_kinds = trakt_kind // .iter() // .map(|&i| possible_kinds[i]) // .collect::>(); let search_kinds = [TraktKind::Show, TraktKind::Season, TraktKind::Movie]; let (trakt_object, trakt_kind) = loop { let name: String = Input::with_theme(&theme) .with_prompt("Search by title") .default(path_to_query(&media)) .interact_text() .unwrap(); let trakt = Trakt::new( SECRETS .api .trakt .as_ref() .ok_or(anyhow!("no trakt api key configured"))?, ); let results = trakt.search(&search_kinds, &name, false).await?; if results.is_empty() { warn!("no search results"); continue; } let correct = FuzzySelect::with_theme(&theme) .items(&results) .default(0) .with_prompt("Metadata Source") .interact_opt() .unwrap(); if let Some(o) = correct { break (results[o].inner.inner().to_owned(), results[o].r#type); } }; assert_eq!(trakt_kind, TraktKind::Movie); let stem = media.file_name().unwrap().to_string_lossy().to_string(); let stem = stem.split_once(".").unwrap_or((stem.as_str(), "")).0; let mut newpath = media.parent().unwrap().join(format!( "{stem}.trakt-{}.mkv", trakt_object.ids.trakt.unwrap() )); let mut n = 1; while newpath.exists() { newpath = media.parent().unwrap().join(format!("{stem}.alt-{n}.mkv",)); n += 1; } if Confirm::with_theme(&theme) .with_prompt(format!("Rename {media:?} -> {newpath:?}?")) .default(true) .interact() .unwrap() { rename(media, newpath).await?; } Ok(()) } _ => unreachable!(), } } // fn validate_id(s: &String) -> anyhow::Result<()> { // if &make_id(s) == s { // Ok(()) // } else { // bail!("invalid id") // } // } // fn make_id(s: &str) -> String { // let mut out = String::new(); // for s in s.chars() { // match s { // 'a'..='z' | '0'..='9' => out.push(s), // 'A'..='Z' => out.push(s.to_ascii_lowercase()), // '-' | ' ' | '_' | ':' => out.push('-'), // _ => (), // } // } // out // } fn path_to_query(path: &Path) -> String { let stem = path.file_name().unwrap().to_string_lossy().to_string(); let stem = stem.split_once(".").unwrap_or((stem.as_str(), "")).0; stem.replace("-", " ") } pub struct PathDisplay(PathBuf); impl Display for PathDisplay { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.write_str("/")?; f.write_str(self.0.to_str().unwrap()) } }