diff --git a/croppa/main.py b/croppa/main.py index f81e38e..38b101b 100644 --- a/croppa/main.py +++ b/croppa/main.py @@ -13,8 +13,6 @@ import json class VideoEditor: # Configuration constants BASE_FRAME_DELAY_MS = 16 # ~60 FPS - KEY_REPEAT_RATE_SEC = 0.3 - FAST_SEEK_ACTIVATION_TIME = 1.5 SPEED_INCREMENT = 0.2 MIN_PLAYBACK_SPEED = 0.1 MAX_PLAYBACK_SPEED = 10.0 @@ -502,18 +500,69 @@ class VideoEditor: self.current_frame = target_frame self.load_current_frame() + def seek_video_smart(self, frames_delta: int): + """Seek using keyframe jumping for large jumps""" + target_frame = max( + 0, min(self.current_frame + frames_delta, self.total_frames - 1) + ) + + # For small jumps, use regular seeking + if abs(frames_delta) <= 10: + self.current_frame = target_frame + self.load_current_frame() + return + + # For larger jumps, use keyframe seeking + # Jump to nearest keyframe first, then seek forward + keyframe_interval = self._detect_keyframe_interval() + nearest_keyframe = (target_frame // keyframe_interval) * keyframe_interval + + # Seek to keyframe first + self.cap.set(cv2.CAP_PROP_POS_FRAMES, nearest_keyframe) + self.current_frame = nearest_keyframe + + # Then seek forward to target frame + frames_to_seek = target_frame - nearest_keyframe + for _ in range(frames_to_seek): + ret, frame = self.cap.read() + if not ret: + break + self.current_frame += 1 + + self.current_display_frame = frame if 'frame' in locals() else None + + def _detect_keyframe_interval(self): + """Detect the keyframe interval for the current video""" + # Try to get keyframe interval from video properties + try: + # Some backends support keyframe interval detection + keyframe_interval = self.cap.get(cv2.CAP_PROP_FRAME_COUNT) / self.cap.get(cv2.CAP_PROP_FPS) + if keyframe_interval > 0: + return int(keyframe_interval) + except: + pass + + # Fallback: estimate based on video characteristics + if self.fps >= 60: + return 60 # High FPS videos often have keyframes every 60 frames + elif self.fps >= 30: + return 30 # Standard videos have keyframes every 30 frames + else: + return 15 # Lower FPS videos have keyframes every 15 frames + def seek_video_with_modifier( self, direction: int, shift_pressed: bool, ctrl_pressed: bool ): """Seek video with different frame counts based on modifiers""" if ctrl_pressed: frames = direction * 60 # Ctrl: 60 frames + self.seek_video_smart(frames) elif shift_pressed: frames = direction * 10 # Shift: 10 frames + self.seek_video_smart(frames) else: frames = direction * 1 # Default: 1 frame - - self.seek_video(frames) + self.seek_video(frames) def start_auto_repeat_seek(self, key: int, direction: int, shift_pressed: bool, ctrl_pressed: bool): """Start auto-repeat seeking for a held key""" @@ -544,12 +593,9 @@ class VideoEditor: # Do initial seek immediately self.seek_video_with_modifier(direction, shift_pressed, ctrl_pressed) - print(f"DEBUG: Started auto-repeat for key {key}") def stop_auto_repeat_seek(self): """Stop auto-repeat seeking""" - if self.auto_repeat_active: - print(f"DEBUG: Stopped auto-repeat") self.auto_repeat_active = False self.auto_repeat_key = None self.auto_repeat_direction = 0 @@ -1826,26 +1872,19 @@ class VideoEditor: delay = self.calculate_frame_delay() if self.is_playing else 1 # Very short delay for responsive key detection key = cv2.waitKey(delay) & 0xFF - # Debug key detection - if key != 255: - print(f"DEBUG: Key detected: {key} (auto_repeat_active: {self.auto_repeat_active}, auto_repeat_key: {self.auto_repeat_key})") # Handle auto-repeat - stop if a different key is pressed or no key for too long if key == 255 and self.auto_repeat_active: # 255 means no key pressed # Check if enough time has passed since last key activity (key was released) if time.time() - self.last_key_activity > 0.1: # 100ms timeout - print(f"DEBUG: Key released - stopping auto-repeat") self.stop_auto_repeat_seek() elif key != 255 and self.auto_repeat_active: # A key is pressed, update the last key activity time self.last_key_activity = time.time() # If it's a different key, stop auto-repeat if key != self.auto_repeat_key: - print(f"DEBUG: Different key pressed ({key} vs {self.auto_repeat_key}) - stopping auto-repeat") self.stop_auto_repeat_seek() # If it's the same key, just update the last activity time (don't restart auto-repeat) - else: - print(f"DEBUG: Same key pressed ({key}) - updating last activity time") # Get modifier key states window_title = "Image Editor" if self.is_image_mode else "Video Editor"