diff --git a/croppa/main.py b/croppa/main.py index 010b798..858e332 100644 --- a/croppa/main.py +++ b/croppa/main.py @@ -16,6 +16,11 @@ class VideoEditor: MIN_PLAYBACK_SPEED = 0.1 MAX_PLAYBACK_SPEED = 10.0 + # Seek multiplier configuration + SEEK_MULTIPLIER_INCREMENT = 1.0 + MIN_SEEK_MULTIPLIER = 1.0 + MAX_SEEK_MULTIPLIER = 100.0 + # Auto-repeat seeking configuration AUTO_REPEAT_DISPLAY_RATE = 1.0 @@ -163,6 +168,7 @@ class VideoEditor: 'looping_between_markers': self.looping_between_markers, 'display_offset': self.display_offset, 'playback_speed': getattr(self, 'playback_speed', 1.0), + 'seek_multiplier': getattr(self, 'seek_multiplier', 1.0), 'is_playing': getattr(self, 'is_playing', False) } @@ -209,6 +215,8 @@ class VideoEditor: self.display_offset = state['display_offset'] if 'playback_speed' in state: self.playback_speed = state['playback_speed'] + if 'seek_multiplier' in state: + self.seek_multiplier = state['seek_multiplier'] if 'is_playing' in state: self.is_playing = state['is_playing'] @@ -218,6 +226,7 @@ class VideoEditor: self.brightness = max(-100, min(100, self.brightness)) self.contrast = max(0.1, min(3.0, self.contrast)) self.playback_speed = max(self.MIN_PLAYBACK_SPEED, min(self.MAX_PLAYBACK_SPEED, self.playback_speed)) + self.seek_multiplier = max(self.MIN_SEEK_MULTIPLIER, min(self.MAX_SEEK_MULTIPLIER, self.seek_multiplier)) return True except Exception as e: @@ -426,6 +435,7 @@ class VideoEditor: self.current_frame = 0 self.is_playing = False if self.is_image_mode else False # Images start paused self.playback_speed = 1.0 + self.seek_multiplier = 1.0 self.current_display_frame = None # Reset crop, zoom, rotation, brightness/contrast, and cut settings for new media @@ -493,14 +503,16 @@ class VideoEditor: def seek_video_with_modifier( self, direction: int, shift_pressed: bool, ctrl_pressed: bool ): - """Seek video with different frame counts based on modifiers""" + """Seek video with different frame counts based on modifiers and seek multiplier""" if ctrl_pressed: - frames = direction * 60 # Ctrl: 60 frames + base_frames = 60 # Ctrl: 60 frames elif shift_pressed: - frames = direction * 10 # Shift: 10 frames + base_frames = 10 # Shift: 10 frames else: - frames = direction * 1 # Default: 1 frame + base_frames = 1 # Default: 1 frame + # Apply seek multiplier to the base frame count + frames = direction * int(base_frames * self.seek_multiplier) self.seek_video(frames) def start_auto_repeat_seek(self, direction: int, shift_pressed: bool, ctrl_pressed: bool): @@ -1078,10 +1090,13 @@ class VideoEditor: contrast_text = ( f" | Contrast: {self.contrast:.1f}" if self.contrast != 1.0 else "" ) + seek_multiplier_text = ( + f" | Seek: {self.seek_multiplier:.1f}x" if self.seek_multiplier != 1.0 else "" + ) if self.is_image_mode: info_text = f"Image | Zoom: {self.zoom_factor:.1f}x{rotation_text}{brightness_text}{contrast_text}" else: - info_text = f"Frame: {self.current_frame}/{self.total_frames} | Speed: {self.playback_speed:.1f}x | Zoom: {self.zoom_factor:.1f}x{rotation_text}{brightness_text}{contrast_text} | {'Playing' if self.is_playing else 'Paused'}" + info_text = f"Frame: {self.current_frame}/{self.total_frames} | Speed: {self.playback_speed:.1f}x | Zoom: {self.zoom_factor:.1f}x{seek_multiplier_text}{rotation_text}{brightness_text}{contrast_text} | {'Playing' if self.is_playing else 'Paused'}" cv2.putText( canvas, info_text, @@ -1721,6 +1736,7 @@ class VideoEditor: print(" Shift+A/D: Seek backward/forward (10 frames)") print(" Ctrl+A/D: Seek backward/forward (60 frames)") print(" W/S: Increase/Decrease speed") + print(" Q/Y: Increase/Decrease seek multiplier") print(" E/Shift+E: Increase/Decrease brightness") print(" R/Shift+R: Increase/Decrease contrast") print(" -: Rotate clockwise 90°") @@ -1829,6 +1845,20 @@ class VideoEditor: self.playback_speed = max( self.MIN_PLAYBACK_SPEED, self.playback_speed - self.SPEED_INCREMENT ) + elif key == ord("Q"): + # Seek multiplier control only for videos + if not self.is_image_mode: + self.seek_multiplier = min( + self.MAX_SEEK_MULTIPLIER, self.seek_multiplier + self.SEEK_MULTIPLIER_INCREMENT + ) + print(f"Seek multiplier: {self.seek_multiplier:.1f}x") + elif key == ord("Y"): + # Seek multiplier control only for videos + if not self.is_image_mode: + self.seek_multiplier = max( + self.MIN_SEEK_MULTIPLIER, self.seek_multiplier - self.SEEK_MULTIPLIER_INCREMENT + ) + print(f"Seek multiplier: {self.seek_multiplier:.1f}x") elif key == ord("e") or key == ord("E"): # Brightness adjustment: E (increase), Shift+E (decrease) if key == ord("E"):