scanner improvements + playback ux
This commit is contained in:
@@ -32,15 +32,15 @@ public class AZKiRpcService(MediaService mediaService) : RPC.AZKi.AZKiBase
|
|||||||
{
|
{
|
||||||
CameraId = c.Key,
|
CameraId = c.Key,
|
||||||
};
|
};
|
||||||
result.Images.AddRange(images);
|
result.Images.AddRange(images.OrderBy(i => i.Date));
|
||||||
result.Videos.AddRange(videos);
|
result.Videos.AddRange(videos.OrderBy(i => i.Date));
|
||||||
return result;
|
return result;
|
||||||
});
|
});
|
||||||
var playback = new PlaybackInfo
|
var playback = new PlaybackInfo
|
||||||
{
|
{
|
||||||
Date = Google.Protobuf.WellKnownTypes.Timestamp.FromDateTime(from),
|
Date = Google.Protobuf.WellKnownTypes.Timestamp.FromDateTime(from),
|
||||||
};
|
};
|
||||||
playback.Channels.AddRange(channels);
|
playback.Channels.AddRange(channels.OrderBy(c => c.CameraId));
|
||||||
return playback;
|
return playback;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ using FFMpegCore;
|
|||||||
using MaybeError;
|
using MaybeError;
|
||||||
using MaybeError.Errors;
|
using MaybeError.Errors;
|
||||||
|
|
||||||
|
using SharpCompress.Common;
|
||||||
|
|
||||||
using SixLabors.ImageSharp;
|
using SixLabors.ImageSharp;
|
||||||
|
|
||||||
using System.Collections.Frozen;
|
using System.Collections.Frozen;
|
||||||
@@ -55,11 +57,17 @@ public class FileScannerService(MediaService mediaService, IConfiguration config
|
|||||||
{
|
{
|
||||||
var files = Directory.GetFiles(path, "*", SearchOption.AllDirectories);
|
var files = Directory.GetFiles(path, "*", SearchOption.AllDirectories);
|
||||||
var existingFiles = await mediaService.GetExistingFilePathsAsync(cancellationToken);
|
var existingFiles = await mediaService.GetExistingFilePathsAsync(cancellationToken);
|
||||||
|
var upToDateFiles = existingFiles.Where(e => e.Value == MediaEntry.CUR_VERSION)
|
||||||
|
.Select(e => e.Key)
|
||||||
|
.ToFrozenSet();
|
||||||
|
var filesToProcess = files.Where(f => !upToDateFiles.Contains(Path.GetRelativePath(path, f))).ToArray();
|
||||||
var total = 0;
|
var total = 0;
|
||||||
foreach (var chunk in files.Chunk(50))
|
var prog = 0;
|
||||||
|
foreach (var chunk in filesToProcess.Chunk(50))
|
||||||
{
|
{
|
||||||
|
prog += chunk.Length;
|
||||||
total += await ScanFileChunkAsync(path, chunk, existingFiles, cancellationToken);
|
total += await ScanFileChunkAsync(path, chunk, existingFiles, cancellationToken);
|
||||||
logger.LogInformation("Added {updated} of {count} [{percentage}%]", total, files.Length, Math.Round(((float)total/ files.Length) * 100));
|
logger.LogInformation("Added {updated} of {count} [{percentage}%]", total, filesToProcess.Length, Math.Round(((float)prog/ filesToProcess.Length) * 100));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ pub fn Player() -> Element {
|
|||||||
Some(Date::from_ordinal_date(now.year(), now.ordinal() as u16).unwrap())
|
Some(Date::from_ordinal_date(now.year(), now.ordinal() as u16).unwrap())
|
||||||
});
|
});
|
||||||
let mut zoom = use_signal(|| 1.0 as f32);
|
let mut zoom = use_signal(|| 1.0 as f32);
|
||||||
|
let mut time = use_signal(|| 0 as i64);
|
||||||
let playbackResult = use_resource(use_reactive!(|(selected_date)| async move {
|
let playbackResult = use_resource(use_reactive!(|(selected_date)| async move {
|
||||||
let mut client = get_rpc_client();
|
let mut client = get_rpc_client();
|
||||||
info!("Load data");
|
info!("Load data");
|
||||||
@@ -75,7 +76,7 @@ pub fn Player() -> Element {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Viewport { }
|
Viewport { }
|
||||||
Timeline { playbackInfo: info, zoom }
|
Timeline { playbackInfo: info, zoom, time }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,23 +3,30 @@ use dioxus::prelude::*;
|
|||||||
use crate::rpc::azki::{MediaChannel, MediaEntry, PlaybackInfo};
|
use crate::rpc::azki::{MediaChannel, MediaEntry, PlaybackInfo};
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
pub fn Timeline(playbackInfo: Option<PlaybackInfo>, zoom: Signal<f32>) -> Element {
|
pub fn Timeline(playbackInfo: Option<PlaybackInfo>, zoom: Signal<f32>, time: Signal<i64>) -> Element {
|
||||||
return match playbackInfo {
|
return match playbackInfo {
|
||||||
Some(info) => rsx! {
|
Some(info) => rsx! {
|
||||||
div{
|
div{
|
||||||
id: "timeline",
|
id: "timeline",
|
||||||
|
PlayHead { time, zoom }
|
||||||
TrackList { channels: info.channels, start: info.date.unwrap().seconds, zoom }
|
TrackList { channels: info.channels, start: info.date.unwrap().seconds, zoom }
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
None => rsx! {
|
None => rsx! {
|
||||||
div{
|
div{
|
||||||
id: "timeline",
|
id: "timeline",
|
||||||
|
PlayHead { time, zoom }
|
||||||
TrackList { channels: Vec::new(), start: 0, zoom }
|
TrackList { channels: Vec::new(), start: 0, zoom }
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
fn PlayHead(time: Signal<i64>, zoom: Signal<f32>) -> Element {
|
||||||
|
rsx! {}
|
||||||
|
}
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
fn TrackList(channels: Vec<MediaChannel>, start: i64, zoom: Signal<f32>) -> Element {
|
fn TrackList(channels: Vec<MediaChannel>, start: i64, zoom: Signal<f32>) -> Element {
|
||||||
rsx! {
|
rsx! {
|
||||||
@@ -62,7 +69,6 @@ fn Clip(media: MediaEntry, start: i64) -> Element {
|
|||||||
let timestamp = date.seconds - start;
|
let timestamp = date.seconds - start;
|
||||||
let duration = (meta.duration as f32 / SEC_PER_DAY) * 100.0;
|
let duration = (meta.duration as f32 / SEC_PER_DAY) * 100.0;
|
||||||
let offset = (timestamp as f32 / SEC_PER_DAY) * 100.0;
|
let offset = (timestamp as f32 / SEC_PER_DAY) * 100.0;
|
||||||
let time = date.to_string();
|
|
||||||
rsx! {
|
rsx! {
|
||||||
div{
|
div{
|
||||||
class: "clip",
|
class: "clip",
|
||||||
|
|||||||
Reference in New Issue
Block a user