Update app.py
Browse files
app.py
CHANGED
|
@@ -167,40 +167,54 @@ def ken_burns(pil, duration_sec=6, fps=30, style="premium"):
|
|
| 167 |
# CAPTIONS β burn into existing video via ffmpeg
|
| 168 |
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 169 |
def add_captions_ffmpeg(video_path, caption, duration_sec, style):
|
| 170 |
-
"""Burn animated captions using ffmpeg drawtext."""
|
|
|
|
|
|
|
|
|
|
| 171 |
words=caption.strip().split()
|
| 172 |
mid=max(1,len(words)//2)
|
| 173 |
-
line1=" ".join(words[:mid])
|
| 174 |
-
line2=" ".join(words[mid:]) if len(words)>1 else line1
|
| 175 |
|
| 176 |
colors={"premium":"FFD232","energetic":"3CC8FF","fun":"FF78C8"}
|
| 177 |
col=colors.get(style,"FFFFFF")
|
| 178 |
-
|
| 179 |
-
# ffmpeg drawtext with fade-in animation
|
| 180 |
-
# line1: shows 1.0s β 3.5s, line2: 3.8s β 6.5s
|
| 181 |
out=video_path.replace(".mp4","_cap.mp4")
|
|
|
|
| 182 |
font_paths=[
|
| 183 |
"/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf",
|
| 184 |
"/usr/share/fonts/truetype/liberation/LiberationSans-Bold.ttf",
|
| 185 |
"/usr/share/fonts/truetype/freefont/FreeSansBold.ttf",
|
| 186 |
]
|
| 187 |
-
font=""
|
| 188 |
for p in font_paths:
|
| 189 |
-
if os.path.exists(p): font=f":fontfile='{p}'"; break
|
| 190 |
|
| 191 |
-
def
|
| 192 |
-
|
|
|
|
| 193 |
return (
|
| 194 |
f"drawtext=text='{text}'{font}"
|
| 195 |
-
f":fontsize=
|
| 196 |
-
f":x=(w-text_w)/2:y=
|
| 197 |
-
f":box=1:boxcolor=black@
|
| 198 |
f":enable='between(t,{start},{end})'"
|
| 199 |
-
f":alpha='if(lt(t,{start+
|
| 200 |
-
f"if(gt(t,{end-fade_dur}),({end}-t)/{fade_dur},1))'"
|
| 201 |
)
|
| 202 |
|
| 203 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 204 |
|
| 205 |
ret=os.system(f'ffmpeg -y -i "{video_path}" -vf "{vf}" -c:a copy "{out}" -loglevel error')
|
| 206 |
return out if (ret==0 and os.path.exists(out)) else video_path
|
|
|
|
| 167 |
# CAPTIONS β burn into existing video via ffmpeg
|
| 168 |
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 169 |
def add_captions_ffmpeg(video_path, caption, duration_sec, style):
|
| 170 |
+
"""Burn animated captions + hashtag tag + shop-now CTA using ffmpeg drawtext."""
|
| 171 |
+
import re
|
| 172 |
+
def clean(t): return re.sub(r"[^A-Za-z0-9 !.,-]","",t).strip()
|
| 173 |
+
|
| 174 |
words=caption.strip().split()
|
| 175 |
mid=max(1,len(words)//2)
|
| 176 |
+
line1=clean(" ".join(words[:mid]))
|
| 177 |
+
line2=clean(" ".join(words[mid:])) if len(words)>1 else line1
|
| 178 |
|
| 179 |
colors={"premium":"FFD232","energetic":"3CC8FF","fun":"FF78C8"}
|
| 180 |
col=colors.get(style,"FFFFFF")
|
|
|
|
|
|
|
|
|
|
| 181 |
out=video_path.replace(".mp4","_cap.mp4")
|
| 182 |
+
|
| 183 |
font_paths=[
|
| 184 |
"/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf",
|
| 185 |
"/usr/share/fonts/truetype/liberation/LiberationSans-Bold.ttf",
|
| 186 |
"/usr/share/fonts/truetype/freefont/FreeSansBold.ttf",
|
| 187 |
]
|
| 188 |
+
font=""; font_reg=""
|
| 189 |
for p in font_paths:
|
| 190 |
+
if os.path.exists(p): font=f":fontfile='{p}'"; font_reg=font; break
|
| 191 |
|
| 192 |
+
def dt(text, start, end, y, size=42, color=None, box_alpha="0.60"):
|
| 193 |
+
c = color or col
|
| 194 |
+
fd=0.4
|
| 195 |
return (
|
| 196 |
f"drawtext=text='{text}'{font}"
|
| 197 |
+
f":fontsize={size}:fontcolor=#{c}"
|
| 198 |
+
f":x=(w-text_w)/2:y={y}"
|
| 199 |
+
f":box=1:boxcolor=black@{box_alpha}:boxborderw=14"
|
| 200 |
f":enable='between(t,{start},{end})'"
|
| 201 |
+
f":alpha='if(lt(t,{start+fd}),(t-{start})/{fd},if(gt(t,{end-fd}),({end}-t)/{fd},1))'"
|
|
|
|
| 202 |
)
|
| 203 |
|
| 204 |
+
end2 = min(duration_sec-0.2, 6.5)
|
| 205 |
+
|
| 206 |
+
# 1. Main captions β inside frame, above bars
|
| 207 |
+
cap1 = dt(line1, 1.0, 3.5, "h-190")
|
| 208 |
+
cap2 = dt(line2, 3.8, end2, "h-190")
|
| 209 |
+
|
| 210 |
+
# 2. "Shop Now" CTA β appears at 4.5s, small, bottom center
|
| 211 |
+
cta_colors={"premium":"FF9900","energetic":"FF4444","fun":"AA44FF"}
|
| 212 |
+
cta = dt("Shop Now >", 4.5, end2, "h-130", size=32, color=cta_colors.get(style,"FF9900"), box_alpha="0.70")
|
| 213 |
+
|
| 214 |
+
# 3. Hashtag top-left β appears early
|
| 215 |
+
tag = dt("#NewCollection", 0.5, 3.0, "60", size=28, color="FFFFFF", box_alpha="0.40")
|
| 216 |
+
|
| 217 |
+
vf = ",".join([cap1, cap2, cta, tag])
|
| 218 |
|
| 219 |
ret=os.system(f'ffmpeg -y -i "{video_path}" -vf "{vf}" -c:a copy "{out}" -loglevel error')
|
| 220 |
return out if (ret==0 and os.path.exists(out)) else video_path
|