basyx commited on
Commit
2c5ac2e
·
verified ·
1 Parent(s): a5e7c90

Create pacing.py

Browse files
Files changed (1) hide show
  1. utils/pacing.py +241 -0
utils/pacing.py ADDED
@@ -0,0 +1,241 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ pacing.py
3
+ ---------------------------------------
4
+ Retention & Pacing Optimization Engine (V8)
5
+
6
+ Purpose:
7
+ - Adjust video pacing for maximum retention
8
+ - Compress slow segments
9
+ - Emphasize high-value moments
10
+ - Create TikTok / Reels optimized flow
11
+
12
+ Works in CPU-only environments (FFmpeg-based).
13
+ """
14
+
15
+ import subprocess
16
+ import os
17
+
18
+
19
+ # =====================================================
20
+ # CONFIG
21
+ # =====================================================
22
+
23
+ OUTPUT_FILE = "pacing_optimized.mp4"
24
+
25
+ SLOW_THRESHOLD = 1.25 # speed multiplier for slow segments
26
+ FAST_THRESHOLD = 1.75 # speed multiplier for filler segments
27
+
28
+
29
+ # =====================================================
30
+ # BASIC SEGMENT ESTIMATION (NO ML DEPENDENCY)
31
+ # =====================================================
32
+
33
+ def estimate_segment_value(text):
34
+ """
35
+ Heuristic scoring system:
36
+ determines importance of spoken segment.
37
+ """
38
+
39
+ text = text.lower()
40
+
41
+ high_value_keywords = [
42
+ "you", "secret", "important", "stop",
43
+ "crazy", "insane", "listen", "this",
44
+ "money", "success", "life", "truth"
45
+ ]
46
+
47
+ filler_keywords = [
48
+ "um", "uh", "like", "you know", "so",
49
+ "actually", "basically"
50
+ ]
51
+
52
+ score = 1.0
53
+
54
+ # boost high value words
55
+ for w in high_value_keywords:
56
+ if w in text:
57
+ score += 0.6
58
+
59
+ # penalize filler speech
60
+ for w in filler_keywords:
61
+ if w in text:
62
+ score -= 0.4
63
+
64
+ return max(0.5, min(score, 2.0))
65
+
66
+
67
+ # =====================================================
68
+ # SPEED MAP GENERATOR
69
+ # =====================================================
70
+
71
+ def build_speed_map(words):
72
+ """
73
+ Converts transcript into pacing instructions
74
+ """
75
+
76
+ segments = []
77
+ buffer = []
78
+
79
+ for w in words:
80
+ buffer.append(w)
81
+
82
+ # group into micro segments
83
+ if len(buffer) >= 6:
84
+ segments.append(buffer)
85
+ buffer = []
86
+
87
+ if buffer:
88
+ segments.append(buffer)
89
+
90
+ speed_map = []
91
+
92
+ for seg in segments:
93
+
94
+ text = " ".join([w["word"] for w in seg])
95
+ score = estimate_segment_value(text)
96
+
97
+ start = seg[0]["start"]
98
+ end = seg[-1]["end"]
99
+
100
+ # decide speed
101
+ if score > 1.4:
102
+ speed = 1.0 # keep normal (important content)
103
+ elif score > 1.0:
104
+ speed = 1.15 # slight compression
105
+ else:
106
+ speed = FAST_THRESHOLD # aggressive speed-up
107
+
108
+ speed_map.append({
109
+ "start": start,
110
+ "end": end,
111
+ "speed": speed
112
+ })
113
+
114
+ return speed_map
115
+
116
+
117
+ # =====================================================
118
+ # FFMEG FILTER BUILDER
119
+ # =====================================================
120
+
121
+ def build_filter(speed_map):
122
+ """
123
+ Creates FFmpeg atempo + setpts filter chain
124
+ """
125
+
126
+ filters = []
127
+
128
+ for i, seg in enumerate(speed_map):
129
+
130
+ start = seg["start"]
131
+ end = seg["end"]
132
+ speed = seg["speed"]
133
+
134
+ # video speed
135
+ filters.append(
136
+ f"[0:v]trim=start={start}:end={end},setpts=PTS/{speed}[v{i}]"
137
+ )
138
+
139
+ # audio speed
140
+ filters.append(
141
+ f"[0:a]atrim=start={start}:end={end},asetpts=PTS-STARTPTS,"
142
+ f"atempo={speed}[a{i}]"
143
+ )
144
+
145
+ v_streams = "".join([f"[v{i}]" for i in range(len(speed_map))])
146
+ a_streams = "".join([f"[a{i}]" for i in range(len(speed_map))])
147
+
148
+ filters.append(
149
+ f"{v_streams}{a_streams}concat=n={len(speed_map)}:v=1:a=1[outv][outa]"
150
+ )
151
+
152
+ return ";".join(filters)
153
+
154
+
155
+ # =====================================================
156
+ # MAIN ENGINE
157
+ # =====================================================
158
+
159
+ def optimize_pacing(video_path, words=None):
160
+ """
161
+ Main entry point for V8 pacing system
162
+ """
163
+
164
+ print("[PACING] Starting optimization...")
165
+
166
+ if not words:
167
+ print("[PACING] No transcript provided — returning original video")
168
+ return video_path
169
+
170
+ # Step 1: build speed map
171
+ speed_map = build_speed_map(words)
172
+
173
+ print(f"[PACING] Segments: {len(speed_map)}")
174
+
175
+ # Step 2: build ffmpeg filter
176
+ filter_complex = build_filter(speed_map)
177
+
178
+ output_path = OUTPUT_FILE
179
+
180
+ # Step 3: render optimized video
181
+ cmd = [
182
+ "ffmpeg", "-y",
183
+ "-i", video_path,
184
+ "-filter_complex", filter_complex,
185
+ "-map", "[outv]",
186
+ "-map", "[outa]",
187
+ "-c:v", "libx264",
188
+ "-preset", "ultrafast",
189
+ "-c:a", "aac",
190
+ output_path
191
+ ]
192
+
193
+ subprocess.run(cmd, check=True)
194
+
195
+ print("[PACING] Done:", output_path)
196
+
197
+ return output_path
198
+
199
+
200
+ # =====================================================
201
+ # LIGHTWEIGHT MODE (FAST FALLBACK)
202
+ # =====================================================
203
+
204
+ def fast_pacing(video_path):
205
+ """
206
+ Simple fallback: global speed-up only
207
+ """
208
+
209
+ output = "fast_pacing.mp4"
210
+
211
+ cmd = [
212
+ "ffmpeg", "-y",
213
+ "-i", video_path,
214
+ "-filter_complex",
215
+ "[0:v]setpts=0.92*PTS[v];[0:a]atempo=1.08[a]",
216
+ "-map", "[v]",
217
+ "-map", "[a]",
218
+ "-c:v", "libx264",
219
+ "-preset", "ultrafast",
220
+ "-c:a", "aac",
221
+ output
222
+ ]
223
+
224
+ subprocess.run(cmd, check=True)
225
+
226
+ return output
227
+
228
+
229
+ # =====================================================
230
+ # PUBLIC API
231
+ # =====================================================
232
+
233
+ def pacing_engine(video_path, words=None, mode="smart"):
234
+ """
235
+ Entry point used by main.py
236
+ """
237
+
238
+ if mode == "fast":
239
+ return fast_pacing(video_path)
240
+
241
+ return optimize_pacing(video_path, words)