diff --git a/croppa/main.py b/croppa/main.py index a7a5de2..d6cd3fa 100644 --- a/croppa/main.py +++ b/croppa/main.py @@ -141,11 +141,16 @@ class VideoEditor: return self._is_video_file(file_path) or self._is_image_file(file_path) def _get_next_edited_filename(self, video_path: Path) -> str: - """Generate the next available _edited_%03d filename""" + """Generate the next available _edited_%03d filename, or overwrite if already edited""" directory = video_path.parent base_name = video_path.stem extension = video_path.suffix + # Check if the current video already contains "_edited_" in its name + if "_edited_" in base_name: + # If it's already an edited video, overwrite it instead of creating _edited_edited + return video_path.name + # Pattern to match existing edited files: basename_edited_001.ext, basename_edited_002.ext, etc. pattern = re.compile(rf"^{re.escape(base_name)}_edited_(\d{{3}}){re.escape(extension)}$") @@ -1321,6 +1326,10 @@ class VideoEditor: last_display_update = current_time out.release() + + # Ensure the video writer is completely closed and file handles are freed + del out + time.sleep(0.1) # Small delay to ensure file is unlocked total_time = time.time() - start_time total_frames_written = end_frame - start_frame + 1 @@ -1560,7 +1569,45 @@ class VideoEditor: self.next_video() elif key == 13: # Enter output_name = self._get_next_edited_filename(self.video_path) - self.render_video(str(self.video_path.parent / output_name)) + output_path = str(self.video_path.parent / output_name) + + # If we're overwriting the same file, use a temporary file first + if output_name == self.video_path.name: + import tempfile + temp_dir = self.video_path.parent + temp_fd, temp_path = tempfile.mkstemp(suffix=self.video_path.suffix, dir=temp_dir) + os.close(temp_fd) # Close the file descriptor, we just need the path + + print(f"Rendering to temporary file first...") + success = self.render_video(temp_path) + + if success: + print("Replacing original file...") + # Release current video capture before replacing the file + if hasattr(self, 'cap') and self.cap: + self.cap.release() + + # Replace the original file with the temporary file + import shutil + shutil.move(temp_path, str(self.video_path)) + + # Small delay to ensure file system operations are complete + time.sleep(0.1) + + try: + self._load_video(self.video_path) + self.load_current_frame() + print("File reloaded successfully") + except Exception as e: + print(f"Warning: Could not reload file after overwrite: {e}") + print("The file was saved successfully, but you may need to restart the editor to continue editing it.") + else: + # Clean up temp file if rendering failed + if os.path.exists(temp_path): + os.remove(temp_path) + else: + # Normal case - render to new file + success = self.render_video(output_path) elif key == ord("t"): # Marker looping only for videos if not self.is_image_mode: