Try to stream the file in handler.
This commit is contained in:
@@ -1,3 +1,4 @@
|
|||||||
|
#[derive(Debug)]
|
||||||
pub enum VideoError {
|
pub enum VideoError {
|
||||||
VideoDoesNotExists,
|
VideoDoesNotExists,
|
||||||
FolderDoesNotExists,
|
FolderDoesNotExists,
|
||||||
|
|||||||
@@ -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));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,22 +28,30 @@ pub async fn get_video_stream(
|
|||||||
) -> impl Responder {
|
) -> impl Responder {
|
||||||
let video_result = video_service.get_video_stream(video_id.into_inner());
|
let video_result = video_service.get_video_stream(video_id.into_inner());
|
||||||
|
|
||||||
|
|
||||||
match video_result {
|
match video_result {
|
||||||
Ok(video) => {
|
Ok(video) => {
|
||||||
if let Some(tokioVideoFile) = video.file.as_any().downcast_ref::<TokioVideoFile>() {
|
if let Some(tokio_video_file) = video.file.as_any().downcast_ref::<TokioVideoFile>() {
|
||||||
HttpResponse::Ok()
|
// 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")
|
.content_type("application/octet-stream")
|
||||||
.streaming(async_stream::stream! {
|
.streaming(async_stream::stream! {
|
||||||
let mut stream = FramedRead::new(tokioVideoFile.file, BytesCodec::new());
|
let mut stream = FramedRead::new(file, BytesCodec::new());
|
||||||
while let Some(chunk) = stream.next().await {
|
while let Some(chunk) = stream.next().await {
|
||||||
yield chunk.map(|bytes| bytes.freeze());
|
yield chunk.map(|bytes| bytes.freeze());
|
||||||
}
|
}
|
||||||
})
|
}),
|
||||||
|
Err(_) => HttpResponse::InternalServerError().body("Failed to clone video file")
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
HttpResponse::InternalServerError().body("Video file format is not handled.")
|
HttpResponse::InternalServerError().body("Video file format is not handled.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(error) => HttpResponse::InternalServerError().body("C P T")
|
Err(error) => HttpResponse::InternalServerError().body(format!("{:?}", error))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -123,7 +123,7 @@ impl VideoPort for LibraryItemAdapter {
|
|||||||
id: video.id,
|
id: video.id,
|
||||||
name: video.name,
|
name: video.name,
|
||||||
format: AVI,
|
format: AVI,
|
||||||
file: Box::new(TokioVideoFile::new(&video_file_path).map_err(|_| VideoError::FileReadingTechnicalIssue)?)
|
file: Box::new(TokioVideoFile::new(&video_file_path))
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(result)
|
Ok(result)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
|
use std::fs::File;
|
||||||
use std::io::{Error, ErrorKind};
|
use std::io::{Error, ErrorKind};
|
||||||
use tokio::fs::File;
|
|
||||||
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;
|
use lumiere_domain::video::model::video::VideoFile;
|
||||||
@@ -10,10 +10,10 @@ pub struct TokioVideoFile {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl TokioVideoFile {
|
impl TokioVideoFile {
|
||||||
pub fn new(file_path: &String) -> Result<Self, Error> {
|
pub fn new(file_path: &String) -> Self {
|
||||||
let rt = tokio::runtime::Runtime::new()?;
|
Self {
|
||||||
let file = rt.block_on(async { File::open(file_path).await })?;
|
file: File::open(file_path).expect(format!("Failed to open file {file_path}").as_str())
|
||||||
Ok(Self { file })
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,4 +9,12 @@
|
|||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
@if (firstVideo(); as firstVideo) {
|
||||||
|
<video controls>
|
||||||
|
<source src="/api/videos/{{firstVideo.id}}/stream" type="video/mp4">
|
||||||
|
Your browser does not support the html video player.
|
||||||
|
</video>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
<router-outlet />
|
<router-outlet />
|
||||||
|
|||||||
Reference in New Issue
Block a user