Make video loading dynamic.
This commit is contained in:
@@ -28,7 +28,6 @@ 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(tokio_video_file) = video.file.as_any().downcast_ref::<TokioVideoFile>() {
|
if let Some(tokio_video_file) = video.file.as_any().downcast_ref::<TokioVideoFile>() {
|
||||||
@@ -39,6 +38,7 @@ pub async fn get_video_stream(
|
|||||||
Ok(file) => HttpResponse::Ok()
|
Ok(file) => HttpResponse::Ok()
|
||||||
.content_type("application/octet-stream")
|
.content_type("application/octet-stream")
|
||||||
.streaming(async_stream::stream! {
|
.streaming(async_stream::stream! {
|
||||||
|
let file = tokio::fs::File::from_std(file);
|
||||||
let mut stream = FramedRead::new(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());
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
:host {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.items-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: .7em;
|
||||||
|
padding: 0 .5em;
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,15 +4,20 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="items-container">
|
<div class="items-container">
|
||||||
@for (item of libraryItems.value(); track item.id) {
|
@for (item of libraryItems.value(); track item.id) {
|
||||||
|
@if (item.type === 'VIDEO') {
|
||||||
|
<button type="button" (click)="activeVideo.set(item)">
|
||||||
|
[{{item.type[0]}}] ({{item.id.substring(0, 4)}}…) {{item.name}}
|
||||||
|
</button>
|
||||||
|
} @else {
|
||||||
<div class="item">
|
<div class="item">
|
||||||
[{{item.type[0]}}] ({{item.id.substring(0, 4)}}…) {{item.name}}
|
[{{item.type[0]}}] ({{item.id.substring(0, 4)}}…) {{item.name}}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
@if (firstVideo(); as firstVideo) {
|
@if (activeVideo(); as firstVideo) {
|
||||||
<video controls>
|
<video width="320" height="240" src="/api/videos/{{firstVideo.id}}/stream" controls>
|
||||||
<source src="/api/videos/{{firstVideo.id}}/stream" type="video/mp4">
|
|
||||||
Your browser does not support the html video player.
|
Your browser does not support the html video player.
|
||||||
</video>
|
</video>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,10 +28,7 @@ export class App implements OnInit {
|
|||||||
loader: ({}) => firstValueFrom(this.#httpClient.get<LibraryItem[]>('/api/library'))
|
loader: ({}) => firstValueFrom(this.#httpClient.get<LibraryItem[]>('/api/library'))
|
||||||
});
|
});
|
||||||
|
|
||||||
firstVideo = computed(() => {
|
activeVideo = signal<LibraryItem | null>(null);
|
||||||
const libraryItems = this.libraryItems.value() ?? [];
|
|
||||||
return libraryItems.length ? libraryItems[0] : null
|
|
||||||
});
|
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.loadLibrary();
|
this.loadLibrary();
|
||||||
|
|||||||
Reference in New Issue
Block a user