import io import urllib.request import cv2 import numpy as np from PIL import Image from config import ( MAX_DIM, MIN_DIM, SQUARE_DIM, MULTIPLE_OF, FIXED_FPS, MIN_FRAMES_MODEL, MAX_FRAMES_MODEL ) def load_image_from_url(url: str) -> Image.Image: if not url or not str(url).strip(): raise ValueError("Masukkan URL gambar terlebih dahulu.") url = str(url).strip() headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"} req = urllib.request.Request(url, headers=headers) with urllib.request.urlopen(req, timeout=15) as resp: img_bytes = resp.read() img = Image.open(io.BytesIO(img_bytes)) return img.convert("RGB") get_timestamp_js = """ function(video, timestamp) { const videoElem = document.querySelector('#generated-video video'); let currentTime = 0; if (videoElem) { currentTime = videoElem.currentTime; console.log("Video found! Time: " + currentTime); } else { console.log("No video element found."); } return [video, currentTime]; } """ def extract_frame(video_path, timestamp): if not video_path: return None, 0 print(f"Extracting frame at timestamp: {timestamp}") cap = cv2.VideoCapture(video_path) if not cap.isOpened(): return None, timestamp fps = cap.get(cv2.CAP_PROP_FPS) if fps <= 0: fps = 16.0 target_frame_num = int(float(timestamp) * fps) total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT)) if total_frames > 0 and target_frame_num >= total_frames: target_frame_num = total_frames - 1 cap.set(cv2.CAP_PROP_POS_FRAMES, target_frame_num) ret, frame = cap.read() cap.release() if ret: return cv2.cvtColor(frame, cv2.COLOR_BGR2RGB), timestamp return None, timestamp def resize_image(image: Image.Image) -> Image.Image: width, height = image.size if width == height: return image.resize((SQUARE_DIM, SQUARE_DIM), Image.LANCZOS) aspect_ratio = width / height MAX_ASPECT_RATIO = MAX_DIM / MIN_DIM MIN_ASPECT_RATIO = MIN_DIM / MAX_DIM image_to_resize = image if aspect_ratio > MAX_ASPECT_RATIO: target_w, target_h = MAX_DIM, MIN_DIM crop_width = int(round(height * MAX_ASPECT_RATIO)) left = (width - crop_width) // 2 image_to_resize = image.crop((left, 0, left + crop_width, height)) elif aspect_ratio < MIN_ASPECT_RATIO: target_w, target_h = MIN_DIM, MAX_DIM crop_height = int(round(width / MIN_ASPECT_RATIO)) top = (height - crop_height) // 2 image_to_resize = image.crop((0, top, width, top + crop_height)) else: if width > height: target_w = MAX_DIM target_h = int(round(target_w / aspect_ratio)) else: target_h = MAX_DIM target_w = int(round(target_h * aspect_ratio)) final_w = round(target_w / MULTIPLE_OF) * MULTIPLE_OF final_h = round(target_h / MULTIPLE_OF) * MULTIPLE_OF final_w = max(MIN_DIM, min(MAX_DIM, final_w)) final_h = max(MIN_DIM, min(MAX_DIM, final_h)) return image_to_resize.resize((final_w, final_h), Image.LANCZOS) def resize_and_crop_to_match(target_image, reference_image): ref_width, ref_height = reference_image.size target_width, target_height = target_image.size scale = max(ref_width / target_width, ref_height / target_height) new_width, new_height = int(target_width * scale), int(target_height * scale) resized = target_image.resize((new_width, new_height), Image.Resampling.LANCZOS) left, top = (new_width - ref_width) // 2, (new_height - ref_height) // 2 return resized.crop((left, top, left + ref_width, top + ref_height)) def get_num_frames(duration_seconds: float): raw = int(round(duration_seconds * FIXED_FPS)) raw = max(MIN_FRAMES_MODEL, min(MAX_FRAMES_MODEL, raw)) return ((raw - 1) // 4) * 4 + 1