mirror of
https://github.com/storytold/deckcraft.git
synced 2026-10-27 12:07:02 -06:00
Media: the playing clip decides whether it carries over (#994)
The carry-over check looked the clip up again on the previous slide. If that slide held the same media under the same id without Play Across Slides, the voice stopped one slide later. A slide without the id fell back to the first match in the deck. Use the clip the voice was started with; the next slide is only checked for another clip under that id. Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
+19
-16
@@ -176,6 +176,11 @@ impl MediaHost {
|
|||||||
self.owners.get(&id).map(|o| o.owner)
|
self.owners.get(&id).map(|o| o.owner)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The clip voice `id` was started with.
|
||||||
|
pub fn clip(&self, id: ShapeId) -> Option<&MediaClip> {
|
||||||
|
self.owners.get(&id).map(|o| &o.clip)
|
||||||
|
}
|
||||||
|
|
||||||
/// Shapes with a voice (playing, paused or ended).
|
/// Shapes with a voice (playing, paused or ended).
|
||||||
pub fn ids(&self) -> Vec<ShapeId> {
|
pub fn ids(&self) -> Vec<ShapeId> {
|
||||||
self.owners.keys().copied().collect()
|
self.owners.keys().copied().collect()
|
||||||
@@ -353,11 +358,10 @@ fn walk_clip(shapes: &[deckcraft_model::Shape], id: ShapeId) -> Option<MediaClip
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether the voice `id`, playing on slide `from`, keeps playing on slide `to`: its clip plays
|
/// Whether the voice `id`, playing `clip` (the clip it was started with), keeps playing on slide
|
||||||
/// across slides and `to` doesn't hold another clip under the same id.
|
/// `to`: the clip plays across slides and `to` doesn't hold another clip under the same id.
|
||||||
pub fn carries_over(doc: &Presentation, id: ShapeId, from: Option<usize>, to: usize) -> bool {
|
pub fn carries_over(clip: &MediaClip, doc: &Presentation, id: ShapeId, to: usize) -> bool {
|
||||||
let Some(clip) = find_clip(doc, id, from).filter(|c| c.play_across_slides) else { return false };
|
clip.play_across_slides && clip_on(doc, to, id).is_none_or(|c| c.media == clip.media)
|
||||||
clip_on(doc, to, id).is_none_or(|c| c.media == clip.media)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Media shapes directly on slide `index`: (id, box in slide points, clip).
|
/// Media shapes directly on slide `index`: (id, box in slide points, clip).
|
||||||
@@ -650,23 +654,22 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn a_clip_across_slides_stops_where_the_next_slide_reuses_its_id() {
|
fn a_clip_across_slides_stops_where_the_next_slide_reuses_its_id() {
|
||||||
let (mut doc, id, first) = deck(4000, wav_level(0.05, 0.5));
|
let (mut doc, id, first) = deck(4000, wav_level(0.05, 0.5));
|
||||||
if let ShapeKind::Media(m) = &mut Arc::make_mut(&mut doc.slides[0]).shapes.last_mut().expect("shape").kind {
|
let playing = MediaClip { play_across_slides: true, ..first.clone() };
|
||||||
m.play_across_slides = true;
|
|
||||||
}
|
|
||||||
let mut plain = (*doc.slides[0]).clone();
|
let mut plain = (*doc.slides[0]).clone();
|
||||||
plain.shapes.clear();
|
plain.shapes.clear();
|
||||||
doc.slides.push(Arc::new(plain));
|
doc.slides.push(Arc::new(plain));
|
||||||
assert!(carries_over(&doc, id, Some(0), 1), "a slide without that id");
|
assert!(carries_over(&playing, &doc, id, 1), "a slide without that id");
|
||||||
let mut same = (*doc.slides[0]).clone();
|
// Slide 0 holds the same media under that id, without Play Across Slides: the voice that
|
||||||
if let ShapeKind::Media(m) = &mut same.shapes.last_mut().expect("shape").kind {
|
// came from elsewhere goes on (its own clip decides), here and on the next slide.
|
||||||
m.play_across_slides = false;
|
assert!(carries_over(&playing, &doc, id, 0), "the same media under that id");
|
||||||
}
|
assert!(!carries_over(&first, &doc, id, 1), "a clip that doesn't play across slides");
|
||||||
doc.slides.push(Arc::new(same));
|
|
||||||
assert!(carries_over(&doc, id, Some(0), 2), "the same media under that id");
|
|
||||||
let other = doc.add_media("two.wav", "audio/wav", wav_level(0.05, 0.25)).expect("media");
|
let other = doc.add_media("two.wav", "audio/wav", wav_level(0.05, 0.25)).expect("media");
|
||||||
let mut reused = (*doc.slides[0]).clone();
|
let mut reused = (*doc.slides[0]).clone();
|
||||||
reused.shapes.last_mut().expect("shape").kind = ShapeKind::Media(MediaClip { media: other, volume: 1.0, autoplay: true, ..first.clone() });
|
reused.shapes.last_mut().expect("shape").kind = ShapeKind::Media(MediaClip { media: other, volume: 1.0, autoplay: true, ..first.clone() });
|
||||||
doc.slides.push(Arc::new(reused));
|
doc.slides.push(Arc::new(reused));
|
||||||
assert!(!carries_over(&doc, id, Some(0), 3), "another clip under that id");
|
assert!(!carries_over(&playing, &doc, id, 2), "another clip under that id");
|
||||||
|
let mut host = MediaHost::new(None);
|
||||||
|
host.play(1, &doc, id, &playing, None, Owner::Show);
|
||||||
|
assert_eq!(host.clip(id), Some(&playing), "the voice keeps the clip it was started with");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1070,7 +1070,7 @@ fn show_media(app: &mut SlideApp, show: &mut Show, doc_uid: u64, doc: &Presentat
|
|||||||
if show.media_slide == cur {
|
if show.media_slide == cur {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let previous = std::mem::replace(&mut show.media_slide, cur);
|
show.media_slide = cur;
|
||||||
let trans = cur.and_then(|i| doc.slides.get(i)).and_then(|s| s.transition.as_ref());
|
let trans = cur.and_then(|i| doc.slides.get(i)).and_then(|s| s.transition.as_ref());
|
||||||
let sound = trans.and_then(|t| t.sound);
|
let sound = trans.and_then(|t| t.sound);
|
||||||
let sound_loop = trans.is_some_and(|t| t.sound_loop);
|
let sound_loop = trans.is_some_and(|t| t.sound_loop);
|
||||||
@@ -1081,7 +1081,7 @@ fn show_media(app: &mut SlideApp, show: &mut Show, doc_uid: u64, doc: &Presentat
|
|||||||
.media
|
.media
|
||||||
.ids()
|
.ids()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|id| crate::media::carries_over(doc, *id, previous, i))
|
.filter(|id| app.media.clip(*id).is_some_and(|c| crate::media::carries_over(c, doc, *id, i)))
|
||||||
// A transition sound plays to its end, as in PowerPoint, unless the new slide has its own.
|
// A transition sound plays to its end, as in PowerPoint, unless the new slide has its own.
|
||||||
.chain((sound.is_none() && !stop_previous).then_some(TRANSITION_SOUND).filter(|id| app.media.is_playing(*id)))
|
.chain((sound.is_none() && !stop_previous).then_some(TRANSITION_SOUND).filter(|id| app.media.is_playing(*id)))
|
||||||
.collect(),
|
.collect(),
|
||||||
|
|||||||
Reference in New Issue
Block a user