Compare commits
9
Commits
edb87ce98a
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5aa894912d | ||
|
|
00c24bca94 | ||
|
|
b6ab469616 | ||
|
|
cef96884c6 | ||
|
|
f8f86eeae1 | ||
|
|
c3806f49ba | ||
|
|
59b7164007 | ||
|
|
35ef389bb8 | ||
|
|
360df2c5f4 |
@@ -6,7 +6,9 @@ use std::path::Path;
|
|||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
use lumiere_domain::video::error::VideoError;
|
use lumiere_domain::video::error::VideoError;
|
||||||
use lumiere_domain::video::model::library_item::{LibraryItem, LibraryRawItem};
|
use lumiere_domain::video::model::library_item::{LibraryItem, LibraryRawItem};
|
||||||
|
use lumiere_domain::video::model::video::Video;
|
||||||
use lumiere_domain::video::port::VideoPort;
|
use lumiere_domain::video::port::VideoPort;
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct VideoService {
|
pub struct VideoService {
|
||||||
@@ -38,6 +40,11 @@ impl VideoService {
|
|||||||
let all_items = already_existing_items.into_iter().chain(new_items).collect();
|
let all_items = already_existing_items.into_iter().chain(new_items).collect();
|
||||||
Ok(all_items)
|
Ok(all_items)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_video_stream(&self, video_id: Uuid) -> Result<Video, VideoError> {
|
||||||
|
self.video_port.get_video_stream(video_id)
|
||||||
|
.map_err(|_| VideoError::VideoDoesNotExists)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
#[derive(Debug)]
|
||||||
pub enum VideoError {
|
pub enum VideoError {
|
||||||
VideoDoesNotExists,
|
VideoDoesNotExists,
|
||||||
FolderDoesNotExists,
|
FolderDoesNotExists,
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use std::any::Any;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
pub enum VideoFormat {
|
pub enum VideoFormat {
|
||||||
@@ -8,7 +9,9 @@ pub enum VideoFormat {
|
|||||||
UNKNOWN
|
UNKNOWN
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait VideoFile {}
|
pub trait VideoFile: Any {
|
||||||
|
fn as_any(&self) -> &dyn Any;
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Video {
|
pub struct Video {
|
||||||
pub id: Uuid,
|
pub id: Uuid,
|
||||||
|
|||||||
Generated
+457
@@ -641,6 +641,41 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "darling"
|
||||||
|
version = "0.21.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9cdf337090841a411e2a7f3deb9187445851f91b309c0c0a29e05f74a00a48c0"
|
||||||
|
dependencies = [
|
||||||
|
"darling_core",
|
||||||
|
"darling_macro",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "darling_core"
|
||||||
|
version = "0.21.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1247195ecd7e3c85f83c8d2a366e4210d588e802133e1e355180a9870b517ea4"
|
||||||
|
dependencies = [
|
||||||
|
"fnv",
|
||||||
|
"ident_case",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"strsim",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "darling_macro"
|
||||||
|
version = "0.21.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81"
|
||||||
|
dependencies = [
|
||||||
|
"darling_core",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "der"
|
name = "der"
|
||||||
version = "0.7.10"
|
version = "0.7.10"
|
||||||
@@ -684,6 +719,52 @@ dependencies = [
|
|||||||
"unicode-xid",
|
"unicode-xid",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "diesel"
|
||||||
|
version = "2.3.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e54d1f576cd3a3460f212a4615fd12ce1b6303c095b79a44449ffbe627753dc1"
|
||||||
|
dependencies = [
|
||||||
|
"diesel_derives",
|
||||||
|
"downcast-rs",
|
||||||
|
"libsqlite3-sys",
|
||||||
|
"sqlite-wasm-rs",
|
||||||
|
"time",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "diesel_derives"
|
||||||
|
version = "2.3.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d1817b7f4279b947fc4cafddec12b0e5f8727141706561ce3ac94a60bddd1cf5"
|
||||||
|
dependencies = [
|
||||||
|
"diesel_table_macro_syntax",
|
||||||
|
"dsl_auto_type",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "diesel_migrations"
|
||||||
|
version = "2.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "28d0f4a98124ba6d4ca75da535f65984badec16a003b6e2f94a01e31a79490b8"
|
||||||
|
dependencies = [
|
||||||
|
"diesel",
|
||||||
|
"migrations_internals",
|
||||||
|
"migrations_macros",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "diesel_table_macro_syntax"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fe2444076b48641147115697648dc743c2c00b61adade0f01ce67133c7babe8c"
|
||||||
|
dependencies = [
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "digest"
|
name = "digest"
|
||||||
version = "0.10.7"
|
version = "0.10.7"
|
||||||
@@ -707,6 +788,16 @@ dependencies = [
|
|||||||
"crypto-common 0.2.2",
|
"crypto-common 0.2.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dispatch2"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"objc2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "displaydoc"
|
name = "displaydoc"
|
||||||
version = "0.2.5"
|
version = "0.2.5"
|
||||||
@@ -718,6 +809,26 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "downcast-rs"
|
||||||
|
version = "2.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "117240f60069e65410b3ae1bb213295bd828f707b5bec6596a1afc8793ce0cbc"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dsl_auto_type"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dd122633e4bef06db27737f21d3738fb89c8f6d5360d6d9d7635dda142a7757e"
|
||||||
|
dependencies = [
|
||||||
|
"darling",
|
||||||
|
"either",
|
||||||
|
"heck",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ecdsa"
|
name = "ecdsa"
|
||||||
version = "0.16.9"
|
version = "0.16.9"
|
||||||
@@ -756,6 +867,12 @@ dependencies = [
|
|||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "either"
|
||||||
|
version = "1.16.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "elliptic-curve"
|
name = "elliptic-curve"
|
||||||
version = "0.13.8"
|
version = "0.13.8"
|
||||||
@@ -802,6 +919,18 @@ dependencies = [
|
|||||||
"windows-sys 0.61.2",
|
"windows-sys 0.61.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fallible-iterator"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fallible-streaming-iterator"
|
||||||
|
version = "0.1.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ff"
|
name = "ff"
|
||||||
version = "0.13.1"
|
version = "0.13.1"
|
||||||
@@ -999,12 +1128,30 @@ dependencies = [
|
|||||||
"foldhash 0.1.5",
|
"foldhash 0.1.5",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashbrown"
|
||||||
|
version = "0.16.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
|
||||||
|
dependencies = [
|
||||||
|
"foldhash 0.2.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "hashbrown"
|
||||||
version = "0.17.0"
|
version = "0.17.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51"
|
checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashlink"
|
||||||
|
version = "0.11.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "824e001ac4f3012dd16a264bec811403a67ca9deb6c102fc5049b32c4574b35f"
|
||||||
|
dependencies = [
|
||||||
|
"hashbrown 0.16.1",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "heck"
|
name = "heck"
|
||||||
version = "0.5.0"
|
version = "0.5.0"
|
||||||
@@ -1241,6 +1388,12 @@ version = "2.3.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954"
|
checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ident_case"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "idna"
|
name = "idna"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
@@ -1378,6 +1531,17 @@ version = "0.2.16"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981"
|
checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libsqlite3-sys"
|
||||||
|
version = "0.37.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b1f111c8c41e7c61a49cd34e44c7619462967221a6443b0ec299e0ac30cfb9b1"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"pkg-config",
|
||||||
|
"vcpkg",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "litemap"
|
name = "litemap"
|
||||||
version = "0.8.2"
|
version = "0.8.2"
|
||||||
@@ -1453,12 +1617,29 @@ dependencies = [
|
|||||||
"jsonwebtoken",
|
"jsonwebtoken",
|
||||||
"lumiere-application",
|
"lumiere-application",
|
||||||
"lumiere-domain",
|
"lumiere-domain",
|
||||||
|
"lumiere-infrastructure",
|
||||||
"serde",
|
"serde",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-util",
|
"tokio-util",
|
||||||
"uuid",
|
"uuid",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lumiere-infrastructure"
|
||||||
|
version = "1.0.0"
|
||||||
|
dependencies = [
|
||||||
|
"chrono",
|
||||||
|
"diesel",
|
||||||
|
"diesel_migrations",
|
||||||
|
"log",
|
||||||
|
"lumiere-domain",
|
||||||
|
"rand 0.10.2",
|
||||||
|
"rusqlite",
|
||||||
|
"sysinfo",
|
||||||
|
"tokio",
|
||||||
|
"uuid",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "matchit"
|
name = "matchit"
|
||||||
version = "0.8.4"
|
version = "0.8.4"
|
||||||
@@ -1471,6 +1652,27 @@ version = "2.8.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
|
checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "migrations_internals"
|
||||||
|
version = "2.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "36c791ecdf977c99f45f23280405d7723727470f6689a5e6dbf513ac547ae10d"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
"toml",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "migrations_macros"
|
||||||
|
version = "2.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "36fc5ac76be324cfd2d3f2cf0fdf5d5d3c4f14ed8aaebadb09e304ba42282703"
|
||||||
|
dependencies = [
|
||||||
|
"migrations_internals",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mime"
|
name = "mime"
|
||||||
version = "0.3.17"
|
version = "0.3.17"
|
||||||
@@ -1499,6 +1701,15 @@ dependencies = [
|
|||||||
"windows-sys 0.61.2",
|
"windows-sys 0.61.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ntapi"
|
||||||
|
version = "0.4.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c3b335231dfd352ffb0f8017f3b6027a4917f7df785ea2143d8af2adc66980ae"
|
||||||
|
dependencies = [
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-bigint"
|
name = "num-bigint"
|
||||||
version = "0.4.6"
|
version = "0.4.6"
|
||||||
@@ -1561,6 +1772,63 @@ dependencies = [
|
|||||||
"libm",
|
"libm",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "objc2"
|
||||||
|
version = "0.6.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3a12a8ed07aefc768292f076dc3ac8c48f3781c8f2d5851dd3d98950e8c5a89f"
|
||||||
|
dependencies = [
|
||||||
|
"objc2-encode",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "objc2-core-foundation"
|
||||||
|
version = "0.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"dispatch2",
|
||||||
|
"objc2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "objc2-encode"
|
||||||
|
version = "4.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "objc2-foundation"
|
||||||
|
version = "0.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"objc2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "objc2-io-kit"
|
||||||
|
version = "0.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "33fafba39597d6dc1fb709123dfa8289d39406734be322956a69f0931c73bb15"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"objc2-core-foundation",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "objc2-open-directory"
|
||||||
|
version = "0.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bb82bed227edf5201dfedf072bba4015a33d3d4a98519837295a90f0a23f676d"
|
||||||
|
dependencies = [
|
||||||
|
"objc2",
|
||||||
|
"objc2-core-foundation",
|
||||||
|
"objc2-foundation",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "once_cell"
|
name = "once_cell"
|
||||||
version = "1.21.4"
|
version = "1.21.4"
|
||||||
@@ -1880,6 +2148,31 @@ dependencies = [
|
|||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rsqlite-vfs"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c51c9ae4df8a7fba42103df5c621fa3c37eccf3a3c650879e90fc48b11cc192c"
|
||||||
|
dependencies = [
|
||||||
|
"hashbrown 0.16.1",
|
||||||
|
"thiserror",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rusqlite"
|
||||||
|
version = "0.39.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a0d2b0146dd9661bf67bb107c0bb2a55064d556eeb3fc314151b957f313bcd4e"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"fallible-iterator",
|
||||||
|
"fallible-streaming-iterator",
|
||||||
|
"hashlink",
|
||||||
|
"libsqlite3-sys",
|
||||||
|
"smallvec",
|
||||||
|
"sqlite-wasm-rs",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc_version"
|
name = "rustc_version"
|
||||||
version = "0.4.1"
|
version = "0.4.1"
|
||||||
@@ -2014,6 +2307,15 @@ dependencies = [
|
|||||||
"serde_core",
|
"serde_core",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_spanned"
|
||||||
|
version = "1.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26"
|
||||||
|
dependencies = [
|
||||||
|
"serde_core",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_urlencoded"
|
name = "serde_urlencoded"
|
||||||
version = "0.7.1"
|
version = "0.7.1"
|
||||||
@@ -2140,12 +2442,30 @@ dependencies = [
|
|||||||
"der",
|
"der",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sqlite-wasm-rs"
|
||||||
|
version = "0.5.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dc3efc0da82635d7e1ced0053bbbfa8c7ab9645d0bf36ceb4f7127bb85315d75"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"js-sys",
|
||||||
|
"rsqlite-vfs",
|
||||||
|
"wasm-bindgen",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "stable_deref_trait"
|
name = "stable_deref_trait"
|
||||||
version = "1.2.1"
|
version = "1.2.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
|
checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "strsim"
|
||||||
|
version = "0.11.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "subtle"
|
name = "subtle"
|
||||||
version = "2.6.1"
|
version = "2.6.1"
|
||||||
@@ -2180,6 +2500,21 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sysinfo"
|
||||||
|
version = "0.39.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d2071df9448915b71c4fe6d25deaf1c22f12bd234f01540b77312bb8e41361e6"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"memchr",
|
||||||
|
"ntapi",
|
||||||
|
"objc2-core-foundation",
|
||||||
|
"objc2-io-kit",
|
||||||
|
"objc2-open-directory",
|
||||||
|
"windows",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "2.0.18"
|
version = "2.0.18"
|
||||||
@@ -2292,6 +2627,37 @@ dependencies = [
|
|||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml"
|
||||||
|
version = "0.9.12+spec-1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cf92845e79fc2e2def6a5d828f0801e29a2f8acc037becc5ab08595c7d5e9863"
|
||||||
|
dependencies = [
|
||||||
|
"serde_core",
|
||||||
|
"serde_spanned",
|
||||||
|
"toml_datetime",
|
||||||
|
"toml_parser",
|
||||||
|
"winnow 0.7.15",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml_datetime"
|
||||||
|
version = "0.7.5+spec-1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347"
|
||||||
|
dependencies = [
|
||||||
|
"serde_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml_parser"
|
||||||
|
version = "1.1.2+spec-1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526"
|
||||||
|
dependencies = [
|
||||||
|
"winnow 1.0.4",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tower"
|
name = "tower"
|
||||||
version = "0.5.3"
|
version = "0.5.3"
|
||||||
@@ -2412,6 +2778,12 @@ dependencies = [
|
|||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "vcpkg"
|
||||||
|
version = "0.2.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "version_check"
|
name = "version_check"
|
||||||
version = "0.9.5"
|
version = "0.9.5"
|
||||||
@@ -2521,6 +2893,49 @@ dependencies = [
|
|||||||
"semver",
|
"semver",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi"
|
||||||
|
version = "0.3.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||||
|
dependencies = [
|
||||||
|
"winapi-i686-pc-windows-gnu",
|
||||||
|
"winapi-x86_64-pc-windows-gnu",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-i686-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-x86_64-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows"
|
||||||
|
version = "0.62.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580"
|
||||||
|
dependencies = [
|
||||||
|
"windows-collections",
|
||||||
|
"windows-core",
|
||||||
|
"windows-future",
|
||||||
|
"windows-numerics",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-collections"
|
||||||
|
version = "0.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610"
|
||||||
|
dependencies = [
|
||||||
|
"windows-core",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-core"
|
name = "windows-core"
|
||||||
version = "0.62.2"
|
version = "0.62.2"
|
||||||
@@ -2534,6 +2949,17 @@ dependencies = [
|
|||||||
"windows-strings",
|
"windows-strings",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-future"
|
||||||
|
version = "0.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb"
|
||||||
|
dependencies = [
|
||||||
|
"windows-core",
|
||||||
|
"windows-link",
|
||||||
|
"windows-threading",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-implement"
|
name = "windows-implement"
|
||||||
version = "0.60.2"
|
version = "0.60.2"
|
||||||
@@ -2562,6 +2988,16 @@ version = "0.2.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-numerics"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26"
|
||||||
|
dependencies = [
|
||||||
|
"windows-core",
|
||||||
|
"windows-link",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-result"
|
name = "windows-result"
|
||||||
version = "0.4.1"
|
version = "0.4.1"
|
||||||
@@ -2614,6 +3050,15 @@ dependencies = [
|
|||||||
"windows_x86_64_msvc",
|
"windows_x86_64_msvc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-threading"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37"
|
||||||
|
dependencies = [
|
||||||
|
"windows-link",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_aarch64_gnullvm"
|
name = "windows_aarch64_gnullvm"
|
||||||
version = "0.52.6"
|
version = "0.52.6"
|
||||||
@@ -2662,6 +3107,18 @@ version = "0.52.6"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winnow"
|
||||||
|
version = "0.7.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winnow"
|
||||||
|
version = "1.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "23b97319f7b8343df12cc98938e5c3eb436064524c8d2b4e30a1d3a36eecdf81"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wit-bindgen"
|
name = "wit-bindgen"
|
||||||
version = "0.51.0"
|
version = "0.51.0"
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ path = "src/lib.rs"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
lumiere-domain = { path = "../domain" }
|
lumiere-domain = { path = "../domain" }
|
||||||
lumiere-application = { path = "../application" }
|
lumiere-application = { path = "../application" }
|
||||||
|
lumiere-infrastructure = { path = "../infrastructure" }
|
||||||
|
|
||||||
actix-web = { version = "4.14.0", features = ["rustls-0_23"] }
|
actix-web = { version = "4.14.0", features = ["rustls-0_23"] }
|
||||||
actix-web-httpauth = "0.8.2"
|
actix-web-httpauth = "0.8.2"
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ extern crate core;
|
|||||||
|
|
||||||
use actix_web::web::ServiceConfig;
|
use actix_web::web::ServiceConfig;
|
||||||
use actix_web::{get, web, HttpResponse, Responder};
|
use actix_web::{get, web, HttpResponse, Responder};
|
||||||
use crate::video::handler::{download_file, get_root_folder_content};
|
use crate::video::handler::{download_file, get_root_folder_content, get_video_stream};
|
||||||
|
|
||||||
pub mod user;
|
pub mod user;
|
||||||
pub mod video;
|
pub mod video;
|
||||||
@@ -14,6 +14,7 @@ async fn health() -> impl Responder {
|
|||||||
|
|
||||||
pub fn init(cfg: &mut ServiceConfig) {
|
pub fn init(cfg: &mut ServiceConfig) {
|
||||||
cfg.service(download_file)
|
cfg.service(download_file)
|
||||||
|
.service(get_video_stream)
|
||||||
.service(get_root_folder_content);
|
.service(get_root_folder_content);
|
||||||
cfg.default_service(web::to(not_found));
|
cfg.default_service(web::to(not_found));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,10 +3,12 @@ use actix_web::{get, web, HttpResponse, Responder};
|
|||||||
use tokio::fs::File;
|
use tokio::fs::File;
|
||||||
use tokio_util::codec::{BytesCodec, FramedRead};
|
use tokio_util::codec::{BytesCodec, FramedRead};
|
||||||
use futures_util::stream::StreamExt;
|
use futures_util::stream::StreamExt;
|
||||||
|
use uuid::Uuid;
|
||||||
use lumiere_application::video::service::VideoService;
|
use lumiere_application::video::service::VideoService;
|
||||||
|
use lumiere_infrastructure::video::model::TokioVideoFile;
|
||||||
use crate::video::model::LibraryItemDto;
|
use crate::video::model::LibraryItemDto;
|
||||||
|
|
||||||
#[get("/download")]
|
#[get("/api/download")]
|
||||||
pub async fn download_file() -> impl Responder {
|
pub async fn download_file() -> impl Responder {
|
||||||
HttpResponse::Ok()
|
HttpResponse::Ok()
|
||||||
.content_type("application/octet-stream")
|
.content_type("application/octet-stream")
|
||||||
@@ -19,7 +21,41 @@ pub async fn download_file() -> impl Responder {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/library")]
|
#[get("/api/videos/{video_id}/stream")]
|
||||||
|
pub async fn get_video_stream(
|
||||||
|
video_service: web::Data<Arc<VideoService>>,
|
||||||
|
video_id: web::Path<Uuid>
|
||||||
|
) -> impl Responder {
|
||||||
|
let video_result = video_service.get_video_stream(video_id.into_inner());
|
||||||
|
|
||||||
|
match video_result {
|
||||||
|
Ok(video) => {
|
||||||
|
if let Some(tokio_video_file) = video.file.as_any().downcast_ref::<TokioVideoFile>() {
|
||||||
|
// Clone the file to extend its lifetime into the streaming closure
|
||||||
|
let cloned_file = tokio_video_file.file.try_clone();
|
||||||
|
|
||||||
|
match cloned_file {
|
||||||
|
Ok(file) => HttpResponse::Ok()
|
||||||
|
.content_type("application/octet-stream")
|
||||||
|
.streaming(async_stream::stream! {
|
||||||
|
let file = tokio::fs::File::from_std(file);
|
||||||
|
let mut stream = FramedRead::new(file, BytesCodec::new());
|
||||||
|
while let Some(chunk) = stream.next().await {
|
||||||
|
yield chunk.map(|bytes| bytes.freeze());
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
Err(_) => HttpResponse::InternalServerError().body("Failed to clone video file")
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
HttpResponse::InternalServerError().body("Video file format is not handled.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(error) => HttpResponse::InternalServerError().body(format!("{:?}", error))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[get("/api/library")]
|
||||||
pub async fn get_root_folder_content(
|
pub async fn get_root_folder_content(
|
||||||
video_service: web::Data<Arc<VideoService>>
|
video_service: web::Data<Arc<VideoService>>
|
||||||
) -> impl Responder {
|
) -> impl Responder {
|
||||||
|
|||||||
@@ -17,5 +17,5 @@ log = "0.4.33"
|
|||||||
rand = "0.10.2"
|
rand = "0.10.2"
|
||||||
rusqlite = { version = "0.39.0", features = ["bundled"] }
|
rusqlite = { version = "0.39.0", features = ["bundled"] }
|
||||||
sysinfo = "0.39.6"
|
sysinfo = "0.39.6"
|
||||||
tokio = { version = "1.52.3", features = ["fs"] }
|
tokio = { version = "1.52.3", features = ["fs", "rt-multi-thread"] }
|
||||||
uuid = { version = "1.23.1", features = ["v4"] }
|
uuid = { version = "1.23.1", features = ["v4"] }
|
||||||
@@ -7,8 +7,10 @@ use std::env;
|
|||||||
use std::fs;
|
use std::fs;
|
||||||
use std::fs::DirEntry;
|
use std::fs::DirEntry;
|
||||||
use std::io::{Error, ErrorKind};
|
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 lumiere_domain::video::model::video::VideoFormat::{AVI, MKV, MP4, WEBM};
|
||||||
use crate::video::model::LibraryItemEntity;
|
use crate::video::model::{LibraryItemEntity, TokioVideoFile};
|
||||||
use crate::video::repository::LibraryItemSqliteRepository;
|
use crate::video::repository::LibraryItemSqliteRepository;
|
||||||
use crate::video::video_format_detector::get_content_type;
|
use crate::video::video_format_detector::get_content_type;
|
||||||
|
|
||||||
@@ -46,7 +48,7 @@ impl LibraryItemAdapter {
|
|||||||
|
|
||||||
fn map_file_into_library_raw_item(file: DirEntry) -> Result<LibraryRawItem, VideoError> {
|
fn map_file_into_library_raw_item(file: DirEntry) -> Result<LibraryRawItem, VideoError> {
|
||||||
let item_type = if file.path().is_dir() {
|
let item_type = if file.path().is_dir() {
|
||||||
LibraryItemType::FOLDER
|
FOLDER
|
||||||
} else {
|
} else {
|
||||||
LibraryItemType::VIDEO
|
LibraryItemType::VIDEO
|
||||||
};
|
};
|
||||||
@@ -74,6 +76,12 @@ impl LibraryItemAdapter {
|
|||||||
database_items.iter()
|
database_items.iter()
|
||||||
.any(|database_item| database_item.name == file_system_item.name)
|
.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 {
|
impl VideoPort for LibraryItemAdapter {
|
||||||
@@ -102,7 +110,28 @@ impl VideoPort for LibraryItemAdapter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn get_video_stream(&self, video_id: Uuid) -> Result<Video, VideoError> {
|
fn get_video_stream(&self, video_id: Uuid) -> Result<Video, VideoError> {
|
||||||
todo!()
|
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> {
|
fn save(&self, library_item: &LibraryItem) -> Result<(), VideoError> {
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
|
use std::any::Any;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{Error, ErrorKind};
|
use std::io::{Error, ErrorKind};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
use lumiere_domain::video::model::library_item::{LibraryItem, LibraryItemType, LibraryRawItem};
|
use lumiere_domain::video::model::library_item::{LibraryItem, LibraryItemType, LibraryRawItem};
|
||||||
|
use lumiere_domain::video::model::video::VideoFile;
|
||||||
|
|
||||||
pub struct TokioVideoFile {
|
pub struct TokioVideoFile {
|
||||||
pub file: File
|
pub file: File
|
||||||
@@ -15,12 +17,18 @@ impl TokioVideoFile {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl VideoFile for TokioVideoFile {
|
||||||
|
fn as_any(&self) -> &dyn Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
pub struct LibraryItemEntity {
|
pub struct LibraryItemEntity {
|
||||||
pub id: Uuid,
|
pub id: Uuid,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub r#type: i8,
|
pub r#type: i8,
|
||||||
pub parent_folder_id: Option<Uuid>
|
pub parent_folder_id: Option<Box<LibraryItemEntity>>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LibraryItemEntity {
|
impl LibraryItemEntity {
|
||||||
@@ -48,7 +56,15 @@ impl LibraryItemEntity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn to_library_raw_item(self) -> Result<LibraryRawItem, Error> {
|
pub fn to_library_item(self) -> LibraryItem {
|
||||||
|
LibraryItem {
|
||||||
|
id: self.id.clone(),
|
||||||
|
name: self.name.clone(),
|
||||||
|
r#type: Self::map_type_from_i8(self.r#type).expect(""),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_library_raw_item(self) -> Result<LibraryRawItem, Error> {
|
||||||
let library_item = LibraryRawItem {
|
let library_item = LibraryRawItem {
|
||||||
id: Some(self.id.clone()),
|
id: Some(self.id.clone()),
|
||||||
name: self.name.clone(),
|
name: self.name.clone(),
|
||||||
|
|||||||
@@ -4,6 +4,13 @@ use rusqlite::fallible_iterator::FallibleIterator;
|
|||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
use crate::video::model::LibraryItemEntity;
|
use crate::video::model::LibraryItemEntity;
|
||||||
|
|
||||||
|
struct LibraryItemRawEntity {
|
||||||
|
pub id: Uuid,
|
||||||
|
pub name: String,
|
||||||
|
pub r#type: i8,
|
||||||
|
pub parent_item_id: Option<Uuid>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct LibraryItemSqliteRepository {
|
pub struct LibraryItemSqliteRepository {
|
||||||
pub database_connection: Arc<Mutex<Connection>>,
|
pub database_connection: Arc<Mutex<Connection>>,
|
||||||
@@ -46,10 +53,10 @@ impl LibraryItemSqliteRepository {
|
|||||||
WHERE parent_folder_id IS NULL",
|
WHERE parent_folder_id IS NULL",
|
||||||
)?;
|
)?;
|
||||||
let rows = prepared_statement.query([])?;
|
let rows = prepared_statement.query([])?;
|
||||||
rows.map(Self::map_to_library_item).collect::<Vec<LibraryItemEntity>>()
|
rows.map(Self::map_to_entity).collect::<Vec<LibraryItemEntity>>()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn map_to_library_item(row: &Row) -> Result<LibraryItemEntity, Error> {
|
fn map_to_entity(row: &Row) -> Result<LibraryItemEntity, Error> {
|
||||||
let id_text: String = row.get(0)?;
|
let id_text: String = row.get(0)?;
|
||||||
let type_as_number: i8 = row.get(2)?;
|
let type_as_number: i8 = row.get(2)?;
|
||||||
Ok(
|
Ok(
|
||||||
@@ -85,4 +92,131 @@ impl LibraryItemSqliteRepository {
|
|||||||
prepared_statement.execute(rusqlite::params_from_iter(params))?;
|
prepared_statement.execute(rusqlite::params_from_iter(params))?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_by_id_with_all_hierarchy(&self, library_item_id: &Uuid) -> Result<Option<LibraryItemEntity>, Error> {
|
||||||
|
let database_connection = self.get_connection();
|
||||||
|
|
||||||
|
/*
|
||||||
|
Given a LibraryItem item_1:
|
||||||
|
LibraryItemEntity {
|
||||||
|
id: <id1>,
|
||||||
|
name: "item_1",
|
||||||
|
type: 0,
|
||||||
|
parent_item_id: <id2>
|
||||||
|
}
|
||||||
|
|
||||||
|
Its parent item is item_2:
|
||||||
|
LibraryItemEntity {
|
||||||
|
id: <id2>,
|
||||||
|
name: "item_2",
|
||||||
|
type: 1,
|
||||||
|
parent_item_id: <id3>
|
||||||
|
}
|
||||||
|
|
||||||
|
item_2 has a parent which is item_3:
|
||||||
|
LibraryItemEntity {
|
||||||
|
id: <id3>,
|
||||||
|
name: "item_3",
|
||||||
|
type: 0,
|
||||||
|
parent_item_id: NULL
|
||||||
|
}
|
||||||
|
|
||||||
|
TODO: Write a SQL query to retrieve all LibraryItemEntity objects. It should filter on id with a parameter.
|
||||||
|
This query should return something like:
|
||||||
|
| id | name | type | parent_item_id |
|
||||||
|
| ----- | -------- | ---- | -------------- |
|
||||||
|
| <id1> | 'item_1' | 0 | <id2> |
|
||||||
|
| <id2> | 'item_2' | 1 | <id3> |
|
||||||
|
| <id3> | 'item_3' | 1 | NULL |
|
||||||
|
|
||||||
|
Take example on the following query and adapt it.
|
||||||
|
*/
|
||||||
|
let mut prepared_statement = database_connection.prepare(
|
||||||
|
"WITH RECURSIVE library_item_hierarchy AS (
|
||||||
|
-- Base case: start with the requested item
|
||||||
|
SELECT id, name, type, parent_folder_id
|
||||||
|
FROM library_item
|
||||||
|
WHERE id = ?1
|
||||||
|
|
||||||
|
UNION ALL
|
||||||
|
|
||||||
|
-- Recursive case: get all ancestors
|
||||||
|
SELECT li.id, li.name, li.type, li.parent_folder_id
|
||||||
|
FROM library_item li
|
||||||
|
INNER JOIN library_item_hierarchy lh ON li.id = lh.parent_folder_id
|
||||||
|
)
|
||||||
|
SELECT id, name, type, parent_folder_id
|
||||||
|
FROM library_item_hierarchy
|
||||||
|
ORDER BY id;"
|
||||||
|
)?;
|
||||||
|
let mut rows = prepared_statement.query([library_item_id.to_string()])?;
|
||||||
|
|
||||||
|
let raw_data = rows.map(Self::map_to_raw_entity).collect::<Vec<LibraryItemRawEntity>>()?;
|
||||||
|
|
||||||
|
// Build a map of id to LibraryItemEntity for quick lookup
|
||||||
|
let mut entity_map: std::collections::HashMap<Uuid, LibraryItemEntity> = std::collections::HashMap::new();
|
||||||
|
|
||||||
|
// First pass: create all entities without parent relationships
|
||||||
|
for raw_entity in &raw_data {
|
||||||
|
let entity = LibraryItemEntity {
|
||||||
|
id: raw_entity.id.clone(),
|
||||||
|
name: raw_entity.name.clone(),
|
||||||
|
r#type: raw_entity.r#type,
|
||||||
|
parent_folder_id: None, // Will be set in second pass
|
||||||
|
};
|
||||||
|
entity_map.insert(raw_entity.id.clone(), entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Second pass: set up parent-child relationships
|
||||||
|
for raw_entity in &raw_data {
|
||||||
|
if let Some(parent_id) = &raw_entity.parent_item_id {
|
||||||
|
if let Some(child_entity) = entity_map.get_mut(&raw_entity.id) {
|
||||||
|
if let Some(parent_entity) = entity_map.get(parent_id) {
|
||||||
|
child_entity.parent_folder_id = Some(Box::new(parent_entity.clone()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the requested item and return it with its full hierarchy
|
||||||
|
if let Some(entity) = entity_map.get(library_item_id) {
|
||||||
|
Ok(Some(entity.clone()))
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn find_by_id(&self, library_item_id: Uuid) -> Result<Option<LibraryItemEntity>, Error> {
|
||||||
|
let database_connection = self.get_connection();
|
||||||
|
|
||||||
|
let mut prepared_statement = database_connection.prepare(
|
||||||
|
"SELECT
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
type,
|
||||||
|
parent_folder_id
|
||||||
|
FROM library_item
|
||||||
|
WHERE id = ?1;"
|
||||||
|
)?;
|
||||||
|
let mut rows = prepared_statement.query([library_item_id.to_string()])?;
|
||||||
|
|
||||||
|
if let Some(row) = rows.next()? {
|
||||||
|
Ok(Some(Self::map_to_entity(&row)?))
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn map_to_raw_entity(row: &Row) -> Result<LibraryItemRawEntity, Error> {
|
||||||
|
let id_text: String = row.get(0)?;
|
||||||
|
let type_as_number: i8 = row.get(2)?;
|
||||||
|
let parent_item_id_text: Option<String> = row.get(3)?;
|
||||||
|
|
||||||
|
let raw_entity = LibraryItemRawEntity {
|
||||||
|
id: Uuid::parse_str(&id_text).unwrap(),
|
||||||
|
name: row.get(1)?,
|
||||||
|
r#type: type_as_number,
|
||||||
|
parent_item_id: parent_item_id_text.map(|id| Uuid::parse_str(id.as_str()).unwrap())
|
||||||
|
};
|
||||||
|
Ok(raw_entity)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Generated
+1
@@ -1883,6 +1883,7 @@ dependencies = [
|
|||||||
"jsonwebtoken",
|
"jsonwebtoken",
|
||||||
"lumiere-application",
|
"lumiere-application",
|
||||||
"lumiere-domain",
|
"lumiere-domain",
|
||||||
|
"lumiere-infrastructure",
|
||||||
"serde",
|
"serde",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-util",
|
"tokio-util",
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
# Editor configuration, see https://editorconfig.org
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
charset = utf-8
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
insert_final_newline = true
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
|
||||||
|
[*.ts]
|
||||||
|
quote_type = single
|
||||||
|
ij_typescript_use_double_quotes = false
|
||||||
|
|
||||||
|
[*.md]
|
||||||
|
max_line_length = off
|
||||||
|
trim_trailing_whitespace = false
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
# See https://docs.github.com/get-started/getting-started-with-git/ignoring-files for more about ignoring files.
|
||||||
|
|
||||||
|
# Compiled output
|
||||||
|
/dist
|
||||||
|
/tmp
|
||||||
|
/out-tsc
|
||||||
|
/bazel-out
|
||||||
|
|
||||||
|
# Node
|
||||||
|
/node_modules
|
||||||
|
npm-debug.log
|
||||||
|
yarn-error.log
|
||||||
|
|
||||||
|
# IDEs and editors
|
||||||
|
.idea/
|
||||||
|
.project
|
||||||
|
.classpath
|
||||||
|
.c9/
|
||||||
|
*.launch
|
||||||
|
.settings/
|
||||||
|
*.sublime-workspace
|
||||||
|
|
||||||
|
# Visual Studio Code
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/settings.json
|
||||||
|
!.vscode/tasks.json
|
||||||
|
!.vscode/launch.json
|
||||||
|
!.vscode/extensions.json
|
||||||
|
!.vscode/mcp.json
|
||||||
|
.history/*
|
||||||
|
|
||||||
|
# Miscellaneous
|
||||||
|
/.angular/cache
|
||||||
|
.sass-cache/
|
||||||
|
/connect.lock
|
||||||
|
/coverage
|
||||||
|
/libpeerconnection.log
|
||||||
|
testem.log
|
||||||
|
/typings
|
||||||
|
__screenshots__/
|
||||||
|
|
||||||
|
# System files
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"printWidth": 100,
|
||||||
|
"singleQuote": true,
|
||||||
|
"overrides": [
|
||||||
|
{
|
||||||
|
"files": "*.html",
|
||||||
|
"options": {
|
||||||
|
"parser": "angular"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
Vendored
+4
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=827846
|
||||||
|
"recommendations": ["angular.ng-template"]
|
||||||
|
}
|
||||||
Vendored
+20
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "ng serve",
|
||||||
|
"type": "chrome",
|
||||||
|
"request": "launch",
|
||||||
|
"preLaunchTask": "npm: start",
|
||||||
|
"url": "http://localhost:4200/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ng test",
|
||||||
|
"type": "chrome",
|
||||||
|
"request": "launch",
|
||||||
|
"preLaunchTask": "npm: test",
|
||||||
|
"url": "http://localhost:9876/debug.html"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
Vendored
+9
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
// For more information, visit: https://angular.dev/ai/mcp
|
||||||
|
"servers": {
|
||||||
|
"angular-cli": {
|
||||||
|
"command": "npx",
|
||||||
|
"args": ["-y", "@angular/cli", "mcp"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Vendored
+42
@@ -0,0 +1,42 @@
|
|||||||
|
{
|
||||||
|
// For more information, visit: https://go.microsoft.com/fwlink/?LinkId=733558
|
||||||
|
"version": "2.0.0",
|
||||||
|
"tasks": [
|
||||||
|
{
|
||||||
|
"type": "npm",
|
||||||
|
"script": "start",
|
||||||
|
"isBackground": true,
|
||||||
|
"problemMatcher": {
|
||||||
|
"owner": "typescript",
|
||||||
|
"pattern": "$tsc",
|
||||||
|
"background": {
|
||||||
|
"activeOnStart": true,
|
||||||
|
"beginsPattern": {
|
||||||
|
"regexp": "Changes detected"
|
||||||
|
},
|
||||||
|
"endsPattern": {
|
||||||
|
"regexp": "bundle generation (complete|failed)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "npm",
|
||||||
|
"script": "test",
|
||||||
|
"isBackground": true,
|
||||||
|
"problemMatcher": {
|
||||||
|
"owner": "typescript",
|
||||||
|
"pattern": "$tsc",
|
||||||
|
"background": {
|
||||||
|
"activeOnStart": true,
|
||||||
|
"beginsPattern": {
|
||||||
|
"regexp": "Changes detected"
|
||||||
|
},
|
||||||
|
"endsPattern": {
|
||||||
|
"regexp": "bundle generation (complete|failed)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
# LumiereFrontend
|
||||||
|
|
||||||
|
This project was generated using [Angular CLI](https://github.com/angular/angular-cli) version 21.2.19.
|
||||||
|
|
||||||
|
## Development server
|
||||||
|
|
||||||
|
To start a local development server, run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ng serve
|
||||||
|
```
|
||||||
|
|
||||||
|
Once the server is running, open your browser and navigate to `http://localhost:4200/`. The application will automatically reload whenever you modify any of the source files.
|
||||||
|
|
||||||
|
## Code scaffolding
|
||||||
|
|
||||||
|
Angular CLI includes powerful code scaffolding tools. To generate a new component, run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ng generate component component-name
|
||||||
|
```
|
||||||
|
|
||||||
|
For a complete list of available schematics (such as `components`, `directives`, or `pipes`), run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ng generate --help
|
||||||
|
```
|
||||||
|
|
||||||
|
## Building
|
||||||
|
|
||||||
|
To build the project run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ng build
|
||||||
|
```
|
||||||
|
|
||||||
|
This will compile your project and store the build artifacts in the `dist/` directory. By default, the production build optimizes your application for performance and speed.
|
||||||
|
|
||||||
|
## Running unit tests
|
||||||
|
|
||||||
|
To execute unit tests with the [Vitest](https://vitest.dev/) test runner, use the following command:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ng test
|
||||||
|
```
|
||||||
|
|
||||||
|
## Running end-to-end tests
|
||||||
|
|
||||||
|
For end-to-end (e2e) testing, run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ng e2e
|
||||||
|
```
|
||||||
|
|
||||||
|
Angular CLI does not come with an end-to-end testing framework by default. You can choose one that suits your needs.
|
||||||
|
|
||||||
|
## Additional Resources
|
||||||
|
|
||||||
|
For more information on using the Angular CLI, including detailed command references, visit the [Angular CLI Overview and Command Reference](https://angular.dev/tools/cli) page.
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
{
|
||||||
|
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
||||||
|
"version": 1,
|
||||||
|
"cli": {
|
||||||
|
"packageManager": "npm"
|
||||||
|
},
|
||||||
|
"newProjectRoot": "projects",
|
||||||
|
"projects": {
|
||||||
|
"lumiere-frontend": {
|
||||||
|
"projectType": "application",
|
||||||
|
"schematics": {},
|
||||||
|
"root": "",
|
||||||
|
"sourceRoot": "src",
|
||||||
|
"prefix": "app",
|
||||||
|
"architect": {
|
||||||
|
"build": {
|
||||||
|
"builder": "@angular/build:application",
|
||||||
|
"options": {
|
||||||
|
"browser": "src/main.ts",
|
||||||
|
"tsConfig": "tsconfig.app.json",
|
||||||
|
"assets": [
|
||||||
|
{
|
||||||
|
"glob": "**/*",
|
||||||
|
"input": "public"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"styles": [
|
||||||
|
"src/styles.css"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"configurations": {
|
||||||
|
"production": {
|
||||||
|
"budgets": [
|
||||||
|
{
|
||||||
|
"type": "initial",
|
||||||
|
"maximumWarning": "500kB",
|
||||||
|
"maximumError": "1MB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "anyComponentStyle",
|
||||||
|
"maximumWarning": "4kB",
|
||||||
|
"maximumError": "8kB"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outputHashing": "all"
|
||||||
|
},
|
||||||
|
"development": {
|
||||||
|
"optimization": false,
|
||||||
|
"extractLicenses": false,
|
||||||
|
"sourceMap": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"defaultConfiguration": "production"
|
||||||
|
},
|
||||||
|
"serve": {
|
||||||
|
"builder": "@angular/build:dev-server",
|
||||||
|
"configurations": {
|
||||||
|
"production": {
|
||||||
|
"buildTarget": "lumiere-frontend:build:production"
|
||||||
|
},
|
||||||
|
"development": {
|
||||||
|
"buildTarget": "lumiere-frontend:build:development"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"defaultConfiguration": "development"
|
||||||
|
},
|
||||||
|
"test": {
|
||||||
|
"builder": "@angular/build:unit-test"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Generated
+8825
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
"name": "lumiere-frontend",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"scripts": {
|
||||||
|
"ng": "ng",
|
||||||
|
"start": "ng serve --proxy-config proxy.conf.json",
|
||||||
|
"build": "ng build",
|
||||||
|
"watch": "ng build --watch --configuration development",
|
||||||
|
"test": "ng test"
|
||||||
|
},
|
||||||
|
"private": true,
|
||||||
|
"packageManager": "npm@10.9.7",
|
||||||
|
"dependencies": {
|
||||||
|
"@angular/common": "^21.2.0",
|
||||||
|
"@angular/compiler": "^21.2.0",
|
||||||
|
"@angular/core": "^21.2.0",
|
||||||
|
"@angular/forms": "^21.2.0",
|
||||||
|
"@angular/platform-browser": "^21.2.0",
|
||||||
|
"@angular/router": "^21.2.0",
|
||||||
|
"@types/video.js": "^7.3.58",
|
||||||
|
"rxjs": "~7.8.0",
|
||||||
|
"tslib": "^2.3.0",
|
||||||
|
"video.js": "^8.23.9"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@angular/build": "^21.2.19",
|
||||||
|
"@angular/cli": "^21.2.19",
|
||||||
|
"@angular/compiler-cli": "^21.2.0",
|
||||||
|
"jsdom": "^28.0.0",
|
||||||
|
"prettier": "^3.8.1",
|
||||||
|
"typescript": "~5.9.2",
|
||||||
|
"vitest": "^4.0.8"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"/api": {
|
||||||
|
"target": "http://localhost:19000",
|
||||||
|
"secure": false
|
||||||
|
}
|
||||||
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
@@ -0,0 +1,11 @@
|
|||||||
|
import { ApplicationConfig, provideBrowserGlobalErrorListeners } from '@angular/core';
|
||||||
|
import { provideRouter } from '@angular/router';
|
||||||
|
|
||||||
|
import { routes } from './app.routes';
|
||||||
|
|
||||||
|
export const appConfig: ApplicationConfig = {
|
||||||
|
providers: [
|
||||||
|
provideBrowserGlobalErrorListeners(),
|
||||||
|
provideRouter(routes)
|
||||||
|
]
|
||||||
|
};
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
:host {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.items-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: .7em;
|
||||||
|
padding: 0 .5em;
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
<h1>Lumière frontend</h1>
|
||||||
|
<div>
|
||||||
|
<button type="button" (click)="loadLibrary()">Reload library</button>
|
||||||
|
</div>
|
||||||
|
<div class="items-container">
|
||||||
|
@for (item of libraryItems.value(); track item.id) {
|
||||||
|
@if (item.type === 'VIDEO') {
|
||||||
|
<button type="button" (click)="activeVideo.set(item); initializeVideoPlayer(item.id)">
|
||||||
|
[{{item.type[0]}}] ({{item.id.substring(0, 4)}}…) {{item.name}}
|
||||||
|
</button>
|
||||||
|
} @else {
|
||||||
|
<div class="item">
|
||||||
|
[{{item.type[0]}}] ({{item.id.substring(0, 4)}}…) {{item.name}}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
@if (activeVideo(); as firstVideo) {
|
||||||
|
<video
|
||||||
|
id="video-player-{{firstVideo.id}}"
|
||||||
|
class="video-js vjs-default-skin"
|
||||||
|
controls
|
||||||
|
preload="auto"
|
||||||
|
width="1024"
|
||||||
|
height="640">
|
||||||
|
<p class="vjs-no-js">
|
||||||
|
To view this video please enable JavaScript, and consider upgrading to a
|
||||||
|
web browser that
|
||||||
|
<a href="https://videojs.com/html5-video-support/" target="_blank">supports HTML5 video</a>
|
||||||
|
</p>
|
||||||
|
</video>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
<router-outlet />
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
import { Routes } from '@angular/router';
|
||||||
|
|
||||||
|
export const routes: Routes = [];
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import { TestBed } from '@angular/core/testing';
|
||||||
|
import { App } from './app';
|
||||||
|
|
||||||
|
describe('App', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [App],
|
||||||
|
}).compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create the app', () => {
|
||||||
|
const fixture = TestBed.createComponent(App);
|
||||||
|
const app = fixture.componentInstance;
|
||||||
|
expect(app).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render title', async () => {
|
||||||
|
const fixture = TestBed.createComponent(App);
|
||||||
|
await fixture.whenStable();
|
||||||
|
const compiled = fixture.nativeElement as HTMLElement;
|
||||||
|
expect(compiled.querySelector('h1')?.textContent).toContain('Hello, lumiere-frontend');
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
import {Component, inject, OnInit, resource, signal} from '@angular/core';
|
||||||
|
import { RouterOutlet } from '@angular/router';
|
||||||
|
import {HttpClient} from '@angular/common/http';
|
||||||
|
import {firstValueFrom} from 'rxjs';
|
||||||
|
import videojs from 'video.js';
|
||||||
|
import type Player from 'video.js/dist/types/player';
|
||||||
|
import 'video.js/dist/video-js.css';
|
||||||
|
enum LibraryItemType {
|
||||||
|
VIDEO = 'VIDEO',
|
||||||
|
FOLDER = 'FOLDER'
|
||||||
|
}
|
||||||
|
|
||||||
|
interface LibraryItem {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
type: LibraryItemType
|
||||||
|
parentFolder: LibraryItem | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-root',
|
||||||
|
imports: [RouterOutlet],
|
||||||
|
templateUrl: './app.html',
|
||||||
|
styleUrl: './app.css'
|
||||||
|
})
|
||||||
|
export class App implements OnInit {
|
||||||
|
#httpClient = inject(HttpClient);
|
||||||
|
|
||||||
|
libraryItems = resource({
|
||||||
|
loader: ({}) => firstValueFrom(this.#httpClient.get<LibraryItem[]>('/api/library'))
|
||||||
|
});
|
||||||
|
|
||||||
|
activeVideo = signal<LibraryItem | null>(null);
|
||||||
|
|
||||||
|
// Store video.js player instances for cleanup
|
||||||
|
#videoPlayers = new Map<string, Player>();
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.loadLibrary();
|
||||||
|
}
|
||||||
|
|
||||||
|
loadLibrary(): void {
|
||||||
|
this.libraryItems.reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize or update the video player
|
||||||
|
initializeVideoPlayer(videoId: string): void {
|
||||||
|
// Wait for the next tick to ensure DOM is updated
|
||||||
|
setTimeout(() => {
|
||||||
|
const element = document.getElementById('video-player-' + videoId);
|
||||||
|
if (!element) {
|
||||||
|
console.error(`Video element with ID 'video-player-${videoId}' not found`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up previous player if it exists
|
||||||
|
const previousPlayer = this.#videoPlayers.get(videoId);
|
||||||
|
if (previousPlayer) {
|
||||||
|
previousPlayer.dispose();
|
||||||
|
this.#videoPlayers.delete(videoId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize new player
|
||||||
|
const player = videojs('video-player-' + videoId, {
|
||||||
|
controls: true,
|
||||||
|
autoplay: false,
|
||||||
|
fluid: true,
|
||||||
|
sources: [{
|
||||||
|
src: `/api/videos/${videoId}/stream`,
|
||||||
|
type: 'video/mp4' // Use MP4 as it's widely supported
|
||||||
|
}],
|
||||||
|
error: (error: Event) => {
|
||||||
|
console.error('Video.js error:', error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.#videoPlayers.set(videoId, player);
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up all video players on component destroy
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
for (const player of this.#videoPlayers.values()) {
|
||||||
|
player.dispose();
|
||||||
|
}
|
||||||
|
this.#videoPlayers.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>LumiereFrontend</title>
|
||||||
|
<base href="/">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<app-root></app-root>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
import { bootstrapApplication } from '@angular/platform-browser';
|
||||||
|
import { appConfig } from './app/app.config';
|
||||||
|
import { App } from './app/app';
|
||||||
|
|
||||||
|
bootstrapApplication(App, appConfig)
|
||||||
|
.catch((err) => console.error(err));
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
/* You can add global styles to this file, and also import other style files */
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */
|
||||||
|
/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */
|
||||||
|
{
|
||||||
|
"extends": "./tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "./out-tsc/app",
|
||||||
|
"types": []
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"src/**/*.ts"
|
||||||
|
],
|
||||||
|
"exclude": [
|
||||||
|
"src/**/*.spec.ts"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */
|
||||||
|
/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */
|
||||||
|
{
|
||||||
|
"compileOnSave": false,
|
||||||
|
"compilerOptions": {
|
||||||
|
"strict": true,
|
||||||
|
"noImplicitOverride": true,
|
||||||
|
"noPropertyAccessFromIndexSignature": true,
|
||||||
|
"noImplicitReturns": true,
|
||||||
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"importHelpers": true,
|
||||||
|
"target": "ES2022",
|
||||||
|
"module": "preserve"
|
||||||
|
},
|
||||||
|
"angularCompilerOptions": {
|
||||||
|
"enableI18nLegacyMessageIdFormat": false,
|
||||||
|
"strictInjectionParameters": true,
|
||||||
|
"strictInputAccessModifiers": true,
|
||||||
|
"strictTemplates": true
|
||||||
|
},
|
||||||
|
"files": [],
|
||||||
|
"references": [
|
||||||
|
{
|
||||||
|
"path": "./tsconfig.app.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "./tsconfig.spec.json"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */
|
||||||
|
/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */
|
||||||
|
{
|
||||||
|
"extends": "./tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "./out-tsc/spec",
|
||||||
|
"types": [
|
||||||
|
"vitest/globals"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"src/**/*.d.ts",
|
||||||
|
"src/**/*.spec.ts"
|
||||||
|
]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user