Compare commits
6
Commits
36eec1e694
...
edb87ce98a
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
edb87ce98a | ||
|
|
8e04be8ece | ||
|
|
437314f304 | ||
|
|
ad44b181cb | ||
|
|
4abd7dc5b9 | ||
|
|
56d5abe964 |
@@ -20,11 +20,23 @@ impl VideoService {
|
||||
|
||||
pub fn list_root_folder(&self) -> Result<Vec<LibraryItem>, VideoError> {
|
||||
let read_items_with_possibly_no_id = self.video_port.list_root_folder()?;
|
||||
let items_with_id = read_items_with_possibly_no_id.iter()
|
||||
|
||||
let new_items = read_items_with_possibly_no_id.iter()
|
||||
.filter(|item| item.id.is_none())
|
||||
.map(LibraryRawItem::map_to_library_item)
|
||||
.collect();
|
||||
self.video_port.save_all(&items_with_id)?;
|
||||
Ok(items_with_id)
|
||||
.collect::<Vec<LibraryItem>>();
|
||||
|
||||
let already_existing_items = read_items_with_possibly_no_id.iter()
|
||||
.filter(|item| item.id.is_some())
|
||||
.map(LibraryRawItem::map_to_library_item)
|
||||
.collect::<Vec<LibraryItem>>();
|
||||
|
||||
if !new_items.is_empty() {
|
||||
self.video_port.save_all(&new_items)?;
|
||||
}
|
||||
|
||||
let all_items = already_existing_items.into_iter().chain(new_items).collect();
|
||||
Ok(all_items)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,18 +11,18 @@ pub fn initialise_database_tables(connection: &Arc<Mutex<Connection>>) -> Result
|
||||
id TEXT PRIMARY KEY,
|
||||
initialisation_code VARCHAR
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS system_resources (
|
||||
id TEXT PRIMARY KEY,
|
||||
datetime INTEGER NOT NULL,
|
||||
cpu_usage INTEGER NOT NULL,
|
||||
total_memory INTEGER NOT NULL,
|
||||
used_memory INTEGER NOT NULL
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS \"user\" (
|
||||
id TEXT PRIMARY KEY,
|
||||
username VARCHAR NOT NULL,
|
||||
encrypted_password VARCHAR NOT NULL
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS library_item (
|
||||
id TEXT PRIMARY KEY,
|
||||
name VARCHAR NOT NULL,
|
||||
type INTEGER NOT NULL,
|
||||
parent_folder_id TEXT NULL,
|
||||
FOREIGN KEY (parent_folder_id) REFERENCES library_item (id)
|
||||
);
|
||||
COMMIT;
|
||||
"
|
||||
)?;
|
||||
|
||||
@@ -6,17 +6,20 @@ use uuid::Uuid;
|
||||
use std::env;
|
||||
use std::fs;
|
||||
use std::fs::DirEntry;
|
||||
use std::io::{Error, ErrorKind};
|
||||
use lumiere_domain::video::model::video::VideoFormat::{AVI, MKV, MP4, WEBM};
|
||||
use crate::video::model::LibraryItemEntity;
|
||||
use crate::video::repository::LibraryItemSqliteRepository;
|
||||
use crate::video::video_format_detector::get_content_type;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct VideoAdapter {
|
||||
|
||||
pub struct LibraryItemAdapter {
|
||||
pub repository: LibraryItemSqliteRepository
|
||||
}
|
||||
|
||||
impl VideoAdapter {
|
||||
pub fn new() -> Self {
|
||||
Self {}
|
||||
impl LibraryItemAdapter {
|
||||
pub fn new(repository: LibraryItemSqliteRepository) -> Self {
|
||||
Self { repository }
|
||||
}
|
||||
|
||||
fn get_root_folder_read_library_items(&self) -> Result<Vec<LibraryRawItem>, VideoError> {
|
||||
@@ -52,28 +55,42 @@ impl VideoAdapter {
|
||||
Ok(library_raw_item)
|
||||
}
|
||||
|
||||
fn get_all_library_items_from_db(&self) -> Result<Vec<LibraryRawItem>, VideoError> {
|
||||
Ok(vec!())
|
||||
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: Vec<LibraryRawItem> = file_system_items.into_iter()
|
||||
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();
|
||||
.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.id == file_system_item.id)
|
||||
.any(|database_item| database_item.name == file_system_item.name)
|
||||
}
|
||||
}
|
||||
|
||||
impl VideoPort for VideoAdapter {
|
||||
impl VideoPort for LibraryItemAdapter {
|
||||
fn list_root_folder(&self) -> Result<Vec<LibraryRawItem>, VideoError> {
|
||||
let file_system_items = self.get_root_folder_read_library_items()?;
|
||||
let db_items = self.get_all_library_items_from_db()?;
|
||||
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);
|
||||
|
||||
@@ -93,6 +110,12 @@ impl VideoPort for VideoAdapter {
|
||||
}
|
||||
|
||||
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(())
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
pub mod adapter;
|
||||
pub mod model;
|
||||
pub mod video_format_detector;
|
||||
pub mod repository;
|
||||
@@ -1,4 +1,7 @@
|
||||
use std::fs::File;
|
||||
use std::io::{Error, ErrorKind};
|
||||
use uuid::Uuid;
|
||||
use lumiere_domain::video::model::library_item::{LibraryItem, LibraryItemType, LibraryRawItem};
|
||||
|
||||
pub struct TokioVideoFile {
|
||||
pub file: File
|
||||
@@ -11,3 +14,46 @@ impl TokioVideoFile {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub struct LibraryItemEntity {
|
||||
pub id: Uuid,
|
||||
pub name: String,
|
||||
pub r#type: i8,
|
||||
pub parent_folder_id: Option<Uuid>
|
||||
}
|
||||
|
||||
impl LibraryItemEntity {
|
||||
pub fn from_library_item(library_item: &LibraryItem) -> Self {
|
||||
Self {
|
||||
id: library_item.id.clone(),
|
||||
name: library_item.name.clone(),
|
||||
r#type: Self::map_type_to_i8(library_item.r#type),
|
||||
parent_folder_id: None
|
||||
}
|
||||
}
|
||||
|
||||
fn map_type_to_i8(item_type: LibraryItemType) -> i8 {
|
||||
match item_type {
|
||||
LibraryItemType::VIDEO => 0,
|
||||
LibraryItemType::FOLDER => 1
|
||||
}
|
||||
}
|
||||
|
||||
fn map_type_from_i8(item_type_number: i8) -> Result<LibraryItemType, Error> {
|
||||
match item_type_number {
|
||||
0 => Ok(LibraryItemType::VIDEO),
|
||||
1 => Ok(LibraryItemType::FOLDER),
|
||||
unknown_value => Err(Error::new(ErrorKind::InvalidData, format!("Unknown library item type value from database: {unknown_value}")))
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn to_library_raw_item(self) -> Result<LibraryRawItem, Error> {
|
||||
let library_item = LibraryRawItem {
|
||||
id: Some(self.id.clone()),
|
||||
name: self.name.clone(),
|
||||
r#type: Self::map_type_from_i8(self.r#type)?
|
||||
};
|
||||
Ok(library_item)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
use std::sync::{Arc, Mutex, MutexGuard};
|
||||
use rusqlite::{Connection, Error, Row};
|
||||
use rusqlite::fallible_iterator::FallibleIterator;
|
||||
use uuid::Uuid;
|
||||
use crate::video::model::LibraryItemEntity;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct LibraryItemSqliteRepository {
|
||||
pub database_connection: Arc<Mutex<Connection>>,
|
||||
}
|
||||
|
||||
impl LibraryItemSqliteRepository {
|
||||
pub fn new(database_connection: Arc<Mutex<Connection>>) -> Self {
|
||||
Self {
|
||||
database_connection,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_connection(&self) -> MutexGuard<'_, Connection> {
|
||||
self.database_connection.lock().unwrap()
|
||||
}
|
||||
|
||||
pub fn create_table(&self) -> Result<(), rusqlite::Error> {
|
||||
let connection = self.get_connection();
|
||||
connection.execute(
|
||||
"CREATE TABLE IF NOT EXISTS library_item (
|
||||
id TEXT PRIMARY KEY,
|
||||
name VARCHAR NOT NULL,
|
||||
type INTEGER NOT NULL,
|
||||
parent_folder_id TEXT NULL FOREIGN KEY REFERENCES library_item (id)
|
||||
);",
|
||||
[]
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_all_of_root_folder(&self) -> Result<Vec<LibraryItemEntity>, Error> {
|
||||
let database_connection = self.get_connection();
|
||||
|
||||
let mut prepared_statement = database_connection.prepare(
|
||||
"SELECT
|
||||
id,
|
||||
name,
|
||||
type
|
||||
FROM library_item
|
||||
WHERE parent_folder_id IS NULL",
|
||||
)?;
|
||||
let rows = prepared_statement.query([])?;
|
||||
rows.map(Self::map_to_library_item).collect::<Vec<LibraryItemEntity>>()
|
||||
}
|
||||
|
||||
fn map_to_library_item(row: &Row) -> Result<LibraryItemEntity, Error> {
|
||||
let id_text: String = row.get(0)?;
|
||||
let type_as_number: i8 = row.get(2)?;
|
||||
Ok(
|
||||
LibraryItemEntity {
|
||||
id: Uuid::parse_str(&id_text).unwrap(),
|
||||
name: row.get(1)?,
|
||||
r#type: type_as_number,
|
||||
parent_folder_id: None
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
pub fn save_all(&self, entities: Vec<LibraryItemEntity>) -> Result<(), Error> {
|
||||
let database_connection = self.get_connection();
|
||||
|
||||
let mut query = String::from("INSERT INTO library_item (id, name, type) VALUES ");
|
||||
let mut params: Vec<rusqlite::types::Value> = Vec::new();
|
||||
|
||||
for (i, entity) in entities.iter().enumerate() {
|
||||
if i > 0 {
|
||||
query.push_str(", ");
|
||||
}
|
||||
query.push_str(&format!("(${}, ${}, ${})", i * 3 + 1, i * 3 + 2, i * 3 + 3));
|
||||
params.push(entity.id.to_string().into());
|
||||
params.push(entity.name.clone().into());
|
||||
params.push(entity.r#type.into());
|
||||
}
|
||||
|
||||
let mut prepared_statement = database_connection
|
||||
.prepare(&query)
|
||||
.expect("Unable to prepare statement for user saving…");
|
||||
|
||||
prepared_statement.execute(rusqlite::params_from_iter(params))?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -9,10 +9,11 @@ use lumiere_infrastructure::configuration::adapter::ConfigurationSqliteAdapter;
|
||||
use lumiere_infrastructure::configuration::repository::ConfigurationSqliteRepository;
|
||||
use lumiere_infrastructure::user::diesel_adapter::UserDieselAdapter;
|
||||
use lumiere_infrastructure::user::diesel_repository::UserDieselRepository;
|
||||
use lumiere_infrastructure::video::adapter::VideoAdapter;
|
||||
use lumiere_infrastructure::video::adapter::LibraryItemAdapter;
|
||||
use rusqlite::Connection;
|
||||
use std::io::{Error, ErrorKind};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use lumiere_infrastructure::video::repository::LibraryItemSqliteRepository;
|
||||
|
||||
pub struct ApplicationContext {
|
||||
pub database_connection: Arc<Mutex<Connection>>,
|
||||
@@ -55,7 +56,8 @@ impl ApplicationContext {
|
||||
// let user_adapter = Box::new(UserSqliteAdapter::new(user_repository.clone()));
|
||||
let user_service = UserService::new(configuration_service.clone(), user_adapter.clone());
|
||||
|
||||
let video_adapter = Box::new(VideoAdapter::new());
|
||||
let library_item_repository = LibraryItemSqliteRepository::new(arc_mutex_connection.clone());
|
||||
let video_adapter = Box::new(LibraryItemAdapter::new(library_item_repository));
|
||||
let video_service = VideoService::new(video_adapter.clone());
|
||||
|
||||
Self {
|
||||
|
||||
Reference in New Issue
Block a user