scanner improvements + playback ux

This commit is contained in:
2026-02-02 12:19:03 -05:00
parent 575c3c5675
commit 20874ecff7
4 changed files with 23 additions and 8 deletions

View File

@@ -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;
} }
} }

View File

@@ -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)

View File

@@ -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 }
} }
} }
} }

View File

@@ -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",