fixes to time conversion and play head ux + improvements to file scanner

This commit is contained in:
2026-03-10 22:07:04 -04:00
parent 951e285f81
commit b7c00323e8
7 changed files with 141 additions and 45 deletions

View File

@@ -18,12 +18,21 @@
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: repeat(3, 1fr);
aspect-ratio: 4/3;
width: auto;
height: 100%;
overflow: hidden;
video {
width: 100%;
height: auto;
image-rendering: smooth;
aspect-ratio: 4/3;
}
.emptyVideo {
width: 100%;
aspect-ratio: 16/9;
}
}
@@ -40,23 +49,73 @@
gap: 1px;
}
#playhead {
#playControls {
display: grid;
width: 100%;
position: relative;
grid-template-columns: $labelWidth 1fr;
grid-template-areas: "Label Input";
input {
background-color: $featureBackroundDark;
border-bottom: 1px solid $mainAccent;
.inputs {
grid-area: Input;
width: 100%;
position: relative;
input {
width: 100%;
height: 100%;
padding: 0;
margin: 0;
border: 0;
appearance: none;
background: transparent;
outline: none;
/* opacity: 0; */
&::-webkit-slider-thumb {
background-color: transparent;
appearance: none;
display: block;
width: 1px;
height: 10px;
}
&::-webkit-slider-runnable-track {
border: 0;
padding: 0;
margin: 0;
}
}
}
.indicator {
.playHead {
width: 1px;
background-color: $secondaryAccentLight;
height: 100%;
top: 0;
height: 300px;
position: absolute;
pointer-events: none;
z-index: 2;
&::before {
display: block;
content: "";
border: 11px solid transparent;
border-top: 20px solid $secondaryAccentLight;
transform: translateX(-11px);
}
}
.timelineMarkers {
left: 0;
top: 0;
height: 100%;
width: 100%;
position: absolute;
pointer-events: none;
.marker {
position: absolute;
border-left: 1px dashed #fff;
height: 100%;
}
}
}

View File

@@ -1,4 +1,4 @@
use chrono::DateTime;
use chrono::{DateTime, Local, TimeZone};
use dioxus::prelude::*;
use crate::rpc::azki::{MediaChannel, MediaEntry, PlaybackInfo};
@@ -24,32 +24,39 @@ pub fn Timeline(playback_info: Option<PlaybackInfo>, zoom: Signal<f32>, time: Si
}
#[component]
fn PlayHead(start: i64, time: Signal<i64>, zoom: Signal<f32>) -> Element
fn PlayControls(start: i64, time: Signal<i64>, zoom: Signal<f32>) -> Element
{
let time_string = get_time_string(start, time());
let p = (time() as f32 / SEC_PER_DAY) * 100.0;
rsx! {
div {
id: "playhead",
input {
style: "width: calc({zoom() * 100.0}%);",
type: "range",
value: "{time()}",
max: "{SEC_PER_DAY}",
oninput: move |e|{
if let Ok(t) = e.value().parse() {
time.set(t);
}
}
},
id: "playControls",
style: "width: calc({zoom() * 100.0}%);",
div{
class: "time",
"{time_string}"
}
div{
class: "indicator",
style: "left: {p}%"
class: "inputs",
input {
type: "range",
value: "{time()}",
max: "{SEC_PER_DAY}",
oninput: move |e|{
if let Ok(t) = e.value().parse() {
time.set(t);
}
}
},
div{
class: "timelineMarkers",
{(0..23).map(|i| rsx!{ div{ class: "marker", style: "left: {((i + 1) as f32/24.0) * 100.0}%;" } })}
}
div{
class: "playHead",
style: "left: {p}%"
}
}
}
}
@@ -61,7 +68,7 @@ fn TrackList(channels: Vec<MediaChannel>, start: i64, zoom: Signal<f32>, time: S
rsx! {
div {
id: "tracklist",
PlayHead { start, time, zoom }
PlayControls { start, time, zoom }
{channels.iter().map(|c|rsx!{
TimelineTrack { channel: c.clone(), start, zoom }
})}
@@ -102,7 +109,10 @@ fn Clip(media: MediaEntry, start: i64) -> Element
{
let meta = media.metadata.unwrap();
let date = media.date.unwrap();
let timestamp = date.seconds - start;
// let timestamp = date.seconds - start;
let utc_date = DateTime::from_timestamp_secs(date.seconds).expect("Failed to convert clip date to utc");
let timestamp = utc_date.timestamp() - start;
let duration = (meta.duration as f32 / SEC_PER_DAY) * 100.0;
let offset = (timestamp as f32 / SEC_PER_DAY) * 100.0;
rsx! {
@@ -116,6 +126,6 @@ fn Clip(media: MediaEntry, start: i64) -> Element
fn get_time_string(start: i64, time: i64) -> String
{
let date_time = DateTime::from_timestamp_secs(start + time).expect("Failed to convert time");
let local = date_time.naive_local();
return local.to_string();
let local: DateTime<Local> = DateTime::from(date_time);
return format!("{}", local.format("%Y-%m-%d %H:%M"));
}

View File

@@ -1,3 +1,4 @@
use chrono::DateTime;
use dioxus::prelude::*;
use crate::{
@@ -25,6 +26,7 @@ fn ViewportFull(playback_info: PlaybackInfo, time: Signal<i64>) -> Element
id: "viewport",
{playback_info.channels.iter().map(|c| {
let entry = get_entry(c, start, cur_time);
rsx!{
Video{ entry }
}
@@ -52,6 +54,7 @@ fn get_entry(channel: &MediaChannel, start: i64, time: i64) -> Option<MediaEntry
.find(|e| {
let meta = e.metadata.unwrap();
let date = e.date.unwrap();
let timestamp = date.seconds - start;
return timestamp <= time && timestamp + meta.duration as i64 >= time;
@@ -64,13 +67,23 @@ fn Video(entry: Option<MediaEntry>) -> Element
{
match entry
{
Some(entry) => rsx! {
video{
autoplay: "true",
muted: "true",
src: "{MEDIA_HOST}/m/v/{entry.id}"
Some(entry) =>
{
info!("date: {}, name: {}", entry.date.unwrap().to_string(), entry.file_path);
rsx! {
video{
autoplay: "true",
muted: "true",
controls: true,
src: "{MEDIA_HOST}/m/v/{entry.id}"
}
}
}
None => rsx! {
div {
class: "emptyVideo",
"No Video"
}
},
None => rsx! { div{ "No Video" }},
}
}