use lumiere_domain::video::error::VideoError; use lumiere_domain::video::model::library_item::{LibraryItem, LibraryItemType, LibraryRawItem}; use lumiere_domain::video::model::video::Video; use lumiere_domain::video::port::VideoPort; use uuid::Uuid; use std::env; use std::fs; use std::fs::DirEntry; use std::io::{Error, ErrorKind}; use LibraryItemType::FOLDER; use lumiere_domain::video::model::library_item::LibraryItemType::VIDEO; use lumiere_domain::video::model::video::VideoFormat::{AVI, MKV, MP4, WEBM}; use crate::video::model::{LibraryItemEntity, TokioVideoFile}; use crate::video::repository::LibraryItemSqliteRepository; use crate::video::video_format_detector::get_content_type; #[derive(Clone)] pub struct LibraryItemAdapter { pub repository: LibraryItemSqliteRepository } impl LibraryItemAdapter { pub fn new(repository: LibraryItemSqliteRepository) -> Self { Self { repository } } fn get_root_folder_read_library_items(&self) -> Result, VideoError> { let root_folder = env::var("ROOT_FOLDER").map_err(|_| VideoError::FolderBrowsingTechnicalIssue)?; let root_folder_files = fs::read_dir(root_folder) .map_err(|_| VideoError::FolderBrowsingTechnicalIssue)?; root_folder_files.into_iter() .flat_map(Result::ok) .filter(Self::file_is_video_or_folder) .map(Self::map_file_into_library_raw_item) .collect() } fn file_is_video_or_folder(file_entry: &DirEntry) -> bool { if file_entry.path().is_dir() { true } else { let content_type = get_content_type(&file_entry.path()); matches!(content_type, AVI | MKV | MP4 | WEBM) } } fn map_file_into_library_raw_item(file: DirEntry) -> Result { let item_type = if file.path().is_dir() { FOLDER } else { LibraryItemType::VIDEO }; let library_raw_item = LibraryRawItem::new(None, file.file_name().to_string_lossy().into_owned(), item_type); Ok(library_raw_item) } fn get_all_library_items_from_db(&self) -> Result, Error> { self.repository.get_all_of_root_folder().map_err(|error| Error::new(ErrorKind::Other, "Unable to retrieve the library items of root folder from database.")) .and_then(|entities| entities.into_iter() .map(LibraryItemEntity::to_library_raw_item) .collect::, _>>()) } fn merge_item_lists(file_system_items: Vec, db_items: Vec) -> Vec { let file_system_items_which_are_not_in_database = file_system_items.into_iter() .filter(|file_system_item| !Self::does_item_belong_to_db_items(&db_items, &file_system_item)) .collect::>(); db_items.into_iter().chain(file_system_items_which_are_not_in_database).collect() } fn does_item_belong_to_db_items(database_items: &Vec, file_system_item: &LibraryRawItem) -> bool { database_items.iter() .any(|database_item| database_item.name == file_system_item.name) } fn find_library_item_by_id(&self, library_item_id: Uuid) -> Result, VideoError> { self.repository.find_by_id(library_item_id) .map_err(|_| VideoError::FileReadingTechnicalIssue) .map(|entity_opt| entity_opt.map(LibraryItemEntity::to_library_item)) } } impl VideoPort for LibraryItemAdapter { fn list_root_folder(&self) -> Result, VideoError> { let adapter1 = self.clone(); let file_system_handle = std::thread::spawn(move || { adapter1.get_root_folder_read_library_items() }); let adapter2 = self.clone(); let db_handle = std::thread::spawn(move || { adapter2.get_all_library_items_from_db() }); let file_system_items = file_system_handle.join().unwrap()?; let db_items = db_handle.join().unwrap() .map_err(|_| VideoError::FileReadingTechnicalIssue)?; let merged = Self::merge_item_lists(file_system_items, db_items); Ok(merged) } fn list_folder(&self, folder_id: Uuid) -> Result, VideoError> { todo!() } fn get_video_stream(&self, video_id: Uuid) -> Result { let video_item = self.find_library_item_by_id(video_id)?; match video_item { None => Err(VideoError::VideoDoesNotExists), Some(video) => { match video.r#type { VIDEO => { let root_folder = env::var("ROOT_FOLDER").map_err(|_| VideoError::FolderBrowsingTechnicalIssue)?; let video_file_path = String::from(format!("{root_folder}/{}", video.name)); let result = Video { id: video.id, name: video.name, format: AVI, file: Box::new(TokioVideoFile::new(&video_file_path)) }; Ok(result) }, FOLDER => Err(VideoError::VideoDoesNotExists) } } } } fn save(&self, library_item: &LibraryItem) -> Result<(), VideoError> { todo!() } fn save_all(&self, library_items: &Vec) -> Result<(), VideoError> { let entities = library_items.iter() .map(LibraryItemEntity::from_library_item) .collect::>(); self.repository.save_all(entities) .map_err(|_| VideoError::FileReadingTechnicalIssue)?; Ok(()) } fn clone_box(&self) -> Box { Box::new(self.clone()) } }