Files
lumi-re/backend/infrastructure/src/video/adapter.rs
T

154 lines
5.8 KiB
Rust

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<Vec<LibraryRawItem>, 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<LibraryRawItem, VideoError> {
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<Vec<LibraryRawItem>, 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::<Result<Vec<LibraryRawItem>, _>>())
}
fn merge_item_lists(file_system_items: Vec<LibraryRawItem>, db_items: Vec<LibraryRawItem>) -> Vec<LibraryRawItem> {
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::<Vec<LibraryRawItem>>();
db_items.into_iter().chain(file_system_items_which_are_not_in_database).collect()
}
fn does_item_belong_to_db_items(database_items: &Vec<LibraryRawItem>, 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<Option<LibraryItem>, 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<Vec<LibraryRawItem>, 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<Vec<LibraryRawItem>, VideoError> {
todo!()
}
fn get_video_stream(&self, video_id: Uuid) -> Result<Video, VideoError> {
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<LibraryItem>) -> Result<(), VideoError> {
let entities = library_items.iter()
.map(LibraryItemEntity::from_library_item)
.collect::<Vec<LibraryItemEntity>>();
self.repository.save_all(entities)
.map_err(|_| VideoError::FileReadingTechnicalIssue)?;
Ok(())
}
fn clone_box(&self) -> Box<dyn VideoPort> {
Box::new(self.clone())
}
}