Refactor imports in ProjectView for clarity and maintainability
This commit is contained in:
110
croppa/project_view.py
Normal file
110
croppa/project_view.py
Normal file
@@ -0,0 +1,110 @@
|
||||
from pathlib import Path
|
||||
from typing import Dict
|
||||
|
||||
import cv2
|
||||
import numpy as np
|
||||
|
||||
|
||||
class ProjectView:
|
||||
"""Project view that displays videos in current directory with progress bars"""
|
||||
|
||||
THUMBNAIL_SIZE = (200, 150)
|
||||
THUMBNAIL_MARGIN = 20
|
||||
PROGRESS_BAR_HEIGHT = 8
|
||||
TEXT_HEIGHT = 30
|
||||
|
||||
BG_COLOR = (40, 40, 40)
|
||||
THUMBNAIL_BG_COLOR = (60, 60, 60)
|
||||
PROGRESS_BG_COLOR = (80, 80, 80)
|
||||
PROGRESS_FILL_COLOR = (0, 120, 255)
|
||||
TEXT_COLOR = (255, 255, 255)
|
||||
SELECTED_COLOR = (255, 165, 0)
|
||||
|
||||
def __init__(self, directory: Path, video_editor):
|
||||
self.directory = directory
|
||||
self.video_editor = video_editor
|
||||
self.video_files = []
|
||||
self.thumbnails: Dict[Path, np.ndarray] = {}
|
||||
self.progress_data = {}
|
||||
self.selected_index = 0
|
||||
self.scroll_offset = 0
|
||||
self.items_per_row = 2
|
||||
self.window_width = 1200
|
||||
self.window_height = 800
|
||||
|
||||
self._load_video_files()
|
||||
self._load_progress_data()
|
||||
|
||||
def _calculate_thumbnail_size(self, window_width: int) -> tuple:
|
||||
available_width = window_width - self.THUMBNAIL_MARGIN
|
||||
item_width = (available_width - (self.items_per_row - 1) * self.THUMBNAIL_MARGIN) // self.items_per_row
|
||||
thumbnail_width = max(50, item_width)
|
||||
thumbnail_height = int(thumbnail_width * self.THUMBNAIL_SIZE[1] / self.THUMBNAIL_SIZE[0])
|
||||
return (thumbnail_width, thumbnail_height)
|
||||
|
||||
def _load_video_files(self):
|
||||
self.video_files = []
|
||||
for file_path in self.directory.iterdir():
|
||||
if (file_path.is_file() and
|
||||
file_path.suffix.lower() in self.video_editor.VIDEO_EXTENSIONS):
|
||||
self.video_files.append(file_path)
|
||||
self.video_files.sort(key=lambda x: x.name)
|
||||
|
||||
def _load_progress_data(self):
|
||||
self.progress_data = {}
|
||||
for video_path in self.video_files:
|
||||
state_file = video_path.with_suffix('.json')
|
||||
if state_file.exists():
|
||||
try:
|
||||
with open(state_file, 'r') as f:
|
||||
import json
|
||||
state = json.load(f)
|
||||
current_frame = state.get('current_frame', 0)
|
||||
cap = cv2.VideoCapture(str(video_path))
|
||||
if cap.isOpened():
|
||||
total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
|
||||
cap.release()
|
||||
if total_frames > 0:
|
||||
progress = current_frame / (total_frames - 1)
|
||||
self.progress_data[video_path] = {
|
||||
'current_frame': current_frame,
|
||||
'total_frames': total_frames,
|
||||
'progress': progress
|
||||
}
|
||||
except Exception as e: # noqa: BLE001 - preserve original behavior
|
||||
print(f"Error loading progress for {video_path.name}: {e}")
|
||||
|
||||
def refresh_progress_data(self):
|
||||
self._load_progress_data()
|
||||
|
||||
def get_progress_for_video(self, video_path: Path) -> float:
|
||||
if video_path in self.progress_data:
|
||||
return self.progress_data[video_path]['progress']
|
||||
return 0.0
|
||||
|
||||
def get_thumbnail_for_video(self, video_path: Path, size: tuple = None) -> np.ndarray:
|
||||
if size is None:
|
||||
size = self.THUMBNAIL_SIZE
|
||||
if video_path in self.thumbnails:
|
||||
original_thumbnail = self.thumbnails[video_path]
|
||||
return cv2.resize(original_thumbnail, size)
|
||||
try:
|
||||
cap = cv2.VideoCapture(str(video_path))
|
||||
if cap.isOpened():
|
||||
total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
|
||||
if total_frames > 0:
|
||||
middle_frame = total_frames // 2
|
||||
cap.set(cv2.CAP_PROP_POS_FRAMES, middle_frame)
|
||||
ret, frame = cap.read()
|
||||
if ret:
|
||||
original_thumbnail = cv2.resize(frame, self.THUMBNAIL_SIZE)
|
||||
self.thumbnails[video_path] = original_thumbnail
|
||||
cap.release()
|
||||
return cv2.resize(original_thumbnail, size)
|
||||
cap.release()
|
||||
except Exception as e: # noqa: BLE001 - preserve original behavior
|
||||
print(f"Error generating thumbnail for {video_path.name}: {e}")
|
||||
placeholder = np.full((size[1], size[0], 3), self.THUMBNAIL_BG_COLOR, dtype=np.uint8)
|
||||
return placeholder
|
||||
|
||||
# draw() and input handling remain in main editor for now to minimize churn
|
Reference in New Issue
Block a user