summary refs log tree commit diff
path: root/src/app.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/app.rs')
-rw-r--r--src/app.rs157
1 files changed, 91 insertions, 66 deletions
diff --git a/src/app.rs b/src/app.rs
index 7aa5abd..066980c 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -12,7 +12,7 @@ use crate::{
     subtitle_view::{SubtitleView, SubtitleViewMsg, SubtitleViewOutput},
     tracks::{SUBTITLE_TRACKS, StreamIndex, SubtitleCue},
     transcript::{Transcript, TranscriptMsg, TranscriptOutput},
-    util::OptionTracker,
+    util::{OptionTracker, Tracker},
 };
 
 pub struct App {
@@ -26,13 +26,14 @@ pub struct App {
     subtitle_selection_dialog: Controller<SubtitleSelectionDialog>,
 
     primary_stream_ix: Option<StreamIndex>,
-    primary_last_cue_ix: OptionTracker<usize>,
+    primary_cue: Tracker<Option<String>>,
+    primary_last_cue_ix: Tracker<Option<usize>>,
+    secondary_cue: Tracker<Option<String>>,
     secondary_stream_ix: Option<StreamIndex>,
-    secondary_last_cue_ix: OptionTracker<usize>,
+    secondary_last_cue_ix: Tracker<Option<usize>>,
 
     // for auto-pausing
     autopaused: bool,
-    primary_cue_active: bool,
     hovering_primary_cue: bool,
 }
 
@@ -162,12 +163,13 @@ impl SimpleComponent for App {
             subtitle_selection_dialog,
 
             primary_stream_ix: None,
-            primary_last_cue_ix: OptionTracker::new(None),
+            primary_cue: Tracker::new(None),
+            primary_last_cue_ix: Tracker::new(None),
             secondary_stream_ix: None,
-            secondary_last_cue_ix: OptionTracker::new(None),
+            secondary_cue: Tracker::new(None),
+            secondary_last_cue_ix: Tracker::new(None),
 
             autopaused: false,
-            primary_cue_active: false,
             hovering_primary_cue: false,
         };
 
@@ -177,9 +179,6 @@ impl SimpleComponent for App {
     }
 
     fn update(&mut self, message: Self::Input, _sender: ComponentSender<Self>) {
-        self.primary_last_cue_ix.reset();
-        self.secondary_last_cue_ix.reset();
-
         match message {
             AppMsg::NewCue(stream_index, cue) => {
                 self.transcript
@@ -203,20 +202,41 @@ impl SimpleComponent for App {
             }
             AppMsg::PositionUpdate(pos) => {
                 if let Some(stream_ix) = self.primary_stream_ix {
-                    let cue =
-                        Self::get_cue_and_update_ix(stream_ix, pos, &mut self.primary_last_cue_ix);
-                    let cue_is_some = cue.is_some();
-
-                    // beginning of new subtitle
-                    if self.primary_last_cue_ix.is_dirty()
-                        || (!self.primary_cue_active && cue_is_some)
-                    {
+                    // sometimes we get a few position update messages after
+                    // auto-pausing; this prevents us from immediately un-autopausing
+                    // again
+                    if self.autopaused {
+                        return;
+                    }
+
+                    let cue_was_some = self.primary_cue.get().is_some();
+
+                    Self::update_cue(
+                        stream_ix,
+                        pos,
+                        &mut self.primary_cue,
+                        &mut self.primary_last_cue_ix,
+                    );
+
+                    if self.primary_cue.is_dirty() {
+                        // last cue just ended -> auto-pause
+                        if cue_was_some && self.hovering_primary_cue {
+                            self.player.sender().send(PlayerMsg::Pause).unwrap();
+                            self.autopaused = true;
+                            return;
+                        }
+
                         self.subtitle_view
                             .sender()
-                            .send(SubtitleViewMsg::SetPrimaryCue(cue))
+                            .send(SubtitleViewMsg::SetPrimaryCue(
+                                self.primary_cue.get().clone(),
+                            ))
                             .unwrap();
-                        self.primary_cue_active = cue_is_some;
 
+                        self.primary_cue.reset();
+                    }
+
+                    if self.primary_last_cue_ix.is_dirty() {
                         if let Some(ix) = self.primary_last_cue_ix.get() {
                             self.transcript
                                 .sender()
@@ -226,33 +246,24 @@ impl SimpleComponent for App {
 
                         self.primary_last_cue_ix.reset();
                     }
-
-                    // end of current subtitle
-                    if self.primary_cue_active && !cue_is_some && !self.autopaused {
-                        if self.hovering_primary_cue {
-                            self.player.sender().send(PlayerMsg::Pause).unwrap();
-                            self.autopaused = true;
-                        } else {
-                            self.subtitle_view
-                                .sender()
-                                .send(SubtitleViewMsg::SetPrimaryCue(None))
-                                .unwrap();
-                            self.primary_cue_active = false;
-                        }
-                    }
                 }
                 if let Some(stream_ix) = self.secondary_stream_ix {
-                    if !self.autopaused {
+                    Self::update_cue(
+                        stream_ix,
+                        pos,
+                        &mut self.secondary_cue,
+                        &mut self.secondary_last_cue_ix,
+                    );
+
+                    if !self.autopaused && self.secondary_cue.is_dirty() {
                         self.subtitle_view
                             .sender()
                             .send(SubtitleViewMsg::SetSecondaryCue(
-                                Self::get_cue_and_update_ix(
-                                    stream_ix,
-                                    pos,
-                                    &mut self.secondary_last_cue_ix,
-                                ),
+                                self.secondary_cue.get().clone(),
                             ))
                             .unwrap();
+
+                        self.secondary_cue.reset();
                     }
                 }
             }
@@ -302,50 +313,64 @@ impl SimpleComponent for App {
 }
 
 impl App {
-    fn get_cue_and_update_ix(
+    fn update_cue(
         stream_ix: StreamIndex,
         position: gst::ClockTime,
-        last_cue_ix: &mut OptionTracker<usize>,
-    ) -> Option<String> {
+        cue: &mut Tracker<Option<String>>,
+        last_cue_ix: &mut Tracker<Option<usize>>,
+    ) {
         let lock = SUBTITLE_TRACKS.read();
-        let track = lock.get(&stream_ix)?;
+        let track = lock.get(&stream_ix).unwrap();
 
         // try to find current cue quickly (should usually succeed during playback)
         if let Some(ix) = last_cue_ix.get() {
-            let last_cue = track.cues.get(*ix)?;
+            let last_cue = track.cues.get(*ix).unwrap();
             if last_cue.start <= position && position <= last_cue.end {
-                return Some(last_cue.text.clone());
-            }
-            let next_cue = track.cues.get(ix + 1)?;
-            if last_cue.end < position && position < next_cue.start {
-                return None;
-            }
-            if next_cue.start <= position && position <= next_cue.end {
-                last_cue_ix.set(Some(ix + 1));
-                return Some(next_cue.text.clone());
+                // still at current cue
+                return;
+            } else if let Some(next_cue) = track.cues.get(ix + 1) {
+                if last_cue.end < position && position < next_cue.start {
+                    // strictly between cues
+                    cue.set(None);
+                    return;
+                }
+                if next_cue.start <= position && position <= next_cue.end {
+                    // already in next cue (this happens when one cue immediately
+                    // follows the previous one)
+                    cue.set(Some(next_cue.text.clone()));
+                    last_cue_ix.set(Some(ix + 1));
+                    return;
+                }
             }
         }
 
         // if we are before the first subtitle, no need to look further
-        if position < track.cues.first()?.start {
+        if track.cues.is_empty() || position < track.cues.first().unwrap().start {
+            cue.set(None);
             last_cue_ix.set(None);
-            return None;
+            return;
         }
 
         // otherwise, search the whole track (e.g. after seeking)
-        let (ix, cue) = track
+        match track
             .cues
             .iter()
             .enumerate()
             .rev()
-            .find(|(_ix, cue)| cue.start <= position)?;
-
-        last_cue_ix.set(Some(ix));
-
-        if position <= cue.end {
-            Some(cue.text.clone())
-        } else {
-            None
-        }
+            .find(|(_ix, cue)| cue.start <= position)
+        {
+            Some((ix, new_cue)) => {
+                last_cue_ix.set(Some(ix));
+                if position <= new_cue.end {
+                    cue.set(Some(new_cue.text.clone()));
+                } else {
+                    cue.set(None);
+                }
+            }
+            None => {
+                cue.set(None);
+                last_cue_ix.set(None);
+            }
+        };
     }
 }