| """ |
| pacing.py |
| --------------------------------------- |
| Retention & Pacing Optimization Engine (V8) |
| |
| Purpose: |
| - Adjust video pacing for maximum retention |
| - Compress slow segments |
| - Emphasize high-value moments |
| - Create TikTok / Reels optimized flow |
| |
| Works in CPU-only environments (FFmpeg-based). |
| """ |
|
|
| import subprocess |
| import os |
|
|
|
|
| |
| |
| |
|
|
| OUTPUT_FILE = "pacing_optimized.mp4" |
|
|
| SLOW_THRESHOLD = 1.25 |
| FAST_THRESHOLD = 1.75 |
|
|
|
|
| |
| |
| |
|
|
| def estimate_segment_value(text): |
| """ |
| Heuristic scoring system: |
| determines importance of spoken segment. |
| """ |
|
|
| text = text.lower() |
|
|
| high_value_keywords = [ |
| "you", "secret", "important", "stop", |
| "crazy", "insane", "listen", "this", |
| "money", "success", "life", "truth" |
| ] |
|
|
| filler_keywords = [ |
| "um", "uh", "like", "you know", "so", |
| "actually", "basically" |
| ] |
|
|
| score = 1.0 |
|
|
| |
| for w in high_value_keywords: |
| if w in text: |
| score += 0.6 |
|
|
| |
| for w in filler_keywords: |
| if w in text: |
| score -= 0.4 |
|
|
| return max(0.5, min(score, 2.0)) |
|
|
|
|
| |
| |
| |
|
|
| def build_speed_map(words): |
| """ |
| Converts transcript into pacing instructions |
| """ |
|
|
| segments = [] |
| buffer = [] |
|
|
| for w in words: |
| buffer.append(w) |
|
|
| |
| if len(buffer) >= 6: |
| segments.append(buffer) |
| buffer = [] |
|
|
| if buffer: |
| segments.append(buffer) |
|
|
| speed_map = [] |
|
|
| for seg in segments: |
|
|
| text = " ".join([w["word"] for w in seg]) |
| score = estimate_segment_value(text) |
|
|
| start = seg[0]["start"] |
| end = seg[-1]["end"] |
|
|
| |
| if score > 1.4: |
| speed = 1.0 |
| elif score > 1.0: |
| speed = 1.15 |
| else: |
| speed = FAST_THRESHOLD |
|
|
| speed_map.append({ |
| "start": start, |
| "end": end, |
| "speed": speed |
| }) |
|
|
| return speed_map |
|
|
|
|
| |
| |
| |
|
|
| def build_filter(speed_map): |
| """ |
| Creates FFmpeg atempo + setpts filter chain |
| """ |
|
|
| filters = [] |
|
|
| for i, seg in enumerate(speed_map): |
|
|
| start = seg["start"] |
| end = seg["end"] |
| speed = seg["speed"] |
|
|
| |
| filters.append( |
| f"[0:v]trim=start={start}:end={end},setpts=PTS/{speed}[v{i}]" |
| ) |
|
|
| |
| filters.append( |
| f"[0:a]atrim=start={start}:end={end},asetpts=PTS-STARTPTS," |
| f"atempo={speed}[a{i}]" |
| ) |
|
|
| v_streams = "".join([f"[v{i}]" for i in range(len(speed_map))]) |
| a_streams = "".join([f"[a{i}]" for i in range(len(speed_map))]) |
|
|
| filters.append( |
| f"{v_streams}{a_streams}concat=n={len(speed_map)}:v=1:a=1[outv][outa]" |
| ) |
|
|
| return ";".join(filters) |
|
|
|
|
| |
| |
| |
|
|
| def optimize_pacing(video_path, words=None): |
| """ |
| Main entry point for V8 pacing system |
| """ |
|
|
| print("[PACING] Starting optimization...") |
|
|
| if not words: |
| print("[PACING] No transcript provided — returning original video") |
| return video_path |
|
|
| |
| speed_map = build_speed_map(words) |
|
|
| print(f"[PACING] Segments: {len(speed_map)}") |
|
|
| |
| filter_complex = build_filter(speed_map) |
|
|
| output_path = OUTPUT_FILE |
|
|
| |
| cmd = [ |
| "ffmpeg", "-y", |
| "-i", video_path, |
| "-filter_complex", filter_complex, |
| "-map", "[outv]", |
| "-map", "[outa]", |
| "-c:v", "libx264", |
| "-preset", "ultrafast", |
| "-c:a", "aac", |
| output_path |
| ] |
|
|
| subprocess.run(cmd, check=True) |
|
|
| print("[PACING] Done:", output_path) |
|
|
| return output_path |
|
|
|
|
| |
| |
| |
|
|
| def fast_pacing(video_path): |
| """ |
| Simple fallback: global speed-up only |
| """ |
|
|
| output = "fast_pacing.mp4" |
|
|
| cmd = [ |
| "ffmpeg", "-y", |
| "-i", video_path, |
| "-filter_complex", |
| "[0:v]setpts=0.92*PTS[v];[0:a]atempo=1.08[a]", |
| "-map", "[v]", |
| "-map", "[a]", |
| "-c:v", "libx264", |
| "-preset", "ultrafast", |
| "-c:a", "aac", |
| output |
| ] |
|
|
| subprocess.run(cmd, check=True) |
|
|
| return output |
|
|
|
|
| |
| |
| |
|
|
| def pacing_engine(video_path, words=None, mode="smart"): |
| """ |
| Entry point used by main.py |
| """ |
|
|
| if mode == "fast": |
| return fast_pacing(video_path) |
|
|
| return optimize_pacing(video_path, words) |