improving some performance
Some checks are pending
/ test (push) Waiting to run

This commit is contained in:
Chris Cochrun 2026-02-01 08:43:25 -06:00
parent 42800c66ca
commit 61bba082aa
3 changed files with 127 additions and 75 deletions

View file

@ -2,7 +2,7 @@ use std::{
borrow::Cow, collections::HashMap, option::Option, path::PathBuf,
};
use cosmic::iced::clipboard::mime::AsMimeTypes;
use cosmic::iced::{Color, clipboard::mime::AsMimeTypes};
use crisp::types::{Keyword, Symbol, Value};
use miette::{IntoDiagnostic, Result, miette};
use serde::{Deserialize, Serialize};
@ -38,6 +38,10 @@ pub struct Song {
pub font: Option<String>,
pub font_size: Option<i32>,
pub stroke_size: Option<i32>,
pub stroke_color: Option<String>,
pub shadow_size: Option<i32>,
pub shadow_offset: Option<(i32, i32)>,
pub shadow_color: Option<String>,
pub verses: Option<Vec<VerseName>>,
pub verse_map: Option<HashMap<VerseName, String>>,
}
@ -342,6 +346,7 @@ impl FromRow<'_, SqliteRow> for Song {
stroke_size: None,
verses,
verse_map,
..Default::default()
})
}
}
@ -1133,6 +1138,7 @@ You saved my soul"
stroke_size: None,
verses: Some(vec![]),
verse_map: None,
..Default::default()
}
}

View file

@ -205,7 +205,7 @@ impl Presenter {
gst::init().into_diagnostic()?;
let pipeline = format!(
r#"playbin uri="{}" video-sink="videoscale ! videoconvert ! appsink name=lumina_video drop=true caps=video/x-raw,format=NV12,pixel-aspect-ratio=1/1""#,
r#"playbin uri="{}" video-sink="videoscale ! videoconvert ! videoflip method=automatic ! appsink name=lumina_video drop=true caps=video/x-raw,format=NV12,pixel-aspect-ratio=1/1""#,
url.as_str()
);
@ -1050,100 +1050,84 @@ pub(crate) fn slide_view<'a>(
delegate: bool,
hide_mouse: bool,
) -> Element<'a, Message> {
let res = responsive(move |size| {
responsive(move |size| {
let width = size.height * 16.0 / 9.0;
let text: Element<Message> =
if let Some(text) = &slide.text_svg {
if let Some(handle) = &text.handle {
image(handle)
.content_fit(ContentFit::ScaleDown)
.width(Length::Shrink)
.height(Length::Shrink)
.into()
} else {
Space::with_width(0).into()
}
} else {
Space::with_width(0).into()
};
let black = Container::new(Space::new(0, 0))
.style(|_| {
container::background(Background::Color(Color::BLACK))
})
.clip(true)
.width(width)
.height(size.height);
let background = match slide.background().kind {
.height(Length::Fill);
let mut stack = stack(vec![black.into()]);
match slide.background().kind {
BackgroundKind::Image => {
let path = slide.background().path.clone();
Container::new(
image(path)
.content_fit(ContentFit::Cover)
.width(width)
.height(size.height),
)
.center(Length::Shrink)
.clip(true)
}
BackgroundKind::Video => {
if delegate {
Container::new(Space::new(0, 0))
.style(|_| {
container::background(Background::Color(
Color::BLACK,
))
})
.center(Length::Fill)
.clip(true)
.width(width)
.height(size.height)
} else if let Some(video) = &video {
stack = stack.push(
Container::new(
VideoPlayer::new(video)
.mouse_hidden(hide_mouse)
image(path)
.content_fit(ContentFit::Contain)
.width(width)
.height(size.height)
.on_end_of_stream(Message::EndVideo)
.on_new_frame(Message::VideoFrame)
.on_missing_plugin(Message::MissingPlugin)
.on_warning(|w| {
Message::Error(w.to_string())
})
.on_error(|e| {
Message::Error(e.to_string())
})
.content_fit(ContentFit::Cover),
.height(Length::Fill),
)
.center(Length::Fill)
.clip(true)
// Container::new(Space::new(0, 0))
} else {
Container::new(Space::new(0, 0))
.clip(true),
);
}
BackgroundKind::Video => {
if let Some(video) = &video
&& !delegate
{
stack = stack.push(
Container::new(
VideoPlayer::new(video)
.mouse_hidden(hide_mouse)
.width(width)
.height(Length::Fill)
.on_end_of_stream(Message::EndVideo)
.on_new_frame(Message::VideoFrame)
.on_missing_plugin(
Message::MissingPlugin,
)
.on_warning(|w| {
Message::Error(w.to_string())
})
.on_error(|e| {
Message::Error(e.to_string())
})
.content_fit(ContentFit::Contain),
)
.center(Length::Fill)
.clip(true),
);
}
}
BackgroundKind::Pdf => {
if let Some(pdf) = slide.pdf_page() {
Container::new(
image(pdf).content_fit(ContentFit::Contain),
)
.center(Length::Fill)
.clip(true)
} else {
Container::new(Space::new(0.0, 0.0))
stack = stack.push(
Container::new(
image(pdf)
.content_fit(ContentFit::Contain),
)
.center(Length::Fill)
.clip(true)
.clip(true),
);
}
}
BackgroundKind::Html => todo!(),
};
let stack = stack!(
black,
background.center_x(Length::Fill),
container(text).center(Length::Fill)
);
if let Some(text) = &slide.text_svg {
if let Some(handle) = &text.handle {
stack = stack.push(
image(handle)
.content_fit(ContentFit::ScaleDown)
.width(Length::Shrink)
.height(Length::Shrink),
);
}
};
Container::new(stack).center(Length::Fill).into()
});
res.into()
})
.into()
}

View file

@ -102,6 +102,7 @@ pub enum Message {
ChangeLyrics(text_editor::Action),
ChangeBackground(Result<PathBuf, SongError>),
UpdateSlides(Vec<Slide>),
UpdateSlide((usize, Slide)),
PickBackground,
Edit(bool),
None,
@ -240,7 +241,28 @@ impl SongEditor {
}
self.background_video(&song.background);
self.background = song.background.clone();
self.song_slides = None;
let font_db = Arc::clone(&self.font_db);
// let slides = song_slides.ok();
// let mut task = Task::none();
// if let Some(slides) = slides {
// for (index, mut slide) in
// slides.into_iter().enumerate()
// {
// let font_db = Arc::clone(&font_db);
// task = task.chain(Task::perform(
// async move {
// text_svg::text_svg_generator(
// &mut slide, font_db,
// );
// (index, slide)
// },
// Message::UpdateSlide,
// ));
// }
// }
let task = Task::perform(
async move {
song_slides
@ -260,6 +282,7 @@ impl SongEditor {
},
Message::UpdateSlides,
);
self.verses = song.verse_map.map(|map| {
map.into_iter()
.sorted()
@ -368,6 +391,18 @@ impl SongEditor {
Message::UpdateSlides(slides) => {
self.song_slides = Some(slides);
}
Message::UpdateSlide((index, slide)) => {
if let Some(slides) = self.song_slides.as_mut() {
if let Some(_old) = slides.get(index) {
let _ = slides.remove(index);
slides.insert(index, slide);
} else {
slides.push(slide);
}
} else {
self.song_slides = Some(vec![slide]);
}
}
Message::UpdateSong(song) => {
self.song = Some(song.clone());
return Action::UpdateSong(song);
@ -1009,6 +1044,32 @@ impl SongEditor {
let font_db = Arc::clone(&self.font_db);
let update_task =
Task::done(Message::UpdateSong(song.clone()));
// need to test to see which of these methods yields faster
// text_svg slide creation. There is a small thought in me that
// believes it's better for the user to see the slides being added
// one by one, rather than all at once, but that isn't how
// the task appears to happen.
// let slides = song.to_slides().ok();
// let mut task = vec![];
// if let Some(slides) = slides {
// for (index, mut slide) in slides.into_iter().enumerate() {
// let font_db = Arc::clone(&font_db);
// task.push(Task::perform(
// async move {
// text_svg::text_svg_generator(
// &mut slide, font_db,
// );
// (index, slide)
// },
// Message::UpdateSlide,
// ));
// }
// }
// I think this implementation is faster
let task = Task::perform(
async move {
song.to_slides()
@ -1028,6 +1089,7 @@ impl SongEditor {
},
Message::UpdateSlides,
);
Action::Task(task.chain(update_task))
}