main.rs 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. use std::{fs, path::Path};
  2. use axum::{
  3. response::Redirect,
  4. routing::{get, get_service},
  5. Router,
  6. };
  7. use minijinja::Environment;
  8. use ost::client::OSClient;
  9. use serde::Serialize;
  10. use tower_http::services::ServeDir;
  11. use tracing::info;
  12. pub mod error;
  13. pub mod ost;
  14. pub mod routes;
  15. #[derive(Debug, Clone)]
  16. pub struct State {
  17. client: OSClient,
  18. env: Environment<'static>,
  19. base_path: String,
  20. }
  21. lazy_static::lazy_static! {
  22. pub static ref INDEX: String =
  23. std::fs::read_to_string("assets/index.html").expect("missing template");
  24. }
  25. lazy_static::lazy_static! {
  26. pub static ref FILE_CONTAINER: String =
  27. std::fs::read_to_string("assets/filecontainer.html").expect("missing template");
  28. }
  29. #[tokio::main]
  30. async fn main() {
  31. dotenv::dotenv().expect("could not load env");
  32. tracing_subscriber::fmt().init();
  33. let client = OSClient::init().await;
  34. info!("Successfully loaded client");
  35. let base_path = std::env::var("BASE_PATH").expect("base path not configured");
  36. let mut env = Environment::new();
  37. env.add_template("file_container", &FILE_CONTAINER)
  38. .expect("unable to add template");
  39. env.add_template("index", &INDEX)
  40. .expect("unable to add template");
  41. let state = State {
  42. client,
  43. env,
  44. base_path,
  45. };
  46. let router = axum::Router::new()
  47. .route("/", get(|| async { Redirect::permanent("/dir/root") }))
  48. .route("/dir/*key", get(routes::get_directory))
  49. .route("/subtitles", get(routes::search_subtitles))
  50. .route("/guess", get(routes::guess_it))
  51. .route("/download", get(routes::download_subtitles))
  52. .with_state(state);
  53. let router_static = Router::new().fallback(get_service(ServeDir::new("assets")));
  54. let listener = tokio::net::TcpListener::bind("0.0.0.0:3001").await.unwrap();
  55. axum::serve(listener, router.merge(router_static))
  56. .await
  57. .unwrap();
  58. }
  59. pub fn scan_dir_contents(path: impl AsRef<Path>) -> std::io::Result<Vec<FileType>> {
  60. let entries = fs::read_dir(path)?
  61. .filter_map(Result::ok)
  62. .collect::<Vec<_>>();
  63. let subs = entries
  64. .iter()
  65. .filter_map(|entry| {
  66. let path = entry.path();
  67. let ext = path.extension()?.to_str()?;
  68. let (name, _) = path.file_name()?.to_str()?.split_once(ext)?;
  69. (ext == "srt").then_some(name.to_owned())
  70. })
  71. .collect::<Vec<_>>();
  72. let mut files: Vec<_> = entries
  73. .iter()
  74. .filter_map(|entry| {
  75. if entry.path().is_dir() {
  76. Some(FileType::Directory(Directory {
  77. name: entry.file_name().to_str().unwrap_or_default().to_string(),
  78. path: entry.path().to_str().unwrap_or_default().to_string(),
  79. }))
  80. // Skip existing subtitles
  81. } else if entry.path().extension().is_some_and(|ext| {
  82. ext.to_str().is_some_and(|ext| ext == "srt")
  83. || ext.to_str().is_some_and(|ext| ext == "smi")
  84. }) {
  85. None
  86. } else {
  87. let path = entry.path();
  88. let ext = path.extension()?.to_str()?;
  89. let (name, _) = path.file_name()?.to_str()?.split_once(ext)?;
  90. Some(FileType::File(File {
  91. name: entry.file_name().to_str().unwrap_or_default().to_string(),
  92. path: entry.path().to_str().unwrap_or_default().to_string(),
  93. has_subs: subs.contains(&name.to_string()),
  94. }))
  95. }
  96. })
  97. .collect();
  98. files.sort();
  99. Ok(files)
  100. }
  101. #[derive(Debug, Serialize, PartialEq, Eq, PartialOrd, Ord)]
  102. pub enum FileType {
  103. Directory(Directory),
  104. File(File),
  105. }
  106. #[derive(Debug, Serialize, PartialEq, Eq, PartialOrd, Ord)]
  107. pub struct File {
  108. name: String,
  109. path: String,
  110. /// `true` if a same file exists with a `.srt` extension found
  111. /// in the same directory
  112. has_subs: bool,
  113. }
  114. #[derive(Debug, Serialize, PartialEq, Eq, PartialOrd, Ord)]
  115. pub struct Directory {
  116. name: String,
  117. path: String,
  118. }