Playlist player is now initialized empty and extended afterwards.

This commit is contained in:
Filipe Rodrigues 2025-09-08 03:52:00 +01:00
parent d36a87cf63
commit c034cd6b1d
Signed by: zenithsiz
SSH Key Fingerprint: SHA256:Mb5ppb3Sh7IarBO/sBTXLHbYEOz37hJAlslLQPPAPaU
5 changed files with 55 additions and 67 deletions

View File

@ -426,7 +426,7 @@ async fn paint_egui(
}) {
match &mut panel.state {
panel::PanelState::None(_) => (),
panel::PanelState::Fade(state) => state.skip(&shared.wgpu),
panel::PanelState::Fade(state) => state.skip(&shared.wgpu).await,
}
break;
}
@ -448,7 +448,7 @@ async fn paint_egui(
false => time_delta_abs,
};
state.step(&shared.wgpu, time_delta);
state.step(&shared.wgpu, time_delta).await;
break;
},
}

View File

@ -168,7 +168,7 @@ impl PanelsRenderer {
// Update the panel before drawing it
match &mut panel.state {
PanelState::None(_) => (),
PanelState::Fade(state) => state.update(wgpu),
PanelState::Fade(state) => state.update(wgpu).await,
}
// If the panel images are empty, there's no sense in rendering it either

View File

@ -6,9 +6,9 @@ use {
crate::playlist::{PlaylistName, PlaylistPlayer, Playlists},
app_error::Context,
chrono::TimeDelta,
core::time::Duration,
core::{ops::Deref, time::Duration},
futures::lock::Mutex,
std::{sync::Arc, time::Instant},
zsw_util::{AppError, Loadable, loadable::Loader},
zsw_wgpu::Wgpu,
zutil_cloned::cloned,
};
@ -78,13 +78,10 @@ pub struct PanelFadeState {
playlist: PlaylistName,
/// Playlist player
playlist_player: Loadable<Result<PlaylistPlayer, AppError>, PlaylistPlayerLoader>,
playlist_player: Arc<Mutex<PlaylistPlayer>>,
}
type PlaylistPlayerLoader = impl Loader<(), Result<PlaylistPlayer, AppError>>;
impl PanelFadeState {
#[define_opaque(PlaylistPlayerLoader)]
pub fn new(
duration: Duration,
fade_duration: Duration,
@ -92,23 +89,28 @@ impl PanelFadeState {
playlist: PlaylistName,
playlists: Arc<Playlists>,
) -> Self {
#[cloned(playlist)]
let playlist_player = Loadable::new(move || {
let playlist = playlist.clone();
let playlists = Arc::clone(&playlists);
async move {
let playlist = playlists
.load(playlist.clone())
.await
.context("Unable to load playlist")?;
tracing::debug!("Loaded default playlist {playlist:?}");
let playlist_player = Arc::new(Mutex::new(PlaylistPlayer::new()));
let playlist_player = PlaylistPlayer::new(&playlist).await;
#[cloned(playlist, playlist_player)]
let task_res = crate::spawn_task(format!("Populate panel {playlist:?} playlist"), async move {
let playlist = playlists
.load(playlist.clone())
.await
.context("Unable to load playlist")?;
tracing::debug!("Loaded default playlist {playlist:?}");
Ok(playlist_player)
}
playlist_player.lock().await.extend(&playlist).await;
Ok(())
});
if let Err(err) = task_res {
tracing::warn!(
"Unable to create task to populate panel {playlist:?} playlist: {}",
err.pretty()
);
}
Self {
paused: false,
shader,
@ -209,8 +211,8 @@ impl PanelFadeState {
}
/// Returns the panel playlist player
pub fn playlist_player(&self) -> Option<&PlaylistPlayer> {
self.playlist_player.get().and_then(|res| res.as_ref().ok())
pub async fn playlist_player(&self) -> impl Deref<Target = PlaylistPlayer> {
self.playlist_player.lock().await
}
/// Returns if paused
@ -248,26 +250,16 @@ impl PanelFadeState {
}
/// Skips to the next image.
///
/// If the playlist player isn't loaded, does nothing
pub fn skip(&mut self, wgpu: &Wgpu) {
let Some(Ok(playlist_player)) = self.playlist_player.try_load(()) else {
return;
};
self.progress = match self.images.step_next(playlist_player, wgpu) {
pub async fn skip(&mut self, wgpu: &Wgpu) {
self.progress = match self.images.step_next(&mut *self.playlist_player.lock().await, wgpu) {
Ok(()) => self.fade_duration,
Err(()) => self.max_progress(),
}
}
/// Steps this panel's state by a certain number of frames (potentially negative).
///
/// If the playlist player isn't loaded, does nothing
pub fn step(&mut self, wgpu: &Wgpu, delta: TimeDelta) {
let Some(Ok(playlist_player)) = self.playlist_player.try_load(()) else {
return;
};
pub async fn step(&mut self, wgpu: &Wgpu, delta: TimeDelta) {
let mut playlist_player = self.playlist_player.lock().await;
let (delta_abs, delta_is_positive) = self::time_delta_to_duration(delta);
let next_progress = match delta_is_positive {
@ -281,7 +273,7 @@ impl PanelFadeState {
Some(next_progress) => match next_progress.checked_sub(self.duration) {
// If we did, `next_progress` is our progress at the next image, so try
// to step to it.
Some(next_progress) => match self.images.step_next(playlist_player, wgpu) {
Some(next_progress) => match self.images.step_next(&mut playlist_player, wgpu) {
// If we successfully stepped to the next image, start at the next progress
// Note: If delta was big enough to overflow 2 durations, then cap it at the
// max duration of the next image.
@ -297,7 +289,7 @@ impl PanelFadeState {
},
// Otherwise, we underflowed, so try to step back
None => match self.images.step_prev(playlist_player, wgpu) {
None => match self.images.step_prev(&mut playlist_player, wgpu) {
// If we successfully stepped backwards, start at where we're supposed to:
Ok(()) => {
// Note: This branch is only taken when `delta` is negative, so we can always
@ -319,16 +311,10 @@ impl PanelFadeState {
}
/// Updates this panel's state using the current time as a delta
///
/// If the playlist player isn't loaded, does nothing
pub fn update(&mut self, wgpu: &Wgpu) {
let Some(Ok(playlist_player)) = self.playlist_player.try_load(()) else {
return;
};
pub async fn update(&mut self, wgpu: &Wgpu) {
// Note: We always load images, even if we're paused, since the user might be
// moving around manually.
self.images.load_missing(playlist_player, wgpu);
self.images.load_missing(&mut *self.playlist_player.lock().await, wgpu);
// If we're paused, don't update anything
if self.paused {
@ -342,7 +328,7 @@ impl PanelFadeState {
// the other has updated.
let delta = self.update_delta();
let delta = TimeDelta::from_std(delta).expect("Last update duration didn't fit into a delta");
self.step(wgpu, delta);
self.step(wgpu, delta).await;
}
}

View File

@ -34,10 +34,21 @@ pub struct PlaylistPlayer {
}
impl PlaylistPlayer {
/// Creates a new player from a playlist
pub async fn new(playlist: &Playlist) -> Self {
let all_items = Mutex::new(HashSet::new());
/// Creates a new, empty, player
pub fn new() -> Self {
Self {
all_items: HashSet::new(),
cur_items: VecDeque::new(),
max_old_items: 100,
cur_pos: 0,
rng: StdRng::from_os_rng(),
}
}
/// Extends this player with a playlist
// TODO: Move this elsewhere so we can lock only when inserting
pub async fn extend(&mut self, playlist: &Playlist) {
let items = Mutex::new(vec![]);
playlist
.items
.iter()
@ -88,7 +99,7 @@ impl PlaylistPlayer {
}
match tokio::fs::canonicalize(&path).await {
Ok(entry) => _ = all_items.lock().await.insert(entry.into()),
Ok(entry) => items.lock().await.push(entry.into()),
Err(err) => {
let err = AppError::new(&err);
tracing::warn!("Unable to read playlist entry {path:?}: {}", err.pretty());
@ -101,7 +112,7 @@ impl PlaylistPlayer {
.await,
PlaylistItemKind::File { ref path } => match tokio::fs::canonicalize(path).await {
Ok(path) => _ = all_items.lock().await.insert(path.into()),
Ok(path) => items.lock().await.push(path.into()),
Err(err) => {
let err = AppError::new(&err);
tracing::warn!("Unable to canonicalize playlist entry {path:?}: {}", err.pretty());
@ -113,13 +124,7 @@ impl PlaylistPlayer {
.collect::<()>()
.await;
Self {
all_items: all_items.into_inner(),
cur_items: VecDeque::new(),
max_old_items: 100,
cur_pos: 0,
rng: StdRng::from_os_rng(),
}
self.all_items.extend(items.into_inner());
}
/// Returns the previous position in the playlist

View File

@ -190,7 +190,7 @@ fn draw_fade_panel_editor(
ui.horizontal(|ui| {
ui.label("Skip");
if ui.button("🔄").clicked() {
panel_state.skip(wgpu);
panel_state.skip(wgpu).block_on();
}
});
@ -207,12 +207,9 @@ fn draw_fade_panel_editor(
});
ui.collapsing("Playlist", |ui| {
ui.label(format!("Playlist: {:?}", panel_state.playlist()));
let playlist_player = panel_state.playlist_player().block_on();
let Some(playlist_player) = panel_state.playlist_player() else {
ui.weak("Not loaded");
return;
};
ui.label(format!("Playlist: {:?}", panel_state.playlist()));
let row_height = ui.text_style_height(&egui::TextStyle::Body);