import ffmpeg import subprocess import json import os from collections import Counter def is_av1(filepath): """Check if a video file is already AV1-encoded.""" try: probe = ffmpeg.probe(filepath) for stream in probe['streams']: if stream['codec_type'] == 'video' and 'codec_name' in stream: if stream['codec_name'] == 'av1': return True except ffmpeg.Error as e: print(f"Error probing {filepath}: {e}") return False def get_fps(filepath): """Get the frames per second (FPS) of the input video using ffmpeg.probe.""" try: probe = ffmpeg.probe(filepath) video_stream = next((s for s in probe['streams'] if s['codec_type'] == 'video'), None) if video_stream and 'r_frame_rate' in video_stream: fps_str = video_stream['r_frame_rate'] # e.g. "30/1", "25/1" num, den = map(int, fps_str.split('/')) return num / den except ffmpeg.Error as e: print(f"Error getting FPS for {filepath}: {e}") return None def get_video_info(filepath): """ Returns dict: { 'width': int, 'height': int, 'bitrate': int, 'fps': float } - bitrate is Kbps (rounded down) - uses stream bit_rate, else format bit_rate, else computed """ cmd = [ "ffprobe","-v","error", "-select_streams","v:0", "-show_entries","stream=width,height,bit_rate,r_frame_rate", "-show_entries","format=bit_rate,duration", "-of","json", filepath ] r = subprocess.run(cmd, capture_output=True, text=True) if r.returncode: return {"width": 0, "height": 0, "bitrate": 0, "fps": 0.0} try: d = json.loads(r.stdout or "{}") s = (d.get("streams") or [{}])[0] f = d.get("format") or {} width = int(s.get("width") or 0) height = int(s.get("height") or 0) # fps (r_frame_rate like "30000/1001") fps = 0.0 rfr = s.get("r_frame_rate") if rfr and rfr != "0/0": try: num, den = rfr.split("/") num = float(num); den = float(den) fps = (num/den) if den else 0.0 except Exception: pass # bitrate in bps → prefer stream, fallback to format, else compute br_bps = s.get("bit_rate") or f.get("bit_rate") if not br_bps: try: dur = float(f.get("duration") or 0) if dur > 0: br_bps = int(os.path.getsize(filepath) * 8 / dur) except Exception: br_bps = 0 br_kbps = int(int(br_bps or 0) / 1000) return {"width": width, "height": height, "bitrate": br_kbps, "fps": fps} except Exception: return {"width": 0, "height": 0, "bitrate": 0, "fps": 0.0} def get_common_resolution(group): """Most common (w,h) across the group's videos. Fallback 1280x720.""" resolutions = [] for v in group: info = get_video_info(v["filepath"]) w, h = info.get("width"), info.get("height") if w and h: resolutions.append((w, h)) if not resolutions: return (1280, 720) return Counter(resolutions).most_common(1)[0][0] def get_target_resolution(group): """ Choose (w,h) whose videos have the highest *total duration*. Tie-breakers: higher count, then larger area. Fallback 1280x720. """ totals = {} # (w,h) -> total duration counts = {} # (w,h) -> number of files for v in group: info = get_video_info(v["filepath"]) w, h = info.get("width"), info.get("height") if not (w and h): continue # Prefer DB duration if present, else probe info['duration'], else 0 dur = v.get("duration", info.get("duration", 0)) try: dur = float(dur) except (TypeError, ValueError): dur = 0.0 key = (w, h) totals[key] = totals.get(key, 0.0) + dur counts[key] = counts.get(key, 0) + 1 if not totals: return (1280, 720) def sort_key(item): (w, h), total = item cnt = counts[(w, h)] area = (w or 0) * (h or 0) return (total, cnt, area) best_resolution = max(totals.items(), key=sort_key)[0] return best_resolution def get_target_bitrate(width, height): """Your existing function to choose a bitrate based on resolution.""" resolutions = {(854, 480): 1000,(1280, 720): 1500,(1920, 1080): 3000,(2560, 1440): 5000,(3840, 2160): 12000} for res, bitrate in resolutions.items(): if width <= res[0] and height <= res[1]: return bitrate return 2500