use crate::{ cache::Cache, error::NtitledError, htmx::{DirHtmx, FileHtmx, SubtitleResponseHtmx}, ost::{data::SubtitleResponse, hash::compute_hash}, FileType, State, }; use axum::{ http::{header, StatusCode}, response::IntoResponse, }; use htmxpress::HtmxElement; use minijinja::context; use serde::Deserialize; use std::{ffi::OsStr, fmt::Write, path::Path}; use tracing::info; pub async fn get_directory( mut state: axum::extract::State, req: axum::extract::Request, ) -> Result { // Strip /dir/ let path = &req.uri().path()[5..]; let path = if path == "root" { state.base_path.clone() } else { let path = urlencoding::decode(path)?; format!("{}/{path}", state.base_path) }; info!("Listing {path}"); let files = state.scan_dir_contents(path)?; let response = files .into_iter() .enumerate() .fold(String::new(), |mut acc, (i, entry)| { match entry { FileType::Directory(mut dir) => { let Some((_, path)) = dir.path.split_once(&state.base_path) else { return acc; }; dir.path = path[1..].to_string(); let dir: DirHtmx = dir.into(); let _ = write!(acc, "{}", dir.to_htmx()); } FileType::File(mut file) => { let Some((_, path)) = file.path.split_once(&state.base_path) else { return acc; }; file.path = path[1..].to_string(); let file = FileHtmx::new(i, file); let _ = write!(acc, "{}", file.to_htmx()); } }; acc }); let template = state.env.get_template("index").unwrap(); let template = template.render(context! {divs => response}).unwrap(); Ok(( StatusCode::OK, [(header::CONTENT_TYPE, "text/html")], template, )) } #[derive(Debug, Deserialize)] pub struct SubtitleSearch { /// Used as the query. name: String, /// Used for the file hash. path: String, } pub async fn search_subtitles( state: axum::extract::State, query: axum::extract::Query, ) -> Result { let path = format!("{}/{}", state.base_path, &query.path); info!("Computing hash for {}", &path); let hash = compute_hash(&path)?; info!("Searching subtitles for {}", &query.name); let search = state.client.search(&query.name, Some(&hash)).await?; let mut response = search.data.into_iter().fold(String::new(), |mut acc, el| { let el = SubtitleResponse::from(el); let el = SubtitleResponseHtmx::new(path.clone(), el).to_htmx(); let _ = write!(acc, "{el}"); acc }); if response.is_empty() { response = "No subtitles found :(".to_string(); } Ok(response) } #[derive(Debug, Deserialize)] pub struct DownloadQuery { file_id: String, full_path: String, } pub async fn download_subtitles( mut state: axum::extract::State, query: axum::extract::Query, ) -> Result { let file_id = urlencoding::decode(&query.file_id)?.parse()?; let full_path = urlencoding::decode(&query.full_path)?.to_string(); let file = full_path.split('/').last(); if let Some(file) = file { info!("Downloading subtitles for {file}"); } let ext = Path::new(&full_path).extension().and_then(OsStr::to_str); let srt_path = ext .map(|ext| query.full_path.replace(&format!(".{ext}"), ".srt")) .unwrap_or(format!("{full_path}.srt")); state.client.download_subtitles(file_id, &srt_path).await?; if let Some(name) = file { let existing_meta = state .cache .get_file_meta(&name.replace(&format!(".{}", ext.unwrap_or_default()), ""))?; if let Some(mut meta) = existing_meta { meta.has_subs = true; state.cache.set_file_meta(&meta)?; } } Ok(String::from("Successfully downloaded subtitles")) }