Compare commits

...

128 Commits

Author SHA1 Message Date
bd1824a7ca Remove spammy prints 2025-09-26 19:44:17 +02:00
4806c95095 Shift 1-2 to go to markers 2025-09-26 19:41:07 +02:00
16c841d14d Remove some retard 2025-09-26 19:39:59 +02:00
bfb9ed54d9 Bigger template aoe 2025-09-26 19:39:53 +02:00
3ac725c2aa Enhance template selection logic in VideoEditor to handle current frame matches
This commit updates the template selection logic in the VideoEditor to correctly identify and utilize templates that start at the current frame. The changes include additional checks to remove or use templates based on their start frames, improving the efficiency and accuracy of template management during video editing sessions.
2025-09-26 19:38:45 +02:00
b5a0811cbd Enhance template management in VideoEditor by including template images
This commit updates the template management system in the VideoEditor to store template images alongside their start frames and regions. The changes include modifications to the loading and saving processes, ensuring that template images are recreated when needed. Additionally, the logic for adding and retrieving templates has been refined to accommodate the new structure, improving the overall efficiency and clarity of template handling during video editing sessions.
2025-09-26 19:35:13 +02:00
1ac8cd04b3 Refactor template management in VideoEditor to simplify template structure
This commit updates the template management system in the VideoEditor by transitioning from a dictionary-based structure to a list of tuples. The new structure simplifies the handling of templates, focusing on start frames and regions without the need for template IDs or counters. Additionally, the loading and saving of templates have been streamlined, enhancing clarity and efficiency in template operations during video editing sessions.
2025-09-26 19:33:52 +02:00
203d036a92 Remove all templates on C 2025-09-26 19:14:00 +02:00
fa2ac22f9f Refactor video scaling logic in VideoEditor to improve display handling
This commit updates the video scaling logic to ensure that videos are scaled down to fit the screen bounds instead of resizing the window. The previous logic for window resizing has been removed, streamlining the display process and enhancing the user experience during video editing sessions. The changes also include adjustments to maintain the aspect ratio of the video while scaling.
2025-09-26 19:04:54 +02:00
2013ccf627 Remove logic for future template management in VideoEditor
This commit eliminates the code that removes templates starting after the current frame, streamlining the template handling process. The changes enhance clarity and efficiency in template management during video editing sessions. Debug messages related to template removal have also been removed to reflect this update.
2025-09-26 18:53:10 +02:00
e1d94f2b24 Refactor template management in VideoEditor to streamline template handling
This commit removes unused attributes and logic related to template matching, enhancing the clarity and efficiency of the template management system. The changes include the removal of the template matching enabled flag and associated state management, focusing on the current template ID and its associated data. Debug messages have been updated to reflect these changes, ensuring better tracking and feedback during video editing sessions.
2025-09-26 18:42:35 +02:00
9df6d73db8 Enhance template management in VideoEditor by removing future templates and ending current ones
This commit updates the template handling logic in the VideoEditor to remove any templates that start after the current frame and properly end templates that start at the current frame. This change improves the clarity and efficiency of template management during video editing sessions, ensuring that only relevant templates are active. Debug messages have been added to provide insights into the template removal and ending process.
2025-09-26 18:31:10 +02:00
01340a0a81 Refactor and streamline template matching logic in VideoEditor
This commit removes unused methods and simplifies the template matching process by eliminating multi-scale matching in favor of a single-scale approach. The changes enhance performance and clarity in the tracking logic, ensuring a more efficient template matching experience. Debug messages have been updated to reflect the current state of the template matching process.
2025-09-26 18:30:19 +02:00
44ed4220b9 Refactor template matching logic in VideoEditor to support full frame and cropped modes
This commit introduces a toggle for template matching modes in the VideoEditor, allowing users to choose between full frame and cropped region matching. The logic has been updated to handle both modes effectively, improving flexibility and performance during template tracking. Additionally, the multi-scale template matching feature has been removed, streamlining the template matching process. Debug messages and feedback have been enhanced to reflect the current mode, ensuring better user experience during video editing sessions.
2025-09-26 18:20:12 +02:00
151744d144 Update 2025-09-26 17:56:28 +02:00
e823a11929 Enhance template matching in VideoEditor by applying motion tracking offsets
This commit updates the template matching logic in the VideoEditor to apply motion tracking offsets to the current frame before performing template matching. The new method, _apply_motion_tracking_offset, creates an offset frame based on the base position, improving tracking accuracy. This change simplifies the process by removing the previous cropping logic and ensures that template matching works effectively on the adjusted frame, enhancing overall performance during video editing sessions.
2025-09-26 17:54:56 +02:00
c1c01e86ca Optimize template matching in VideoEditor to utilize cropped regions for improved performance
This commit modifies the template matching logic in the VideoEditor to use only the cropped region of the frame when available, significantly enhancing the speed of template tracking. If no crop is applied, the original frame is used for tracking. This change improves the efficiency of the template matching process while maintaining accuracy in locating objects during video editing sessions.
2025-09-26 17:52:09 +02:00
184aceeee3 Enhance template selection and tracking logic in VideoEditor
This commit introduces a mechanism to select the best template for the current frame if templates are available. Additionally, it updates the template matching process to apply tracking directly on the original frame, avoiding infinite recursion and improving tracking accuracy. These changes enhance the overall functionality and reliability of the template management system during video editing sessions.
2025-09-26 17:50:07 +02:00
db2aa57ce5 Refactor template matching logic in VideoEditor to utilize transformed frames
This commit updates the template matching process in the VideoEditor to apply tracking on the already transformed frame, which includes crop, zoom, and rotation adjustments. This change enhances tracking accuracy by ensuring that the template can locate the object in its modified position, while also improving code clarity and performance during video editing sessions.
2025-09-26 17:49:29 +02:00
92c2e62166 Refactor template matching logic in VideoEditor to use original frame for tracking
This commit updates the template matching process in the VideoEditor to apply tracking directly on the original frame instead of a cropped region. This change ensures that the template can accurately locate the object in its original position, improving tracking reliability. The code has been simplified by removing the previous cropping logic, enhancing overall clarity and performance during video editing sessions.
2025-09-26 17:45:19 +02:00
86c31a49d9 Refactor template management in VideoEditor to enhance tracking functionality
This commit removes the initial template selection logic from the tracking method and introduces a mechanism to disable template matching when no templates are available. Additionally, it updates the comments to clarify the enabling of template matching upon template creation, improving the overall management of templates during video editing sessions.
2025-09-26 17:42:30 +02:00
f5b8656bc2 Refactor template navigation methods in VideoEditor for improved functionality
This commit renames and updates the template navigation methods in the VideoEditor to enhance user experience. The methods now allow users to jump directly to the previous or next template markers, improving frame handling and feedback. Debug messages have been added to provide clearer insights during navigation, ensuring users are informed about the current actions and template positions.
2025-09-26 17:39:02 +02:00
b9c60ffc25 Refactor template dot rendering and right-click functionality in VideoEditor
This commit simplifies the rendering of template dots in the VideoEditor by standardizing their appearance to match motion tracking points. Additionally, it enhances the right-click functionality to allow for template removal when clicking near template centers, improving user interaction and feedback during video editing sessions.
2025-09-26 17:37:35 +02:00
b6c7863b77 Refactor template navigation in VideoEditor for improved frame handling and user feedback
This commit updates the navigation methods for templates in the VideoEditor, enhancing the logic to jump to the start frame of the selected template. The feedback messages have been improved to provide clearer information about the selected template and its corresponding frame. Additionally, visual indicators for active templates have been added, allowing users to easily identify which templates are currently in use during video editing sessions.
2025-09-26 17:34:27 +02:00
612d024161 Update VideoEditor to manage template lifecycle and improve frame handling
This commit enhances the VideoEditor by implementing logic to end the previous template at the current frame when a new template is added. It also updates the method for retrieving the best template for a given frame to prioritize the most recent template. Additionally, a new method is introduced to recreate templates from their data region, ensuring templates are correctly initialized when needed. These changes improve template management and user feedback during video editing sessions.
2025-09-26 17:31:34 +02:00
840440eb1a Add multiple templates management to VideoEditor
This commit introduces a system for managing multiple templates within the VideoEditor. Users can now add, remove, and navigate through templates, enhancing the flexibility of template tracking. The state management has been updated to save and load multiple templates, including their regions and frame ranges. Additionally, visual indicators for active templates have been implemented in the frame display, improving user feedback during editing sessions.
2025-09-26 17:23:04 +02:00
c3bf49f301 Implement fullscreen scaling for video display in VideoEditor
This commit adds functionality to scale down the video display when in fullscreen mode if the video dimensions exceed the available screen size. This enhancement ensures that videos are properly displayed within the window dimensions, improving user experience during video editing.
2025-09-26 15:22:57 +02:00
192a5c7124 Disable multi-scale template matching by default in VideoEditor and update state management
This commit changes the default setting for multi-scale template matching to false, streamlining the initial configuration. It also updates the state management to include the multi-scale setting, ensuring that the user's preference is preserved across sessions. Additionally, the display information has been enhanced to reflect the current template matching status, improving user feedback during video editing.
2025-09-26 14:59:30 +02:00
2246ef9f45 Enable multi-scale template matching in VideoEditor with toggle functionality
This commit introduces a toggle for multi-scale template matching, allowing users to switch between multi-scale and single-scale approaches for improved tracking accuracy. The default setting is now multi-scale, enhancing the template matching process by evaluating templates at various sizes. Additionally, debug messages and user feedback have been updated to reflect this new feature.
2025-09-26 14:56:06 +02:00
c52d9b9399 Enhance template matching in VideoEditor with multi-scale approach and adaptive thresholding
This commit introduces a multi-scale template matching technique to improve tracking accuracy by evaluating templates at various sizes. Additionally, it implements adaptive thresholding based on recent match confidences, allowing for more dynamic and responsive matching criteria. A new method for image preprocessing is also added to enhance contrast and reduce noise, further optimizing the template matching process.
2025-09-26 14:53:16 +02:00
10284dad81 Implement template matching position retrieval in VideoEditor
This commit introduces a new method to get the template matching position and confidence for a specific frame. It optimizes the tracking process by utilizing cropped regions for faster template matching while maintaining functionality for full frame matching when no crop is set. Additionally, it updates the rendering logic to visualize the template matching point on the canvas, enhancing user feedback with confidence levels displayed. This update improves the overall accuracy and efficiency of the template matching feature.
2025-09-26 14:51:04 +02:00
a2dc4a2186 Refactor tracking position calculation in VideoEditor to compute the average of all available positions from manual, template, and feature tracking methods. This change simplifies the logic by aggregating positions instead of calculating weighted offsets, enhancing accuracy and clarity in tracking results. Debug messages have been updated to reflect the new averaging process. 2025-09-26 14:48:17 +02:00
5d76681ded Refactor tracking position calculation in VideoEditor to combine manual, template, and feature tracking methods. This update introduces a more robust approach to determine the final tracking position by calculating offsets from both template matching and feature tracking, weighted appropriately against a base position derived from manual tracking points. The logic ensures smoother transitions and improved accuracy in tracking results, with debug messages added for better insight into the combined tracking process. 2025-09-26 14:44:19 +02:00
f8acef2da4 Update VideoEditor to set current display frame during rendering for improved motion tracking
This commit adds functionality to store the current display frame and update the current frame index during the rendering process. This enhancement supports better motion tracking by ensuring the correct frame is referenced, contributing to more accurate processing of video frames.
2025-09-26 14:37:23 +02:00
65b80034cb Enhance template matching in VideoEditor by utilizing cropped regions for improved speed and accuracy. This update modifies the tracking logic to prioritize cropped areas for faster processing, while maintaining functionality for full frame matching when no crop is set. Debug messages have been refined to provide clearer insights into tracking results and confidence levels. 2025-09-26 14:35:51 +02:00
5400592afd Refactor template matching in VideoEditor to use raw frame coordinates for improved accuracy. This update simplifies the tracking logic by directly utilizing the raw display frame, eliminating unnecessary transformations. Additionally, it enhances the template setting process by ensuring the selected region is correctly mapped to raw frame coordinates, improving usability and feedback during template selection. Debug messages have been updated to reflect these changes. 2025-09-26 14:34:48 +02:00
e6616ed1b1 Optimize template matching in VideoEditor by utilizing cropped regions for faster processing. This update modifies the tracking logic to first check for a defined crop rectangle, allowing for quicker template matching on smaller frames. If no crop is set, the full frame is used, maintaining the previous functionality. Debug messages remain to assist in tracking accuracy and confidence levels. 2025-09-26 14:33:36 +02:00
048e8ef033 Refactor template management in VideoEditor to improve tracking functionality
This commit removes the direct storage of the tracking template and introduces a method to recreate the template from the specified region when needed. It enhances the template tracking logic by ensuring that the template is dynamically generated based on the current frame and region, improving accuracy and usability. Debug messages have been added to assist in tracking the template recreation process.
2025-09-26 14:31:35 +02:00
c08d5c5999 Refactor template tracking in VideoEditor to use raw display frame
This commit modifies the template tracking logic in the VideoEditor to utilize the raw display frame instead of applying transformations, improving accuracy in tracking. It updates the coordinate mapping to account for various rotation angles, ensuring correct positioning of the tracked template. This enhancement streamlines the tracking process and enhances overall functionality.
2025-09-26 14:29:50 +02:00
8c1efb1b05 Add template selection rectangle visualization in VideoEditor
This commit introduces a new feature in the VideoEditor that allows users to visualize the template selection rectangle on the canvas. The rectangle is drawn in magenta, enhancing the user interface and providing clearer feedback during template selection. This update complements the existing functionality for selective feature deletion and improves overall usability.
2025-09-26 14:27:40 +02:00
f942392fb3 Enhance VideoEditor with template matching state management and user interaction updates
This commit adds functionality to manage the state of template matching in the VideoEditor, including loading and saving template matching settings. It also updates user interaction for selecting template regions, changing the control scheme from Alt+Right-click to Ctrl+Left-click for better usability. Additionally, it improves the handling of the current display frame during feature extraction, ensuring robustness in the tracking process.
2025-09-26 14:26:42 +02:00
c749d9af80 Add template matching tracking to VideoEditor
This commit introduces a new tracking method using template matching, enhancing the tracking capabilities of the VideoEditor. It includes the ability to set a tracking template from a selected region, track the template in the current frame, and toggle template matching on and off. Additionally, debug messages have been added to provide insights during the template tracking process, improving user experience and functionality.
2025-09-26 14:24:31 +02:00
71e5870306 Enhance feature tracking with smooth interpolation and center calculation
This commit improves the feature tracking logic in the VideoEditor by implementing smooth interpolation between feature positions across frames. It introduces methods to calculate the center of features for a given frame, ensuring a more fluid tracking experience. These enhancements provide better continuity in feature tracking, particularly when features are sparse, and improve overall user experience.
2025-09-26 14:21:14 +02:00
e813be2890 Improve feature tracking logic and debugging in VideoEditor
This commit refines the feature tracking process by enhancing the get_tracking_position method to handle cases where frame features are missing. It also adds detailed debug messages throughout the VideoEditor class, particularly during frame seeking and feature interpolation, to provide better insights into the feature availability and interpolation process. These changes improve the robustness and user experience of the feature tracking functionality.
2025-09-26 14:19:42 +02:00
80fb35cced Implement feature interpolation and gap filling in optical flow tracking
This commit introduces methods for interpolating features between frames and filling gaps in feature tracking using linear interpolation. It enhances the optical flow tracking capabilities by ensuring continuity of features across frames. Debug messages have been added to provide insights during the interpolation process, improving the overall functionality and user experience in the VideoEditor.
2025-09-26 14:14:15 +02:00
d8b4439382 Add optical flow tracking for feature tracking in VideoEditor
This commit introduces a new method for tracking features using Lucas-Kanade optical flow, enhancing the feature tracking capabilities. It includes logic to toggle optical flow tracking, store previous frames for flow calculations, and update feature positions based on optical flow results. Debug messages have been added to provide insights during the tracking process, improving user experience and functionality.
2025-09-26 14:11:05 +02:00
463228baf5 Enhance feature tracking to handle descriptor dimension mismatches
This commit updates the feature tracking logic to check for descriptor dimension mismatches when adding features to existing entries. If a mismatch is detected, existing features are replaced instead of concatenated, ensuring data integrity. Additionally, debug messages have been added to provide better insights during the feature extraction process, improving overall user experience.
2025-09-26 14:04:25 +02:00
e7571a78f4 Refactor feature extraction to handle SURF fallback and add features from regions
This commit updates the feature extraction process to provide a fallback to SIFT when SURF is not available, enhancing robustness. It introduces a new method to extract features from specific regions of a frame, allowing for the addition of features to existing ones. Feedback messages have been improved to reflect these changes, ensuring better user experience and clarity during feature extraction.
2025-09-26 14:00:01 +02:00
ea008ba23c Implement shift-right click and ctrl-right click for feature extraction 2025-09-26 13:57:00 +02:00
366c338c5d Finally reinvent the whole mapping procedure again! 2025-09-26 13:54:01 +02:00
0d26ffaca4 Refactor feature extraction to directly use rotated frame coordinates in VideoEditor
This commit updates the feature extraction process to store and utilize features in rotated frame coordinates, aligning with existing motion tracking methods. It simplifies the coordinate mapping logic by removing unnecessary transformations, enhancing the accuracy of feature tracking. Additionally, feedback messages have been refined to reflect the changes in feature extraction, improving user experience.
2025-09-26 13:43:42 +02:00
aaf78bf0da Refactor feature extraction to utilize transformed frames in VideoEditor
This commit enhances the feature extraction process by ensuring that features are extracted from the transformed frame that users see, accounting for all transformations such as cropping, zooming, and rotation. It simplifies the coordinate mapping logic, allowing for accurate feature tracking without the need for manual coordinate adjustments. Feedback messages have been updated to reflect the extraction from the visible area, improving user experience.
2025-09-26 13:40:10 +02:00
43d350fff2 Refactor feature extraction to support cropping in VideoEditor
This commit updates the feature extraction process to handle cropped regions of the original frame, allowing for more accurate feature tracking based on the visible area. It introduces logic to extract features from both cropped and full frames, ensuring that coordinates are correctly mapped back to the original frame space. Feedback messages have been enhanced to inform users about the success or failure of feature extraction, improving the overall user experience.
2025-09-26 13:37:41 +02:00
d1b9e7c470 Enhance feature extraction with coordinate mapping in VideoEditor
This commit modifies the feature extraction process to include a coordinate mapper, allowing for accurate mapping of features from transformed frames back to their original coordinates. It introduces new methods for handling coordinate transformations during cropping and rotation, ensuring that features are correctly stored and retrieved in the appropriate frame space. This improvement enhances the tracking accuracy and overall functionality of the VideoEditor.
2025-09-26 13:24:48 +02:00
c50234f5c1 Refactor feature extraction to use transformed frames in VideoEditor
This commit updates the feature extraction process to utilize the transformed frames that users see, rather than the original frames. It includes a new method for mapping coordinates from transformed frames back to original coordinates, ensuring accurate tracking. Additionally, feedback messages have been improved to reflect the success or failure of feature extraction based on the visible area.
2025-09-26 13:21:59 +02:00
171155e528 Change some fucking keys it invented 2025-09-26 13:13:53 +02:00
710a1f7de3 Maybe implement a feature tracker 2025-09-26 13:10:47 +02:00
13fbc45b74 Add special case for _edited files for cleaner 2025-09-26 11:12:35 +02:00
8b4f8026cc Make cleaner work with all date videos 2025-09-26 11:08:15 +02:00
5c66935157 Add UTF-8 image loading support in VideoEditor and update dependencies 2025-09-26 11:05:14 +02:00
bae760837c Refactor display logic in MediaGrader to maintain aspect ratio and improve frame rendering
This commit enhances the display functionality by calculating the available height for video timelines, resizing frames while preserving their aspect ratio, and centering them on a canvas. The window resizing logic has been updated to ensure the entire monitor space is utilized effectively, improving the visual presentation of media files.
2025-09-26 10:59:15 +02:00
4a1649a568 Implement Cv2BufferedCap for improved video frame handling
This commit introduces the Cv2BufferedCap class, which optimizes video frame loading, seeking, and caching. The MediaGrader class has been updated to utilize this new class, enhancing frame accuracy and playback performance. Additionally, configuration constants have been adjusted for better playback speed control, and redundant backend handling has been removed to streamline video loading. Overall, these changes improve the efficiency and reliability of video playback in the application.
2025-09-19 18:23:15 +02:00
ea1a6e58f4 Enhance image and screenshot saving quality in VideoEditor
This commit updates the image and screenshot saving functionality in the VideoEditor class to use high-quality JPEG settings. The JPEG quality is set to 95, ensuring better image fidelity when saving processed frames and static images.
2025-09-19 07:17:52 +02:00
0c3e5e21bf Increase window dimensions in ProjectView and VideoEditor to accommodate 1080p videos. Adjust resizing logic to maintain original video quality by resizing the window instead of downscaling the video. Ensure frames fit within canvas bounds for better display. 2025-09-19 07:14:41 +02:00
472efbb9d9 Fix up speed controls to be ws and save to be S 2025-09-18 18:07:23 +02:00
dd2f40460b Max speed at 1 2025-09-18 17:59:55 +02:00
b2c7cf11e9 Fix seeking blyat 2025-09-18 17:58:04 +02:00
e0e5c8d933 Fix autorepeat 2025-09-18 17:54:06 +02:00
04e391551e Remove unused trash 2025-09-18 17:51:29 +02:00
f9f442a2d0 "optimnize" playback by removing retardation 2025-09-18 17:49:09 +02:00
0fd108bc9a Don't seek EVERY GOD DAMN FUCKING FRAME 2025-09-18 17:31:10 +02:00
83ef71934b Enhance tracking marker navigation in VideoEditor
This commit improves the navigation logic in the VideoEditor class for jumping to previous and next tracking markers. It ensures that when at the beginning of the tracking frames, the user jumps to the first marker, and when at the end, they jump to the last marker. Debug statements have been updated to reflect these changes, providing clearer insights during navigation. The specification has also been updated to document the new behavior.
2025-09-17 19:55:31 +02:00
b123b12d0d Refactor motion path logic in VideoEditor for clarity
This commit updates the VideoEditor class to improve the logic for drawing and checking motion paths between tracking points. The comments have been adjusted to clarify the conditions for drawing lines between previous→current and previous→next frames. Additionally, debug statements have been enhanced to provide clearer insights during the snapping process, ensuring better tracking point interactions.
2025-09-17 15:46:39 +02:00
1bd935646e Refactor motion path and snapping logic in VideoEditor
This commit enhances the VideoEditor class by refining the logic for drawing motion paths and checking line snapping between tracking points. It introduces a unified approach to handle both previous→next and previous→current lines, improving the clarity of the visual representation. Additionally, debug statements have been updated to provide better insights during the snapping process, ensuring accurate tracking point interactions. The display update method is also called to refresh the visual state after changes.
2025-09-17 15:44:55 +02:00
c3e0088a60 Refactor point-to-line distance calculation in VideoEditor
This commit refines the distance calculation method in the VideoEditor class by introducing a new function, _point_to_line_distance_and_foot, which computes the distance from a point to an infinite line and returns the foot of the perpendicular. The snapping logic has been updated to utilize this new method, enhancing the accuracy of line snapping between tracking points. Additionally, debug statements have been added to assist in tracking the snapping process and verifying point conversions.
2025-09-17 15:38:57 +02:00
68a1cc3e7d Enhance snapping functionality in VideoEditor to include line snapping
This commit improves the snapping feature in the VideoEditor class by adding the ability to snap new tracking points to the closest point on the line segment between consecutive tracking points, in addition to existing point snapping. The distance calculation logic has been refined, and the specification has been updated to reflect the new snapping behavior, which now operates within a 10px radius for both point and line snapping, enhancing the precision of motion tracking.
2025-09-17 15:12:35 +02:00
498a1911b1 Refactor constants to constants 2025-09-17 15:09:10 +02:00
d068da20f4 Remove some dead code 2025-09-17 15:08:42 +02:00
84a0748f0b Implement snapping functionality for tracking points in VideoEditor
This commit enhances the VideoEditor class by adding a snapping feature that allows users to snap new tracking points to existing points from any frame within a 50px radius. The logic first checks for the removal of existing points before determining if a new point should be added or if it should snap to a nearby point. The specification has been updated to reflect this new snapping behavior, improving the precision of motion tracking.
2025-09-17 15:05:12 +02:00
8c45b30bca Enhance VideoEditor with motion path visualization for tracking points
This commit updates the VideoEditor class to visualize the motion path between previous and next tracking points using yellow arrows. The rendering of tracking points has been modified, with previous points displayed as red circles and next points as magenta circles, both with white borders for better visibility. The specification has been updated to reflect these changes in the display of tracking points and their motion direction.
2025-09-17 14:34:26 +02:00
615a3dce0d Implement methods for retrieving previous and next tracking points in VideoEditor
This commit introduces two new methods, _get_previous_tracking_point and _get_next_tracking_point, to the VideoEditor class. These methods allow for the retrieval of tracking points from the previous and next frames that contain tracking data, enhancing navigation capabilities. The rendering logic has been updated to utilize these methods, ensuring that the previous (red) and next (green) tracking points are displayed correctly during video playback. Additionally, the specification has been updated to reflect these changes in the display of tracking points.
2025-09-17 14:29:11 +02:00
1ce05d33ba Add complete reset functionality to VideoEditor for all transformations
This commit introduces a new method, complete_reset, in the VideoEditor class, allowing users to reset all transformations and settings, including crop, zoom, rotation, brightness, contrast, and motion tracking. The method ensures a clean slate for editing by clearing the transformation cache and saving the state. Additionally, the specification has been updated to include this new functionality, enhancing user control over the editing environment.
2025-09-17 14:12:02 +02:00
1aea3b8a6e Update VideoEditor encoding settings for improved output quality
This commit modifies the encoding parameters in the VideoEditor class, changing the preset to 'veryslow' and adjusting the CRF value to 12 for enhanced video quality. Additionally, it introduces new parameters for profile and level settings, as well as x264 parameters for reference frames and B-frames, optimizing the encoding process for better performance and visual fidelity.
2025-09-17 13:55:13 +02:00
fbac3b0dbb Enhance VideoEditor mouse wheel functionality for zoom and frame seeking
This commit updates the mouse wheel event handling in the VideoEditor class to allow for zooming in and out with Ctrl+scroll, while enabling frame seeking with plain scroll. The specification has been updated to reflect the new mouse wheel behavior, improving user navigation and control during video editing.
2025-09-17 11:49:25 +02:00
b90b5e5725 Merge branch 'motion-track-01' 2025-09-17 11:42:15 +02:00
ed6f809029 Refactor marker navigation in VideoEditor to utilize tracking points
This commit enhances the VideoEditor class by updating the marker navigation methods to focus on tracking points instead of cut markers. The new methods, jump_to_previous_marker and jump_to_next_marker, now utilize a sorted list of frames with tracking points, improving navigation efficiency. Additionally, the documentation has been updated to reflect these changes, providing clearer instructions for users on how to navigate using tracking markers.
2025-09-17 11:41:45 +02:00
8a7e2609c5 Add marker navigation functionality to VideoEditor
This commit introduces keyboard controls for jumping to previous and next markers (cut start or end) in the VideoEditor class. The new functionality enhances user navigation during video editing, allowing for more efficient management of cut points. This addition improves the overall editing experience by providing quick access to key markers in the timeline.
2025-09-17 11:33:40 +02:00
dd1bc12667 Add exact frame seeking functionality to VideoEditor
This commit introduces a new method, seek_video_exact_frame, in the VideoEditor class, allowing users to seek video by exactly one frame, independent of the seek multiplier. The functionality is integrated with keyboard controls for navigating to the previous and next frames, enhancing precision in video editing. This addition improves user control and responsiveness during frame navigation.
2025-09-17 11:23:48 +02:00
9c14249f88 Update VideoEditor configuration for seek multiplier settings
This commit modifies the seek multiplier settings in the VideoEditor class, increasing the SEEK_MULTIPLIER_INCREMENT from 2.0 to 4.0 and expanding the MAX_SEEK_MULTIPLIER from 100.0 to 1000.0. These changes enhance the flexibility and responsiveness of the seeking functionality during video editing.
2025-09-17 11:18:35 +02:00
47ec7fed04 Enhance VideoEditor with previous and next frame tracking point visualization
This commit adds functionality to the VideoEditor class to render tracking points from the previous and next frames with 50% alpha blending. Red circles indicate previous frame points, while green circles represent next frame points, improving the visual feedback during video editing. Additionally, the feedback message duration has been reduced for a more responsive user experience.
2025-09-17 10:42:54 +02:00
e80278a2dd Refactor frame processing in VideoEditor to enhance cropping and rotation handling
This commit updates the frame processing logic in the VideoEditor class to apply rotation before cropping, ensuring accurate handling of frames in rotated space. It introduces effective cropping that accommodates out-of-bounds scenarios by padding with black, improving the visual output during editing. Additionally, debug statements have been refined to provide clearer information on output dimensions and effective crop details, enhancing the overall user experience in video editing.
2025-09-17 09:00:14 +02:00
f1d4145e43 Implement unified display parameters in VideoEditor for improved cropping and zoom handling
This commit introduces a new method, _get_display_params, in the VideoEditor class to centralize the calculation of display parameters, including effective crop dimensions, offsets, and scaling factors. The coordinate mapping methods have been updated to utilize these unified parameters, enhancing the accuracy of point mapping between original and screen coordinates during zoom and cropping operations. This refactor improves code readability and maintainability while ensuring a consistent user experience in video editing.
2025-09-17 08:06:52 +02:00
1d987a341a Refactor visibility calculations in VideoEditor to ensure proper cropping bounds
This commit updates the visibility width and height calculations in the VideoEditor class to use the minimum of the new dimensions and the window dimensions when cropping due to zoom. This change enhances the accuracy of the displayed area during zoom operations, ensuring that the canvas scaling is correctly applied and improving the overall user experience in video editing.
2025-09-17 01:56:16 +02:00
47ce52da37 Refactor coordinate mapping in VideoEditor to improve effective crop handling
This commit updates the coordinate mapping methods in the VideoEditor class to utilize the effective crop dimensions more accurately. It clarifies the calculations for mapping points between rotated frame coordinates and screen coordinates, ensuring consistent behavior during zoom and cropping operations. Additionally, comments have been refined for better understanding of the effective crop application, enhancing code readability and maintainability.
2025-09-17 01:54:04 +02:00
6c86271428 Refactor cropping logic in VideoEditor to utilize effective crop rectangle
This commit updates the cropping functionality in the VideoEditor class to use an effective crop rectangle derived from the current frame. It ensures that the crop is applied correctly within the bounds of the processed frame, enhancing the accuracy of cropping after rotation. Additionally, a visual outline of the effective crop is drawn on the canvas for debugging purposes, improving the user experience during video editing.
2025-09-17 01:51:56 +02:00
d0d2f66b11 Enhance effective crop rectangle calculation in VideoEditor for motion tracking
This commit updates the _get_effective_crop_rect_for_frame method to improve the calculation of the crop rectangle in rotated frame coordinates, incorporating tracking follow functionality. It ensures that the crop rectangle is accurately centered on the interpolated tracking position when motion tracking is enabled. Additionally, comments have been clarified to reflect the use of the effective crop, enhancing code readability and maintainability.
2025-09-17 01:48:49 +02:00
eeaeff6fe0 Enhance tracking point management in VideoEditor with rotated frame coordinates
This commit updates the VideoEditor class to store and manage tracking points in rotated frame coordinates, improving accuracy in overlay rendering and user interactions. It introduces a new method for mapping rotated coordinates to screen space and modifies existing methods to ensure consistent handling of coordinates throughout the editing process. These changes enhance the overall functionality and user experience when working with motion tracking in video editing.
2025-09-17 01:44:58 +02:00
d478b28e0d Refactor cropping and coordinate mapping in VideoEditor to support rotation
This commit modifies the cropping functionality to apply transformations in rotated frame coordinates, ensuring accurate cropping after rotation. It introduces a new method for mapping screen coordinates back to rotated frame coordinates, enhancing the overall cropping experience. Additionally, debug statements are added for better tracking of crop operations, improving the debugging process during development.
2025-09-17 01:42:08 +02:00
b440da3094 Refactor coordinate mapping in VideoEditor for improved zoom and rotation handling
This commit enhances the _map_original_to_screen and _map_screen_to_original methods by clarifying the calculations for zoom and rotation. It introduces new variables for better readability and ensures accurate mapping of coordinates, including adjustments for display offsets. The changes streamline the processing of frame dimensions and improve the overall functionality of the video editing experience.
2025-09-17 01:19:12 +02:00
fdf7d98850 Add motion tracking functionality to VideoEditor
This commit introduces motion tracking capabilities, allowing users to add and remove tracking points on video frames. The tracking state is managed with new attributes, and the crop functionality is enhanced to follow the tracked motion. Additionally, the user interface is updated to reflect the tracking status, and keyboard shortcuts are added for toggling tracking and clearing points. This feature improves the editing experience by enabling dynamic cropping based on motion analysis.
2025-09-17 01:14:26 +02:00
3ee5c1bddc Update spec 2025-09-17 00:18:40 +02:00
1d1d113a92 Refactor tracking point management in VideoEditor and MotionTracker to ensure accurate display coordinates
This commit modifies the handling of tracking points in the VideoEditor and MotionTracker classes by removing the storage of display coordinates. Instead, display coordinates are now calculated dynamically during rendering, ensuring accuracy regardless of crop or rotation states. The changes enhance the consistency of point transformations and improve logging for better debugging and verification of coordinate accuracy during mouse interactions.
2025-09-16 22:07:16 +02:00
e162e4fe92 Refactor tracking point management in VideoEditor and MotionTracker to ensure accurate display coordinates
This commit modifies the handling of tracking points in the VideoEditor and MotionTracker classes by removing the storage of display coordinates. Instead, display coordinates are now calculated dynamically during rendering, ensuring accuracy regardless of crop or rotation states. The changes enhance the consistency of point transformations and improve logging for better debugging and verification of coordinate accuracy during mouse interactions.
2025-09-16 21:34:17 +02:00
cd86cfc9f2 Enhance tracking point management in VideoEditor and MotionTracker with dual coordinate storage
This commit introduces a new TrackingPoint class to encapsulate both original and display coordinates for tracking points, improving the accuracy and consistency of point transformations. The VideoEditor class has been updated to utilize this new structure, allowing for better handling of tracking points during video editing. Additionally, logging has been enhanced to provide clearer insights into the addition and processing of tracking points, while redundant verification steps have been removed for efficiency. This change streamlines the tracking process and improves the overall user experience.
2025-09-16 21:33:28 +02:00
33a553c092 Refine VideoEditor point transformation methods with enhanced consistency and logging
This commit improves the point transformation and untransformation methods in the VideoEditor class by ensuring they match the applied transformations precisely. It adds checks for null points and current display frames, enhancing robustness. Additionally, detailed logging has been introduced to track coordinate adjustments during cropping and transformations, aiding in debugging and ensuring consistent behavior across the coordinate mapping process.
2025-09-16 20:58:54 +02:00
2979dca40a Refine VideoEditor point transformation and crop handling with enhanced logging
This commit improves the point transformation methods in the VideoEditor class by incorporating interpolated positions for cropping and refining the handling of coordinates during transformations. It adds detailed logging to track the adjustments made to crop centers and the verification of transformed points, ensuring better debugging and visibility into the state of the video editing process. Additionally, it enhances bounds checking for transformed points, maintaining consistency with the original frame dimensions.
2025-09-16 20:38:16 +02:00
cb097c55f1 Enhance VideoEditor with improved point transformation, bounds checking, and debugging features
This commit refines the point transformation methods in the VideoEditor class, ensuring coordinates are validated against frame dimensions and crop areas. It adds detailed logging for point transformations and mouse interactions, improving visibility into the state of tracking points. Additionally, it introduces debug features for visualizing crop rectangles and point indices, enhancing the debugging experience during video editing.
2025-09-16 20:32:33 +02:00
70364d0458 Update .gitignore and enhance VideoEditor with improved crop handling and logging
This commit adds a new entry to the .gitignore file to exclude log files. In the VideoEditor class, it refines the crop position adjustment logic to calculate the center of the crop rectangle before applying offsets, ensuring more accurate positioning. Additionally, it enhances logging throughout the point transformation and tracking processes, providing better insights into the state of tracking points and their visibility relative to the crop area.
2025-09-16 20:24:20 +02:00
c88c2cc354 Enhance VideoEditor and MotionTracker with improved logging and crop handling
This commit adds detailed logging to the VideoEditor and MotionTracker classes, providing insights into the transformations and adjustments made during point processing and cropping. It refines the crop position adjustment logic to ensure offsets are only applied when necessary and enhances the visibility of tracking points based on their position relative to the crop area. Additionally, it improves the handling of motion tracking toggling, ensuring a default crop rect is created when needed, thus enhancing the overall user experience during video editing.
2025-09-16 20:17:54 +02:00
9085a82bdd Enhance VideoEditor with improved point transformation and tracking logic
This commit refines the point transformation process in the VideoEditor class by ensuring coordinates are converted to floats and validating their positions relative to the crop area. It also updates the right-click event handling to accurately convert display coordinates to original frame coordinates, allowing for better interaction with tracking points. Additionally, the MotionTracker class is modified to set a default zoom center based on the crop rect if none is provided, improving the tracking functionality.
2025-09-16 20:04:34 +02:00
85891a5f99 Add motion tracking functionality to VideoEditor
This commit introduces a new MotionTracker class for handling motion tracking during video editing. The VideoEditor class has been updated to integrate motion tracking features, including adding and removing tracking points, interpolating positions, and applying tracking offsets during cropping. The user can toggle motion tracking and clear tracking points via keyboard shortcuts. Additionally, the state management has been enhanced to save and load motion tracking data, improving the overall editing experience.
2025-09-16 19:56:58 +02:00
66b23834fd Add spec file 2025-09-16 19:46:16 +02:00
5baa2572ea Add Cv2BufferedCap class for efficient video frame handling in VideoEditor
This commit introduces the Cv2BufferedCap class, which provides a buffered wrapper around cv2.VideoCapture. It implements frame caching with LRU eviction to optimize frame retrieval and reduce latency during video playback. The VideoEditor class has been updated to utilize this new class, enhancing performance and simplifying frame management. Unused frame cache methods have been removed to streamline the codebase.
2025-09-16 13:35:55 +02:00
c7c092d3f3 Implement frame caching in VideoEditor: add methods for managing frame cache with LRU eviction, improving playback performance by reducing frame retrieval time. Clear cache when switching videos to optimize memory usage. 2025-09-16 13:29:09 +02:00
f0d540be27 Add refresh_progress_data method in ProjectView and update VideoEditor to call it on state save. This ensures progress data is up-to-date when the editor state changes, enhancing user experience and project management. 2025-09-16 10:06:44 +02:00
c8dfcca954 Refactor thumbnail caching in ProjectView: store original thumbnails by video path instead of size, allowing for on-demand resizing. This change optimizes thumbnail generation and improves performance by reducing redundant processing. 2025-09-16 10:05:09 +02:00
b9cf9f0125 Update keyboard shortcuts in ProjectView: correct functionality for adjusting items per row, swapping the actions for 'Q' and 'Y' to improve user navigation and experience. Revise instructions to reflect these changes. 2025-09-16 10:04:00 +02:00
b8899004f3 Refactor ProjectView to improve thumbnail layout and item display: set default items per row to 2, implement dynamic thumbnail size calculation, and update keyboard shortcuts for adjusting items per row. Enhance thumbnail caching mechanism to optimize performance and maintain aspect ratio during resizing. 2025-09-16 10:02:53 +02:00
8c4663c4ef Remove unnecessary mouse interaction handling in VideoEditor when in project view mode, streamlining the mouse callback functionality. 2025-09-16 09:56:50 +02:00
9dd0c837b4 Update keyboard shortcuts in ProjectView: add 'q' for quitting and refine instructions for user navigation. Enhance VideoEditor to handle quit action, improving overall user experience. 2025-09-16 09:54:16 +02:00
c56b012246 Enhance ProjectView responsiveness: dynamically adjust canvas size and layout based on actual window dimensions, improving thumbnail placement and visibility. Update calculations for text positioning and item display to ensure consistent user experience across varying window sizes. 2025-09-16 09:48:11 +02:00
46f4441357 Improve thumbnail handling in ProjectView: add bounds checking for thumbnail placement on canvas and adjust resizing logic to prevent exceeding canvas dimensions. Update thumbnail size constraints for resizing operations to ensure minimum size limits. 2025-09-16 09:46:14 +02:00
d60828d787 Refactor thumbnail layout and navigation in ProjectView: dynamically calculate items per row based on window width and thumbnail size, and add keyboard shortcuts for resizing thumbnails. Update instructions to reflect new functionality. 2025-09-16 09:43:01 +02:00
cfd919a377 Enhance VideoEditor functionality: implement loading of saved state when opening videos, ensuring continuity in user experience across sessions. 2025-09-16 09:37:30 +02:00
d235fa693e Update filename display logic and improve video editor behavior: increase filename length limit and adjust text rendering properties. Maintain project view when switching to video editor, enhancing user interface fluidity. 2025-09-16 09:36:49 +02:00
97e4a140eb Add project view functionality to VideoEditor: implement video browsing with thumbnails, progress tracking, and keyboard navigation. Toggle between project and editor modes, enhancing user experience. 2025-09-16 09:35:32 +02:00
ae2b156b87 Refactor VideoEditor initialization: load first video and state after attribute setup, validate cut markers, and streamline default value assignments 2025-09-15 18:38:38 +02:00
01aaa36eb0 Add caching for video transformations to improve performance during auto-repeat seeking
???
2025-09-15 18:34:04 +02:00
10 changed files with 3212 additions and 478 deletions

1
.gitignore vendored
View File

@@ -1,3 +1,4 @@
__pycache__
croppa/build/lib
croppa/croppa.egg-info
*.log

View File

@@ -1,3 +1,14 @@
module tcleaner
go 1.23.6
require git.site.quack-lab.dev/dave/cylogger v1.4.0
require (
github.com/google/go-cmp v0.5.9 // indirect
github.com/hexops/valast v1.5.0 // indirect
golang.org/x/mod v0.7.0 // indirect
golang.org/x/sys v0.3.0 // indirect
golang.org/x/tools v0.4.0 // indirect
mvdan.cc/gofumpt v0.4.0 // indirect
)

28
cleaner/go.sum Normal file
View File

@@ -0,0 +1,28 @@
git.site.quack-lab.dev/dave/cylogger v1.4.0 h1:3Ca7V5JWvruARJd5S8xDFwW9LnZ9QInqkYLRdrEFvuY=
git.site.quack-lab.dev/dave/cylogger v1.4.0/go.mod h1:wctgZplMvroA4X6p8f4B/LaCKtiBcT1Pp+L14kcS8jk=
github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/hexops/autogold v0.8.1 h1:wvyd/bAJ+Dy+DcE09BoLk6r4Fa5R5W+O+GUzmR985WM=
github.com/hexops/autogold v0.8.1/go.mod h1:97HLDXyG23akzAoRYJh/2OBs3kd80eHyKPvZw0S5ZBY=
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
github.com/hexops/valast v1.5.0 h1:FBTuvVi0wjTngtXJRZXMbkN/Dn6DgsUsBwch2DUJU8Y=
github.com/hexops/valast v1.5.0/go.mod h1:Jcy1pNH7LNraVaAZDLyv21hHg2WBv9Nf9FL6fGxU7o4=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
golang.org/x/mod v0.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA=
golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ=
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/tools v0.4.0 h1:7mTAgkunk3fr4GAloyyCasadO6h9zSsQZbwvcaIciV4=
golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ=
mvdan.cc/gofumpt v0.4.0 h1:JVf4NN1mIpHogBj7ABpgOyZc65/UUOkKQFkoURsz4MM=
mvdan.cc/gofumpt v0.4.0/go.mod h1:PljLOHDeZqgS8opHRKLzp2It2VBuSdteAgqUfzMTxlQ=

View File

@@ -1,7 +1,7 @@
Windows Registry Editor Version 5.00
[HKEY_CURRENT_USER\Software\Classes\*\shell\Clean video name]
@="Clean video name"
[HKEY_CURRENT_USER\Software\Classes\*\shell\Clean name]
@="Clean name"
[HKEY_CURRENT_USER\Software\Classes\*\shell\Clean video name\command]
[HKEY_CURRENT_USER\Software\Classes\*\shell\Clean name\command]
@="C:\\Users\\administrator\\go\\bin\\tcleaner.exe \"%1\""

View File

@@ -6,51 +6,71 @@ import (
"os"
"path/filepath"
"regexp"
logger "git.site.quack-lab.dev/dave/cylogger"
)
func main() {
flag.Parse()
logger.InitFlag()
if flag.NArg() == 0 {
fmt.Println("Usage: cleaner <files>")
os.Exit(1)
}
// regex to match " - 2025-07-08 01h31m45s - "
re := regexp.MustCompile(` - (\d{4}-\d{2}-\d{2} \d{2}h\d{2}m\d{2}s) - `)
// regex to match "2025-07-08"
re := regexp.MustCompile(`\d{4}-\d{2}-\d{2}`)
editedRe := regexp.MustCompile(`_edited_\d{5}`)
for _, file := range flag.Args() {
filelog := logger.Default.WithPrefix(file)
filelog.Info("Processing file")
info, err := os.Stat(file)
if err != nil {
fmt.Printf("ERROR: %v\n", err)
filelog.Error("ERROR: %v\n", err)
continue
}
if info.IsDir() {
fmt.Printf("SKIP (directory): %s\n", file)
filelog.Info("SKIP (directory): %s\n", file)
continue
}
name := filepath.Base(file)
match := re.FindStringSubmatch(name)
filelog.Debug("Match: %v", match)
if match == nil {
fmt.Printf("SKIP (no date pattern): %s\n", name)
filelog.Info("SKIP (no date pattern): %s\n", name)
continue
}
newName := match[1] + filepath.Ext(name)
namePart := match[0]
editMatch := editedRe.FindStringSubmatch(name)
filelog.Debug("Edit match: %v", editMatch)
if editMatch != nil {
namePart = namePart + editMatch[0]
filelog.Info("Video has edited part, new name: %s", namePart)
}
newName := namePart + filepath.Ext(name)
filelog.Debug("New name: %s", newName)
if name == newName {
fmt.Printf("SKIP (already named): %s\n", name)
filelog.Info("SKIP (already named): %s\n", name)
continue
}
filelog.Debug("Checking if target exists: %s", newName)
if _, err := os.Stat(newName); err == nil {
fmt.Printf("SKIP (target exists): %s -> %s\n", name, newName)
filelog.Info("SKIP (target exists): %s -> %s\n", name, newName)
continue
}
filelog.Info("Renaming to: %s", newName)
err = os.Rename(name, newName)
if err != nil {
fmt.Printf("ERROR renaming %s: %v\n", name, err)
filelog.Error("ERROR renaming %s: %v\n", name, err)
} else {
fmt.Printf("RENAMED: %s -> %s\n", name, newName)
filelog.Info("RENAMED: %s -> %s\n", name, newName)
}
filelog.Info("All done")
continue
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -6,7 +6,8 @@ readme = "README.md"
requires-python = ">=3.13"
dependencies = [
"opencv-python>=4.8.0",
"numpy>=1.24.0"
"numpy>=1.24.0",
"Pillow>=10.0.0"
]
[project.scripts]

138
croppa/spec.md Normal file
View File

@@ -0,0 +1,138 @@
# Croppa - Feature Specification
## Overview
Croppa is a lightweight video and image editor that provides real-time editing capabilities with persistent state management.
## Notes:
Note the distinction between lowercase and uppercase keys
Uppercase keys imply shift+key
Note that every transformation (cropping, motion track points) are almost always applied to an already transformed frame
Be that by rotation, cropping, zooming or motion tracking itself
Which means that the user input must be "de-transformed" before being applied to the frame
In other words if we zoom into an area and right click to add a tracking point it must be added to that exact pixel ON THE ORIGINAL FRAME
And NOT the zoomed in / processed frame
Likwise with rotations
All coordinates (crop region, zoom center, motion tracking point) are to be in reference to the original raw unprocessed frame
To then display these points they must be transformed to the processed - display - frame
Likewise when accepting user input from the processed display frame the coordinates must be transformed back to the original raw unprocessed frame
A simple example if we are rotated by 90 degrees and click on the top left corner of the display frame
That coordinate is to be mapped to the bottom left corner of the original raw unprocessed frame
The input to the editor is either a list of video files
Or a directory
In the case a directory is provided the editor is to open "all" editable files in the given directory
In the case multiple files are open we are able to navigate between them using n and N keys for next and previous file
Be careful to save and load settings when navigating this way
## Core Features
### Video Playback
- **Space**: Play/pause video
- **a/d**: Seek backward/forward 1 frame
- **A/D**: Seek backward/forward 10 frames
- **Ctrl+a/d**: Seek backward/forward 60 frames
- **Mouse Wheel**: Seek backward/forward 1 frame (ignores seek multiplier)
- **W/S**: Increase/decrease playback speed (0.1x to 10.0x, increments of 0.2)
- **Q/Y**: Increase/decrease seek multiplier (multiplies the frame count for a/d/A/D/Ctrl+a/d keys by 1.0x to 100.0x, increments of 2.0)
- **q**: Quit the program
- **Timeline**: Click anywhere to jump to that position
- **Auto-repeat**: Hold seek keys for continuous seeking at 1 FPS rate
### Visual Transformations
- **-**: Rotate 90 degrees clockwise
- **e/E**: Increase/decrease brightness (-100 to +100, increments of 5)
- **r/R**: Increase/decrease contrast (0.1 to 3.0, increments of 0.1)
- **Ctrl+Scroll**: Zoom in/out (0.1x to 10.0x, increments of 0.1)
- **Ctrl+Click**: Set zoom center point
### Cropping
- **Shift+Click+Drag**: Select crop area with green rectangle preview
- **h/j/k/l**: Expand crop from right/down/up/left edges (15 pixels per keypress)
- **H/J/K/L**: Contract crop to left/down/up/right edges (15 pixels per keypress)
- **u**: Undo last crop
- **c**: Clear all cropping
- **C**: Complete reset (crop, zoom, rotation, brightness, contrast, tracking points, cut markers)
### Motion Tracking
- **Right-click**: Add tracking point (green circle with white border)
- **Right-click existing point**: Remove tracking point (within 10px)
- **Right-click near existing point**: Snap to existing point from any frame (within 10px radius)
- **Right-click near motion path**: Snap to closest point on yellow arrow line between tracking points (within 10px radius)
- **v**: Toggle motion tracking on/off
- **V**: Clear all tracking points
- **Blue cross**: Shows computed tracking position
- **Automatic interpolation**: Tracks between keyframes
- **Crop follows**: Crop area centers on tracked object
- **Display** Points are rendered as blue dots per frame, in addition the previous tracking point (red) and next tracking point (magenta) are shown with yellow arrows indicating motion direction
#### Motion Tracking Navigation
- **,**: Jump to previous tracking marker (previous frame that has one or more tracking points). Goes to first marker if at beginning.
- **.**: Jump to next tracking marker (next frame that has one or more tracking points). Goes to last marker if at end.
### Markers and Looping
- **1**: Set cut start marker at current frame
- **2**: Set cut end marker at current frame
- **t**: Toggle loop playback between markers
- **Red lines**: Markers shown on timeline with numbers
- **Continuous loop**: Playback loops between markers when enabled
### File Management
- **Enter**: Render video (overwrites if filename contains "_edited_")
- **b**: Render video with new "_edited_001" filename (does NOT overwrite!)
- **s**: Save screenshot with auto-incrementing filename (video_frame_00001.jpg, video_frame_00002.jpg, etc. - NEVER overwrite existing screenshots)
- **N/n**: Next/previous video in directory
- **p**: Toggle project view (directory browser)
### Project View
- **wasd**: Navigate through video thumbnails
- **e**: Open selected video
- **Q/Y**: Change thumbnail size (fewer/more per row, size automatically computed to fit row)
- **q**: Quit
- **Progress bars**: Show editing progress for each video (blue bar showing current_frame/total_frames)
- **ESC**: Return to editor
### Display and Interface
- **f**: Toggle fullscreen
- **Status overlay**: Shows "Frame: 1500/3000 | Speed: 1.5x | Zoom: 2.0x | Seek: 5.0x | Rotation: 90° | Brightness: 10 | Contrast: 1.2 | Motion: ON (3 pts) | Playing/Paused"
- **Timeline**: Visual progress bar with current position handle
- **Feedback messages**: Temporary on-screen notifications (e.g. "Screenshot saved: video_frame_00001.jpg")
- **Progress bar**: Shows rendering progress with FPS counter (e.g. "Processing 1500/3000 frames | 25.3 FPS")
### State Management
- **Auto-save**: Settings saved automatically on changes and on quit
- **Per-video state**: Each video remembers its own settings
- **Cross-session**: Settings persist between application restarts
- **JSON files**: State stored as .json files next to videos with the same name as the video
### Rendering
- **Background rendering**: Continue editing while rendering (rendering happens in separate thread, you can still seek/play/edit)
- **x**: Cancel active render
- **FFmpeg output**: Invoke FFmpeg process, pipe raw video frames via stdin, output MP4 with H.264 encoding (CRF 18, preset fast)
- **Progress tracking**: Real-time progress with FPS display
- **Overwrite protection**: Only overwrites files with "_edited_" in name
### Image Mode
- **Same controls**: All editing features work on static images
- **No playback**: Space key disabled, no timeline
- **Screenshot mode**: Treats single images like video frames
### Error Handling
- **Format support**: MP4, AVI, MOV, MKV, WMV, FLV, WebM, M4V, JPG, PNG, BMP, TIFF, WebP
- **Backend fallback**: Tries multiple video backends automatically
- **Error messages**: Clear feedback for common issues
- **Graceful degradation**: Continues working when possible
### Performance Features
- **Frame caching**: Smooth seeking with cached frames (cache the decoded frames, LRU eviction, max 3000 frames)
- **Transformation caching**: Fast repeated operations (cache transformed frames during auto-repeat seeking)
- **Memory management**: Automatic cache cleanup
### Window Management
- **Resizable**: Window can be resized dynamically
- **Multi-window**: Project view opens in separate window
- **Focus handling**: Keys only affect active window
- **Context menu**: Right-click integration on Windows
This specification describes what Croppa does from the user's perspective - the features, controls, and behaviors that make up the application.

269
main.py
View File

@@ -7,23 +7,74 @@ import argparse
import shutil
import time
import threading
import subprocess
import json
from concurrent.futures import ThreadPoolExecutor
from pathlib import Path
from typing import List
class Cv2BufferedCap:
"""Buffered wrapper around cv2.VideoCapture that handles frame loading, seeking, and caching correctly"""
def __init__(self, video_path, backend=None):
self.video_path = video_path
self.cap = cv2.VideoCapture(str(video_path), backend)
if not self.cap.isOpened():
raise ValueError(f"Could not open video: {video_path}")
# Video properties
self.total_frames = int(self.cap.get(cv2.CAP_PROP_FRAME_COUNT))
self.fps = self.cap.get(cv2.CAP_PROP_FPS)
self.frame_width = int(self.cap.get(cv2.CAP_PROP_FRAME_WIDTH))
self.frame_height = int(self.cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
# Current position tracking
self.current_frame = 0
def get_frame(self, frame_number):
"""Get frame at specific index - always accurate"""
# Clamp frame number to valid range
frame_number = max(0, min(frame_number, self.total_frames - 1))
# Optimize for sequential reading (next frame)
if frame_number == self.current_frame + 1:
ret, frame = self.cap.read()
else:
# Seek for non-sequential access
self.cap.set(cv2.CAP_PROP_POS_FRAMES, frame_number)
ret, frame = self.cap.read()
if ret:
self.current_frame = frame_number
return frame
else:
raise ValueError(f"Failed to read frame {frame_number}")
def advance_frame(self, frames=1):
"""Advance by specified number of frames"""
new_frame = self.current_frame + frames
return self.get_frame(new_frame)
def release(self):
"""Release the video capture"""
if self.cap:
self.cap.release()
def isOpened(self):
"""Check if capture is opened"""
return self.cap and self.cap.isOpened()
class MediaGrader:
BASE_FRAME_DELAY_MS = 16
# Configuration constants - matching croppa implementation
TARGET_FPS = 80 # Target FPS for speed calculations
SPEED_INCREMENT = 0.1
MIN_PLAYBACK_SPEED = 0.05
MAX_PLAYBACK_SPEED = 1.0
# Legacy constants for compatibility
KEY_REPEAT_RATE_SEC = 0.5
FAST_SEEK_ACTIVATION_TIME = 2.0
FRAME_RENDER_TIME_MS = 50
SPEED_INCREMENT = 0.2
MIN_PLAYBACK_SPEED = 0.1
MAX_PLAYBACK_SPEED = 100.0
FAST_SEEK_MULTIPLIER = 60
IMAGE_DISPLAY_DELAY_MS = 100
MONITOR_WIDTH = 2560
MONITOR_HEIGHT = 1440
@@ -108,30 +159,53 @@ class MediaGrader:
# Get frame dimensions
frame_height, frame_width = frame.shape[:2]
# Calculate aspect ratio
frame_aspect_ratio = frame_width / frame_height
monitor_aspect_ratio = self.MONITOR_WIDTH / self.MONITOR_HEIGHT
# Calculate available height (subtract timeline height for videos)
timeline_height = self.TIMELINE_HEIGHT if self.is_video(self.media_files[self.current_index]) else 0
available_height = self.MONITOR_HEIGHT - timeline_height
# Determine if frame is vertical or horizontal relative to monitor
if frame_aspect_ratio < monitor_aspect_ratio:
# Frame is more vertical than monitor - maximize height
display_height = self.MONITOR_HEIGHT
display_width = int(display_height * frame_aspect_ratio)
# Calculate scale to fit within monitor bounds while maintaining aspect ratio
scale_x = self.MONITOR_WIDTH / frame_width
scale_y = available_height / frame_height
scale = min(scale_x, scale_y)
# Calculate display dimensions
display_width = int(frame_width * scale)
display_height = int(frame_height * scale)
# Resize the frame to maintain aspect ratio
if scale != 1.0:
resized_frame = cv2.resize(frame, (display_width, display_height), interpolation=cv2.INTER_AREA)
else:
# Frame is more horizontal than monitor - maximize width
display_width = self.MONITOR_WIDTH
display_height = int(display_width / frame_aspect_ratio)
resized_frame = frame
# Resize window to calculated dimensions
cv2.resizeWindow("Media Grader", display_width, display_height)
# Create canvas with proper dimensions
canvas_height = self.MONITOR_HEIGHT
canvas_width = self.MONITOR_WIDTH
canvas = np.zeros((canvas_height, canvas_width, 3), dtype=np.uint8)
# Center the resized frame on canvas
start_y = (available_height - display_height) // 2
start_x = (self.MONITOR_WIDTH - display_width) // 2
# Ensure frame fits within canvas bounds
end_y = min(start_y + display_height, available_height)
end_x = min(start_x + display_width, self.MONITOR_WIDTH)
actual_height = end_y - start_y
actual_width = end_x - start_x
if actual_height > 0 and actual_width > 0:
canvas[start_y:end_y, start_x:end_x] = resized_frame[:actual_height, :actual_width]
# Resize window to full monitor size
cv2.resizeWindow("Media Grader", self.MONITOR_WIDTH, self.MONITOR_HEIGHT)
# Center the window on screen
x_position = (self.MONITOR_WIDTH - display_width) // 2
y_position = (self.MONITOR_HEIGHT - display_height) // 2
x_position = 0
y_position = 0
cv2.moveWindow("Media Grader", x_position, y_position)
# Display the frame
cv2.imshow("Media Grader", frame)
# Display the canvas with properly aspect-ratioed frame
cv2.imshow("Media Grader", canvas)
def find_media_files(self) -> List[Path]:
"""Find all media files recursively in the directory"""
@@ -158,19 +232,18 @@ class MediaGrader:
def calculate_frame_delay(self) -> int:
"""Calculate frame delay in milliseconds based on playback speed"""
delay_ms = int(self.BASE_FRAME_DELAY_MS / self.playback_speed)
# Round to 2 decimals to handle floating point precision issues
speed = round(self.playback_speed, 2)
if speed >= 1.0:
# Speed >= 1: maximum FPS (no delay)
return 1
else:
# Speed < 1: scale FPS based on speed
# Formula: fps = TARGET_FPS * speed, so delay = 1000 / fps
target_fps = self.TARGET_FPS * speed
delay_ms = int(1000 / target_fps)
return max(1, delay_ms)
def calculate_frames_to_skip(self) -> int:
"""Calculate how many frames to skip for high-speed playback"""
if self.playback_speed <= 1.0:
return 0
elif self.playback_speed <= 2.0:
return 0
elif self.playback_speed <= 5.0:
return int(self.playback_speed - 1)
else:
return int(self.playback_speed * 2)
def load_media(self, file_path: Path) -> bool:
"""Load media file for display"""
@@ -178,43 +251,17 @@ class MediaGrader:
self.current_cap.release()
if self.is_video(file_path):
# Try different backends for better performance
# For video files: FFmpeg is usually best, DirectShow is for cameras
backends_to_try = []
if hasattr(cv2, 'CAP_FFMPEG'): # FFmpeg - best for video files
backends_to_try.append(cv2.CAP_FFMPEG)
if hasattr(cv2, 'CAP_DSHOW'): # DirectShow - usually for cameras, but try as fallback
backends_to_try.append(cv2.CAP_DSHOW)
backends_to_try.append(cv2.CAP_ANY) # Final fallback
self.current_cap = None
for backend in backends_to_try:
try:
self.current_cap = cv2.VideoCapture(str(file_path), backend)
if self.current_cap.isOpened():
# Optimize buffer settings for better performance
self.current_cap.set(cv2.CAP_PROP_BUFFERSIZE, 1) # Minimize buffer to reduce latency
# Try to set hardware acceleration if available
if hasattr(cv2, 'CAP_PROP_HW_ACCELERATION'):
self.current_cap.set(cv2.CAP_PROP_HW_ACCELERATION, cv2.VIDEO_ACCELERATION_ANY)
break
self.current_cap.release()
except:
continue
if not self.current_cap or not self.current_cap.isOpened():
print(f"Warning: Could not open video file {file_path.name} (unsupported codec)")
return False
self.total_frames = int(self.current_cap.get(cv2.CAP_PROP_FRAME_COUNT))
# Use Cv2BufferedCap for better frame handling
self.current_cap = Cv2BufferedCap(file_path)
self.total_frames = self.current_cap.total_frames
self.current_frame = 0
# Get codec information for debugging
fourcc = int(self.current_cap.get(cv2.CAP_PROP_FOURCC))
codec = "".join([chr((fourcc >> 8 * i) & 0xFF) for i in range(4)])
backend = self.current_cap.getBackendName()
print(f"Loaded: {file_path.name} | Frames: {self.total_frames} | FPS: {self.current_cap.fps:.2f}")
print(f"Loaded: {file_path.name} | Codec: {codec} | Backend: {backend} | Frames: {self.total_frames}")
except Exception as e:
print(f"Warning: Could not open video file {file_path.name}: {e}")
return False
else:
self.current_cap = None
@@ -235,11 +282,12 @@ class MediaGrader:
if not self.current_cap:
return False
ret, frame = self.current_cap.read()
if ret:
self.current_display_frame = frame
self.current_frame = int(self.current_cap.get(cv2.CAP_PROP_POS_FRAMES))
try:
# Use Cv2BufferedCap to get frame
self.current_display_frame = self.current_cap.get_frame(self.current_frame)
return True
except Exception as e:
print(f"Failed to load frame {self.current_frame}: {e}")
return False
else:
frame = cv2.imread(str(self.media_files[self.current_index]))
@@ -672,7 +720,7 @@ class MediaGrader:
# Draw timeline
self.draw_timeline(frame)
# Maintain aspect ratio when displaying
# Display with proper aspect ratio
self.display_with_aspect_ratio(frame)
def display_multi_segment_frame(self):
@@ -789,7 +837,7 @@ class MediaGrader:
# Draw multi-segment timeline
self.draw_multi_segment_timeline(combined_frame)
# Maintain aspect ratio when displaying
# Display with proper aspect ratio
self.display_with_aspect_ratio(combined_frame)
def draw_multi_segment_timeline(self, frame):
@@ -904,38 +952,30 @@ class MediaGrader:
target_frame = max(0, min(target_frame, self.total_frames - 1))
# Seek to target frame
self.current_cap.set(cv2.CAP_PROP_POS_FRAMES, target_frame)
self.current_frame = target_frame
self.load_current_frame()
def advance_frame(self):
"""Advance to next frame(s) based on playback speed"""
if (
not self.is_video(self.media_files[self.current_index])
or not self.is_playing
):
return
"""Advance to next frame - handles playback speed and marker looping"""
if not self.is_playing:
return True
if self.multi_segment_mode:
self.update_segment_frames()
return True
else:
frames_to_skip = self.calculate_frames_to_skip()
# Always advance by 1 frame - speed is controlled by delay timing
new_frame = self.current_frame + 1
for _ in range(frames_to_skip + 1):
ret, frame = self.current_cap.read()
if not ret:
actual_frame = int(self.current_cap.get(cv2.CAP_PROP_POS_FRAMES))
if actual_frame < self.total_frames - 5:
print(f"Frame count mismatch! Reported: {self.total_frames}, Actual: {actual_frame}")
self.total_frames = actual_frame
return False
self.current_display_frame = frame
self.current_frame = int(self.current_cap.get(cv2.CAP_PROP_POS_FRAMES))
# Handle looping bounds
if new_frame >= self.total_frames:
# Loop to beginning
new_frame = 0
# Update current frame and load it
self.current_frame = new_frame
self.update_watch_tracking()
return True
return self.load_current_frame()
def seek_video(self, frames_delta: int):
"""Seek video by specified number of frames"""
@@ -952,7 +992,7 @@ class MediaGrader:
0, min(self.current_frame + frames_delta, self.total_frames - 1)
)
self.current_cap.set(cv2.CAP_PROP_POS_FRAMES, target_frame)
self.current_frame = target_frame
self.load_current_frame()
def process_seek_key(self, key: int) -> bool:
@@ -1165,28 +1205,38 @@ class MediaGrader:
cv2.setWindowTitle("Media Grader", window_title)
while True:
# Update display
self.display_current_frame()
if self.is_video(current_file):
if self.is_seeking:
delay = self.FRAME_RENDER_TIME_MS
# Calculate appropriate delay based on playback state
if self.is_playing and self.is_video(current_file):
# Use calculated frame delay for proper playback speed
delay_ms = self.calculate_frame_delay()
else:
delay = self.calculate_frame_delay()
else:
delay = self.IMAGE_DISPLAY_DELAY_MS
# Use minimal delay for immediate responsiveness when not playing
delay_ms = 1
key = cv2.waitKey(delay) & 0xFF
# Auto advance frame when playing (videos only)
if self.is_playing and self.is_video(current_file):
self.advance_frame()
# Key capture with appropriate delay
key = cv2.waitKey(delay_ms) & 0xFF
if key == ord("q") or key == 27:
return
elif key == ord(" "):
self.is_playing = not self.is_playing
elif key == ord("s"):
# Speed control only for videos
if self.is_video(current_file):
self.playback_speed = max(
self.MIN_PLAYBACK_SPEED,
self.playback_speed - self.SPEED_INCREMENT,
)
elif key == ord("w"):
# Speed control only for videos
if self.is_video(current_file):
self.playback_speed = min(
self.MAX_PLAYBACK_SPEED,
self.playback_speed + self.SPEED_INCREMENT,
@@ -1229,17 +1279,6 @@ class MediaGrader:
if self.is_seeking and self.current_seek_key is not None:
self.process_seek_key(self.current_seek_key)
if (
self.is_playing
and self.is_video(current_file)
and not self.is_seeking
):
if not self.advance_frame():
# Video reached the end, restart it instead of navigating
self.current_cap.set(cv2.CAP_PROP_POS_FRAMES, 0)
self.current_frame = 0
self.load_current_frame()
if key not in [ord("p"), ord("u"), ord("1"), ord("2"), ord("3"), ord("4"), ord("5")]:
print("Navigating to (pu12345): ", self.current_index)
self.current_index += 1

57
uv.lock generated
View File

@@ -15,12 +15,14 @@ source = { virtual = "croppa" }
dependencies = [
{ name = "numpy" },
{ name = "opencv-python" },
{ name = "pillow" },
]
[package.metadata]
requires-dist = [
{ name = "numpy", specifier = ">=1.24.0" },
{ name = "opencv-python", specifier = ">=4.8.0" },
{ name = "pillow", specifier = ">=10.0.0" },
]
[[package]]
@@ -85,6 +87,61 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/fa/80/eb88edc2e2b11cd2dd2e56f1c80b5784d11d6e6b7f04a1145df64df40065/opencv_python-4.12.0.88-cp37-abi3-win_amd64.whl", hash = "sha256:d98edb20aa932fd8ebd276a72627dad9dc097695b3d435a4257557bbb49a79d2", size = 39000307, upload-time = "2025-07-07T09:14:16.641Z" },
]
[[package]]
name = "pillow"
version = "11.3.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/f3/0d/d0d6dea55cd152ce3d6767bb38a8fc10e33796ba4ba210cbab9354b6d238/pillow-11.3.0.tar.gz", hash = "sha256:3828ee7586cd0b2091b6209e5ad53e20d0649bbe87164a459d0676e035e8f523", size = 47113069, upload-time = "2025-07-01T09:16:30.666Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/1e/93/0952f2ed8db3a5a4c7a11f91965d6184ebc8cd7cbb7941a260d5f018cd2d/pillow-11.3.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:1c627742b539bba4309df89171356fcb3cc5a9178355b2727d1b74a6cf155fbd", size = 2128328, upload-time = "2025-07-01T09:14:35.276Z" },
{ url = "https://files.pythonhosted.org/packages/4b/e8/100c3d114b1a0bf4042f27e0f87d2f25e857e838034e98ca98fe7b8c0a9c/pillow-11.3.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:30b7c02f3899d10f13d7a48163c8969e4e653f8b43416d23d13d1bbfdc93b9f8", size = 2170652, upload-time = "2025-07-01T09:14:37.203Z" },
{ url = "https://files.pythonhosted.org/packages/aa/86/3f758a28a6e381758545f7cdb4942e1cb79abd271bea932998fc0db93cb6/pillow-11.3.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:7859a4cc7c9295f5838015d8cc0a9c215b77e43d07a25e460f35cf516df8626f", size = 2227443, upload-time = "2025-07-01T09:14:39.344Z" },
{ url = "https://files.pythonhosted.org/packages/01/f4/91d5b3ffa718df2f53b0dc109877993e511f4fd055d7e9508682e8aba092/pillow-11.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ec1ee50470b0d050984394423d96325b744d55c701a439d2bd66089bff963d3c", size = 5278474, upload-time = "2025-07-01T09:14:41.843Z" },
{ url = "https://files.pythonhosted.org/packages/f9/0e/37d7d3eca6c879fbd9dba21268427dffda1ab00d4eb05b32923d4fbe3b12/pillow-11.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7db51d222548ccfd274e4572fdbf3e810a5e66b00608862f947b163e613b67dd", size = 4686038, upload-time = "2025-07-01T09:14:44.008Z" },
{ url = "https://files.pythonhosted.org/packages/ff/b0/3426e5c7f6565e752d81221af9d3676fdbb4f352317ceafd42899aaf5d8a/pillow-11.3.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2d6fcc902a24ac74495df63faad1884282239265c6839a0a6416d33faedfae7e", size = 5864407, upload-time = "2025-07-03T13:10:15.628Z" },
{ url = "https://files.pythonhosted.org/packages/fc/c1/c6c423134229f2a221ee53f838d4be9d82bab86f7e2f8e75e47b6bf6cd77/pillow-11.3.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f0f5d8f4a08090c6d6d578351a2b91acf519a54986c055af27e7a93feae6d3f1", size = 7639094, upload-time = "2025-07-03T13:10:21.857Z" },
{ url = "https://files.pythonhosted.org/packages/ba/c9/09e6746630fe6372c67c648ff9deae52a2bc20897d51fa293571977ceb5d/pillow-11.3.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c37d8ba9411d6003bba9e518db0db0c58a680ab9fe5179f040b0463644bc9805", size = 5973503, upload-time = "2025-07-01T09:14:45.698Z" },
{ url = "https://files.pythonhosted.org/packages/d5/1c/a2a29649c0b1983d3ef57ee87a66487fdeb45132df66ab30dd37f7dbe162/pillow-11.3.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:13f87d581e71d9189ab21fe0efb5a23e9f28552d5be6979e84001d3b8505abe8", size = 6642574, upload-time = "2025-07-01T09:14:47.415Z" },
{ url = "https://files.pythonhosted.org/packages/36/de/d5cc31cc4b055b6c6fd990e3e7f0f8aaf36229a2698501bcb0cdf67c7146/pillow-11.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:023f6d2d11784a465f09fd09a34b150ea4672e85fb3d05931d89f373ab14abb2", size = 6084060, upload-time = "2025-07-01T09:14:49.636Z" },
{ url = "https://files.pythonhosted.org/packages/d5/ea/502d938cbaeec836ac28a9b730193716f0114c41325db428e6b280513f09/pillow-11.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:45dfc51ac5975b938e9809451c51734124e73b04d0f0ac621649821a63852e7b", size = 6721407, upload-time = "2025-07-01T09:14:51.962Z" },
{ url = "https://files.pythonhosted.org/packages/45/9c/9c5e2a73f125f6cbc59cc7087c8f2d649a7ae453f83bd0362ff7c9e2aee2/pillow-11.3.0-cp313-cp313-win32.whl", hash = "sha256:a4d336baed65d50d37b88ca5b60c0fa9d81e3a87d4a7930d3880d1624d5b31f3", size = 6273841, upload-time = "2025-07-01T09:14:54.142Z" },
{ url = "https://files.pythonhosted.org/packages/23/85/397c73524e0cd212067e0c969aa245b01d50183439550d24d9f55781b776/pillow-11.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:0bce5c4fd0921f99d2e858dc4d4d64193407e1b99478bc5cacecba2311abde51", size = 6978450, upload-time = "2025-07-01T09:14:56.436Z" },
{ url = "https://files.pythonhosted.org/packages/17/d2/622f4547f69cd173955194b78e4d19ca4935a1b0f03a302d655c9f6aae65/pillow-11.3.0-cp313-cp313-win_arm64.whl", hash = "sha256:1904e1264881f682f02b7f8167935cce37bc97db457f8e7849dc3a6a52b99580", size = 2423055, upload-time = "2025-07-01T09:14:58.072Z" },
{ url = "https://files.pythonhosted.org/packages/dd/80/a8a2ac21dda2e82480852978416cfacd439a4b490a501a288ecf4fe2532d/pillow-11.3.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:4c834a3921375c48ee6b9624061076bc0a32a60b5532b322cc0ea64e639dd50e", size = 5281110, upload-time = "2025-07-01T09:14:59.79Z" },
{ url = "https://files.pythonhosted.org/packages/44/d6/b79754ca790f315918732e18f82a8146d33bcd7f4494380457ea89eb883d/pillow-11.3.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5e05688ccef30ea69b9317a9ead994b93975104a677a36a8ed8106be9260aa6d", size = 4689547, upload-time = "2025-07-01T09:15:01.648Z" },
{ url = "https://files.pythonhosted.org/packages/49/20/716b8717d331150cb00f7fdd78169c01e8e0c219732a78b0e59b6bdb2fd6/pillow-11.3.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1019b04af07fc0163e2810167918cb5add8d74674b6267616021ab558dc98ced", size = 5901554, upload-time = "2025-07-03T13:10:27.018Z" },
{ url = "https://files.pythonhosted.org/packages/74/cf/a9f3a2514a65bb071075063a96f0a5cf949c2f2fce683c15ccc83b1c1cab/pillow-11.3.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f944255db153ebb2b19c51fe85dd99ef0ce494123f21b9db4877ffdfc5590c7c", size = 7669132, upload-time = "2025-07-03T13:10:33.01Z" },
{ url = "https://files.pythonhosted.org/packages/98/3c/da78805cbdbee9cb43efe8261dd7cc0b4b93f2ac79b676c03159e9db2187/pillow-11.3.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1f85acb69adf2aaee8b7da124efebbdb959a104db34d3a2cb0f3793dbae422a8", size = 6005001, upload-time = "2025-07-01T09:15:03.365Z" },
{ url = "https://files.pythonhosted.org/packages/6c/fa/ce044b91faecf30e635321351bba32bab5a7e034c60187fe9698191aef4f/pillow-11.3.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:05f6ecbeff5005399bb48d198f098a9b4b6bdf27b8487c7f38ca16eeb070cd59", size = 6668814, upload-time = "2025-07-01T09:15:05.655Z" },
{ url = "https://files.pythonhosted.org/packages/7b/51/90f9291406d09bf93686434f9183aba27b831c10c87746ff49f127ee80cb/pillow-11.3.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a7bc6e6fd0395bc052f16b1a8670859964dbd7003bd0af2ff08342eb6e442cfe", size = 6113124, upload-time = "2025-07-01T09:15:07.358Z" },
{ url = "https://files.pythonhosted.org/packages/cd/5a/6fec59b1dfb619234f7636d4157d11fb4e196caeee220232a8d2ec48488d/pillow-11.3.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:83e1b0161c9d148125083a35c1c5a89db5b7054834fd4387499e06552035236c", size = 6747186, upload-time = "2025-07-01T09:15:09.317Z" },
{ url = "https://files.pythonhosted.org/packages/49/6b/00187a044f98255225f172de653941e61da37104a9ea60e4f6887717e2b5/pillow-11.3.0-cp313-cp313t-win32.whl", hash = "sha256:2a3117c06b8fb646639dce83694f2f9eac405472713fcb1ae887469c0d4f6788", size = 6277546, upload-time = "2025-07-01T09:15:11.311Z" },
{ url = "https://files.pythonhosted.org/packages/e8/5c/6caaba7e261c0d75bab23be79f1d06b5ad2a2ae49f028ccec801b0e853d6/pillow-11.3.0-cp313-cp313t-win_amd64.whl", hash = "sha256:857844335c95bea93fb39e0fa2726b4d9d758850b34075a7e3ff4f4fa3aa3b31", size = 6985102, upload-time = "2025-07-01T09:15:13.164Z" },
{ url = "https://files.pythonhosted.org/packages/f3/7e/b623008460c09a0cb38263c93b828c666493caee2eb34ff67f778b87e58c/pillow-11.3.0-cp313-cp313t-win_arm64.whl", hash = "sha256:8797edc41f3e8536ae4b10897ee2f637235c94f27404cac7297f7b607dd0716e", size = 2424803, upload-time = "2025-07-01T09:15:15.695Z" },
{ url = "https://files.pythonhosted.org/packages/73/f4/04905af42837292ed86cb1b1dabe03dce1edc008ef14c473c5c7e1443c5d/pillow-11.3.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:d9da3df5f9ea2a89b81bb6087177fb1f4d1c7146d583a3fe5c672c0d94e55e12", size = 5278520, upload-time = "2025-07-01T09:15:17.429Z" },
{ url = "https://files.pythonhosted.org/packages/41/b0/33d79e377a336247df6348a54e6d2a2b85d644ca202555e3faa0cf811ecc/pillow-11.3.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0b275ff9b04df7b640c59ec5a3cb113eefd3795a8df80bac69646ef699c6981a", size = 4686116, upload-time = "2025-07-01T09:15:19.423Z" },
{ url = "https://files.pythonhosted.org/packages/49/2d/ed8bc0ab219ae8768f529597d9509d184fe8a6c4741a6864fea334d25f3f/pillow-11.3.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0743841cabd3dba6a83f38a92672cccbd69af56e3e91777b0ee7f4dba4385632", size = 5864597, upload-time = "2025-07-03T13:10:38.404Z" },
{ url = "https://files.pythonhosted.org/packages/b5/3d/b932bb4225c80b58dfadaca9d42d08d0b7064d2d1791b6a237f87f661834/pillow-11.3.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2465a69cf967b8b49ee1b96d76718cd98c4e925414ead59fdf75cf0fd07df673", size = 7638246, upload-time = "2025-07-03T13:10:44.987Z" },
{ url = "https://files.pythonhosted.org/packages/09/b5/0487044b7c096f1b48f0d7ad416472c02e0e4bf6919541b111efd3cae690/pillow-11.3.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:41742638139424703b4d01665b807c6468e23e699e8e90cffefe291c5832b027", size = 5973336, upload-time = "2025-07-01T09:15:21.237Z" },
{ url = "https://files.pythonhosted.org/packages/a8/2d/524f9318f6cbfcc79fbc004801ea6b607ec3f843977652fdee4857a7568b/pillow-11.3.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:93efb0b4de7e340d99057415c749175e24c8864302369e05914682ba642e5d77", size = 6642699, upload-time = "2025-07-01T09:15:23.186Z" },
{ url = "https://files.pythonhosted.org/packages/6f/d2/a9a4f280c6aefedce1e8f615baaa5474e0701d86dd6f1dede66726462bbd/pillow-11.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7966e38dcd0fa11ca390aed7c6f20454443581d758242023cf36fcb319b1a874", size = 6083789, upload-time = "2025-07-01T09:15:25.1Z" },
{ url = "https://files.pythonhosted.org/packages/fe/54/86b0cd9dbb683a9d5e960b66c7379e821a19be4ac5810e2e5a715c09a0c0/pillow-11.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:98a9afa7b9007c67ed84c57c9e0ad86a6000da96eaa638e4f8abe5b65ff83f0a", size = 6720386, upload-time = "2025-07-01T09:15:27.378Z" },
{ url = "https://files.pythonhosted.org/packages/e7/95/88efcaf384c3588e24259c4203b909cbe3e3c2d887af9e938c2022c9dd48/pillow-11.3.0-cp314-cp314-win32.whl", hash = "sha256:02a723e6bf909e7cea0dac1b0e0310be9d7650cd66222a5f1c571455c0a45214", size = 6370911, upload-time = "2025-07-01T09:15:29.294Z" },
{ url = "https://files.pythonhosted.org/packages/2e/cc/934e5820850ec5eb107e7b1a72dd278140731c669f396110ebc326f2a503/pillow-11.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:a418486160228f64dd9e9efcd132679b7a02a5f22c982c78b6fc7dab3fefb635", size = 7117383, upload-time = "2025-07-01T09:15:31.128Z" },
{ url = "https://files.pythonhosted.org/packages/d6/e9/9c0a616a71da2a5d163aa37405e8aced9a906d574b4a214bede134e731bc/pillow-11.3.0-cp314-cp314-win_arm64.whl", hash = "sha256:155658efb5e044669c08896c0c44231c5e9abcaadbc5cd3648df2f7c0b96b9a6", size = 2511385, upload-time = "2025-07-01T09:15:33.328Z" },
{ url = "https://files.pythonhosted.org/packages/1a/33/c88376898aff369658b225262cd4f2659b13e8178e7534df9e6e1fa289f6/pillow-11.3.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:59a03cdf019efbfeeed910bf79c7c93255c3d54bc45898ac2a4140071b02b4ae", size = 5281129, upload-time = "2025-07-01T09:15:35.194Z" },
{ url = "https://files.pythonhosted.org/packages/1f/70/d376247fb36f1844b42910911c83a02d5544ebd2a8bad9efcc0f707ea774/pillow-11.3.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f8a5827f84d973d8636e9dc5764af4f0cf2318d26744b3d902931701b0d46653", size = 4689580, upload-time = "2025-07-01T09:15:37.114Z" },
{ url = "https://files.pythonhosted.org/packages/eb/1c/537e930496149fbac69efd2fc4329035bbe2e5475b4165439e3be9cb183b/pillow-11.3.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ee92f2fd10f4adc4b43d07ec5e779932b4eb3dbfbc34790ada5a6669bc095aa6", size = 5902860, upload-time = "2025-07-03T13:10:50.248Z" },
{ url = "https://files.pythonhosted.org/packages/bd/57/80f53264954dcefeebcf9dae6e3eb1daea1b488f0be8b8fef12f79a3eb10/pillow-11.3.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c96d333dcf42d01f47b37e0979b6bd73ec91eae18614864622d9b87bbd5bbf36", size = 7670694, upload-time = "2025-07-03T13:10:56.432Z" },
{ url = "https://files.pythonhosted.org/packages/70/ff/4727d3b71a8578b4587d9c276e90efad2d6fe0335fd76742a6da08132e8c/pillow-11.3.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4c96f993ab8c98460cd0c001447bff6194403e8b1d7e149ade5f00594918128b", size = 6005888, upload-time = "2025-07-01T09:15:39.436Z" },
{ url = "https://files.pythonhosted.org/packages/05/ae/716592277934f85d3be51d7256f3636672d7b1abfafdc42cf3f8cbd4b4c8/pillow-11.3.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:41342b64afeba938edb034d122b2dda5db2139b9a4af999729ba8818e0056477", size = 6670330, upload-time = "2025-07-01T09:15:41.269Z" },
{ url = "https://files.pythonhosted.org/packages/e7/bb/7fe6cddcc8827b01b1a9766f5fdeb7418680744f9082035bdbabecf1d57f/pillow-11.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:068d9c39a2d1b358eb9f245ce7ab1b5c3246c7c8c7d9ba58cfa5b43146c06e50", size = 6114089, upload-time = "2025-07-01T09:15:43.13Z" },
{ url = "https://files.pythonhosted.org/packages/8b/f5/06bfaa444c8e80f1a8e4bff98da9c83b37b5be3b1deaa43d27a0db37ef84/pillow-11.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:a1bc6ba083b145187f648b667e05a2534ecc4b9f2784c2cbe3089e44868f2b9b", size = 6748206, upload-time = "2025-07-01T09:15:44.937Z" },
{ url = "https://files.pythonhosted.org/packages/f0/77/bc6f92a3e8e6e46c0ca78abfffec0037845800ea38c73483760362804c41/pillow-11.3.0-cp314-cp314t-win32.whl", hash = "sha256:118ca10c0d60b06d006be10a501fd6bbdfef559251ed31b794668ed569c87e12", size = 6377370, upload-time = "2025-07-01T09:15:46.673Z" },
{ url = "https://files.pythonhosted.org/packages/4a/82/3a721f7d69dca802befb8af08b7c79ebcab461007ce1c18bd91a5d5896f9/pillow-11.3.0-cp314-cp314t-win_amd64.whl", hash = "sha256:8924748b688aa210d79883357d102cd64690e56b923a186f35a82cbc10f997db", size = 7121500, upload-time = "2025-07-01T09:15:48.512Z" },
{ url = "https://files.pythonhosted.org/packages/89/c7/5572fa4a3f45740eaab6ae86fcdf7195b55beac1371ac8c619d880cfe948/pillow-11.3.0-cp314-cp314t-win_arm64.whl", hash = "sha256:79ea0d14d3ebad43ec77ad5272e6ff9bba5b679ef73375ea760261207fa8e0aa", size = 2512835, upload-time = "2025-07-01T09:15:50.399Z" },
]
[[package]]
name = "ruff"
version = "0.12.12"