routes.rs 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. use crate::{
  2. cache::Cache,
  3. error::NtitledError,
  4. htmx::{DirHtmx, FileHtmx, SubtitleResponseHtmx},
  5. ost::{data::SubtitleResponse, hash::compute_hash},
  6. FileType, State,
  7. };
  8. use axum::{
  9. http::{header, StatusCode},
  10. response::IntoResponse,
  11. };
  12. use htmxpress::HtmxElement;
  13. use minijinja::context;
  14. use serde::Deserialize;
  15. use std::{ffi::OsStr, fmt::Write, path::Path};
  16. use tracing::info;
  17. pub async fn get_directory(
  18. mut state: axum::extract::State<State>,
  19. req: axum::extract::Request,
  20. ) -> Result<impl IntoResponse, NtitledError> {
  21. // Strip /dir/
  22. let path = &req.uri().path()[5..];
  23. let path = if path == "root" {
  24. state.base_path.clone()
  25. } else {
  26. let path = urlencoding::decode(path)?;
  27. format!("{}/{path}", state.base_path)
  28. };
  29. info!("Listing {path}");
  30. let files = state.scan_dir_contents(path)?;
  31. let response = files
  32. .into_iter()
  33. .enumerate()
  34. .fold(String::new(), |mut acc, (i, entry)| {
  35. match entry {
  36. FileType::Directory(mut dir) => {
  37. let Some((_, path)) = dir.path.split_once(&state.base_path) else {
  38. return acc;
  39. };
  40. dir.path = path[1..].to_string();
  41. let dir: DirHtmx = dir.into();
  42. let _ = write!(acc, "{}", dir.to_htmx());
  43. }
  44. FileType::File(mut file) => {
  45. let Some((_, path)) = file.path.split_once(&state.base_path) else {
  46. return acc;
  47. };
  48. file.path = path[1..].to_string();
  49. let file = FileHtmx::new(i, file);
  50. let _ = write!(acc, "{}", file.to_htmx());
  51. }
  52. };
  53. acc
  54. });
  55. let template = state.env.get_template("index").unwrap();
  56. let template = template.render(context! {divs => response}).unwrap();
  57. Ok((
  58. StatusCode::OK,
  59. [(header::CONTENT_TYPE, "text/html")],
  60. template,
  61. ))
  62. }
  63. #[derive(Debug, Deserialize)]
  64. pub struct SubtitleSearch {
  65. /// Used as the query.
  66. name: String,
  67. /// Used for the file hash.
  68. path: String,
  69. }
  70. pub async fn search_subtitles(
  71. state: axum::extract::State<State>,
  72. query: axum::extract::Query<SubtitleSearch>,
  73. ) -> Result<String, NtitledError> {
  74. let path = format!("{}/{}", state.base_path, &query.path);
  75. info!("Computing hash for {}", &path);
  76. let hash = compute_hash(&path)?;
  77. info!("Searching subtitles for {}", &query.name);
  78. let search = state.client.search(&query.name, Some(&hash)).await?;
  79. let mut response = search.data.into_iter().fold(String::new(), |mut acc, el| {
  80. let el = SubtitleResponse::from(el);
  81. let el = SubtitleResponseHtmx::new(path.clone(), el).to_htmx();
  82. let _ = write!(acc, "{el}");
  83. acc
  84. });
  85. if response.is_empty() {
  86. response = "No subtitles found :(".to_string();
  87. }
  88. Ok(response)
  89. }
  90. #[derive(Debug, Deserialize)]
  91. pub struct DownloadQuery {
  92. file_id: String,
  93. full_path: String,
  94. }
  95. pub async fn download_subtitles(
  96. mut state: axum::extract::State<State>,
  97. query: axum::extract::Query<DownloadQuery>,
  98. ) -> Result<String, NtitledError> {
  99. let file_id = urlencoding::decode(&query.file_id)?.parse()?;
  100. let full_path = urlencoding::decode(&query.full_path)?.to_string();
  101. let file = full_path.split('/').last();
  102. if let Some(file) = file {
  103. info!("Downloading subtitles for {file}");
  104. }
  105. let ext = Path::new(&full_path).extension().and_then(OsStr::to_str);
  106. let srt_path = ext
  107. .map(|ext| query.full_path.replace(&format!(".{ext}"), ".srt"))
  108. .unwrap_or(format!("{full_path}.srt"));
  109. state.client.download_subtitles(file_id, &srt_path).await?;
  110. if let Some(name) = file {
  111. let existing_meta = state
  112. .cache
  113. .get_file_meta(&name.replace(&format!(".{}", ext.unwrap_or_default()), ""))?;
  114. if let Some(mut meta) = existing_meta {
  115. meta.has_subs = true;
  116. state.cache.set_file_meta(&meta)?;
  117. }
  118. }
  119. Ok(String::from("Successfully downloaded subtitles"))
  120. }