static_video_server/
lib.rs1use clap::Parser;
2use std::{
3 collections::{HashMap, HashSet},
4 path::PathBuf,
5 sync::{
6 atomic::{AtomicUsize, Ordering},
7 Arc, Mutex,
8 },
9};
10use tracing::log::info;
11use lazy_static::lazy_static;
12
13lazy_static! {
14 pub static ref VIDEO_EXTENSIONS: Vec<String> = vec![
15 "mp4".into(),
16 "avi".into(),
17 "flv".into(),
18 "heic".into(),
19 "mkv".into(),
20 "mov".into(),
21 "mpg".into(),
22 "mpeg".into(),
23 "m4v".into(),
24 "webm".into(),
25 "wmv".into(),
26 "3gp".into()
27 ];
28}
29
30
31
32#[derive(Parser, Debug, Clone)]
34pub struct VideoPlayerConfig {
35 #[clap(short, long, default_value = "assets")]
36 pub assets_root: String,
37
38 #[clap(short, long, default_value = "9092")]
39 pub port: u16,
40
41 #[clap(short, long, default_value = "0.0.0.0")]
42 pub host: String,
43}
44
45#[derive(Default)]
48pub struct VideoPlayerState {
49 pub videos: HashMap<String, String>,
50 video_extensions: HashSet<String>,
51 next_index: AtomicUsize,
52 root: Option<String>,
53}
54
55pub type SharedState = Arc<Mutex<VideoPlayerState>>;
56
57impl VideoPlayerState {
58 pub fn new() -> Self {
61 Self {
62 video_extensions: HashSet::from_iter(
63 VIDEO_EXTENSIONS.iter().map(|s| s.to_string()),
64 ),
65 ..Default::default()
66 }
67 }
68
69
70 fn advance_index(&mut self) {
71 self.next_index
72 .fetch_add(1, std::sync::atomic::Ordering::Relaxed);
73 }
74
75 pub fn is_video_file<P: AsRef<std::path::Path>>(&self, path: P) -> bool {
77 if let Some(extension) = path.as_ref().extension() {
78 if self.video_extensions.contains(extension.to_str().unwrap()) {
79 return true;
80 }
81 }
82 false
83 }
84
85 pub fn load_videos<P: AsRef<std::path::Path>>(&mut self, root: P) -> std::io::Result<()> {
86 self.visit_dirs(root)
87 }
88
89 pub fn load_video(&mut self, path: PathBuf) {
91 let stored_file_name = path.to_str().unwrap().to_string();
92 let extension = path.extension().unwrap();
93 let server_path = format!(
94 "{}.{}",
95 self.next_index.load(Ordering::SeqCst),
96 extension.to_str().unwrap()
97 );
98 info!("Loading video: {} as {}", stored_file_name, server_path);
99 self.advance_index();
100 self.videos.insert(server_path, stored_file_name);
101 }
102
103 pub fn visit_dirs<P: AsRef<std::path::Path>>(&mut self, root: P) -> std::io::Result<()> {
105 if root.as_ref().is_dir() {
106 if let Ok(dir) = std::fs::read_dir(root.as_ref()) {
107 for entry in dir {
108 let entry = entry?;
109 let path = entry.path();
110 if path.is_dir() {
111 self.visit_dirs(path)?;
112 } else if self.is_video_file(path.as_path()) {
113 self.load_video(path);
114 }
115 }
116 }
117 }
118 Ok(())
119 }
120
121 pub fn build(config: &VideoPlayerConfig) -> Self {
123 let mut state = Self::new();
124 state.root = Some(config.assets_root.clone());
125 state.load_videos(state.root.clone().unwrap()).unwrap();
126 state
127 }
128
129 pub fn reload(&mut self) {
131 self.next_index = AtomicUsize::new(0);
132 self.videos.clear();
133 self.load_videos(self.root.clone().unwrap()).unwrap();
134 }
135}