Compare commits

...

22 Commits

Author SHA1 Message Date
LeoMortari
78e35d65fd Merge branch 'feat' 2025-11-12 11:43:49 -03:00
d737177eab Ajusta 3000k bitrate 2025-08-05 21:23:29 +02:00
6420a02090 revert 2be19ee02c
revert remove bitrate
2025-08-05 21:19:31 +02:00
2be19ee02c remove bitrate 2025-08-05 20:32:07 +02:00
98613a0002 Implementa desacoplamento de I/O 2025-08-05 14:58:44 +02:00
501c45cad7 Ajusta callback 2025-08-05 14:43:12 +02:00
0fd0cda460 Ajusta rabbit 2025-08-05 04:39:03 +02:00
dd4f9fc51c Ajusta rabbitmq 2025-08-05 03:59:08 +02:00
6288d77d46 Ajusta FPS e bitrate de render 2025-08-05 00:02:00 +02:00
Leonardo Mortari
8f5934d576 Add param 2025-08-04 13:17:42 -03:00
Leonardo Mortari
a941eb6b98 Adjusta vars de font e videocodec 2025-08-04 13:08:57 -03:00
Leonardo Mortari
503f2817d2 Merge branch 'master' of https://gitea.leolitas.work.gd/admin/video-render-api 2025-08-04 09:04:55 -03:00
Leonardo Mortari
85b5717595 Adiciona vars faltantes 2025-08-04 09:04:51 -03:00
Leonardo Mortari
9c626a1e4a Altera background de branco para preto, altera cor da letra para branco, cria um auto-resize para formatar os textos com quebras de linhas 2025-08-04 09:03:34 -03:00
ad84469037 Remove parametro de audio false 2025-08-03 23:29:35 +02:00
561be6a182 Adjust queue 2025-08-02 21:45:52 +02:00
Leonardo Mortari
1e15544687 Muda nome do environment 2025-08-02 14:09:28 -03:00
Leonardo Mortari
927eabb2d5 Remove webhook e adiciona push na fila 2025-08-02 14:09:06 -03:00
LeoMortari
1425f852e6 Adjust compose 2025-08-02 12:29:35 -03:00
LeoMortari
95d287bafc Ajusta projeto para consumir uma fila 2025-08-02 12:27:26 -03:00
Leonardo Mortari
5bb58c98e5 Adjusts in project 2025-08-02 01:45:36 -03:00
Leonardo Mortari
55c7ccf316 Init repo 2025-07-31 19:29:14 -03:00
3 changed files with 166 additions and 2 deletions

164
components/video.py Normal file
View File

@@ -0,0 +1,164 @@
import os
from moviepy.video.io.VideoFileClip import VideoFileClip
from moviepy.video.VideoClip import ColorClip
from moviepy.video.compositing.CompositeVideoClip import CompositeVideoClip
from moviepy import TextClip
font = "./Montserrat.ttf"
font_size = 70
video_codec = "libx264"
def auto_wrap_text(text, max_width):
if not text:
return ""
words = text.split()
lines = []
line = ""
for word in words:
test_line = f"{line} {word}".strip()
test_clip = TextClip(text=test_line, font=font, font_size=font_size, color='white', method='label')
if test_clip.w > max_width and line != "":
lines.append(line)
line = word
else:
line = test_line
test_clip.close()
lines.append(line)
return "\n".join(lines)
def cut_video_new_clip(input_path: str, start: float, end: float, output_path: str):
with VideoFileClip(input_path) as clip:
segment = clip.subclipped(start, end)
fps = clip.fps or 30
segment.write_videofile(
output_path,
codec=video_codec,
remove_temp=True,
fps=fps,
bitrate="3000k",
ffmpeg_params=[
"-preset", "ultrafast",
"-tune", "zerolatency",
"-pix_fmt", "yuv420p",
"-profile:v", "high",
"-level", "4.1"
]
)
def process_segment(input_path: str, top_text: str = "", bottom_text: str = "", filename="", idx=1) -> str:
os.makedirs("outputs", exist_ok=True)
os.makedirs(f"outputs/{filename}", exist_ok=True)
final_width, final_height = 1080, 1920
top_h, middle_h, bottom_h = 480, 960, 480
with VideoFileClip(input_path) as clip:
dur = clip.duration
bg = ColorClip(size=(final_width, final_height), color=(0, 0, 0), duration=dur)
video_resized = clip.resized(width=final_width)
y = top_h + (middle_h - video_resized.h) // 2
video_resized = video_resized.with_position((0, y))
wrapped_top_text = auto_wrap_text(top_text, final_width - 40)
wrapped_bottom_text = auto_wrap_text(bottom_text, final_width - 40)
txt_top = TextClip(
text=wrapped_top_text,
font_size=70,
color="white",
font=font,
method="label",
size=(final_width, top_h)
).with_duration(dur).with_position((0, 0))
txt_bot = TextClip(
text=wrapped_bottom_text,
font_size=70,
color="white",
font=font,
method="label",
size=(final_width, bottom_h),
).with_duration(dur).with_position((0, final_height - bottom_h))
final = CompositeVideoClip([bg, video_resized, txt_top, txt_bot], size=(final_width, final_height))
output_path = f"outputs/{filename}/clip_{idx}.mp4"
final.write_videofile(
output_path,
codec=video_codec,
remove_temp=True,
fps=30,
bitrate="3000k",
ffmpeg_params=[
"-preset", "ultrafast",
"-tune", "zerolatency",
"-pix_fmt", "yuv420p",
"-profile:v", "high",
"-level", "4.1"
]
)
final.close()
return output_path
def timestamp_to_seconds(ts):
if isinstance(ts, (int, float)):
return ts
parts = ts.split(":")
parts = [float(p) for p in parts]
if len(parts) == 3:
h, m, s = parts
return h * 3600 + m * 60 + s
elif len(parts) == 2:
m, s = parts
return m * 60 + s
elif len(parts) == 1:
return parts[0]
else:
raise ValueError(f"Timestamp inválido: {ts}")
def process_full_video(filename: str, times: list = None) -> list:
os.makedirs("temp", exist_ok=True)
times = times or []
video_path = f"videos/{filename}"
processed = []
print(f"Total de trechos: {len(times)}")
print(f"Codec de render: {video_codec}")
for idx, interval in enumerate(times, start=1):
start = timestamp_to_seconds(interval.get("start", 0))
end_raw = interval.get("end", None)
end = timestamp_to_seconds(end_raw) if end_raw is not None else None
top_text = interval.get("topText", "")
bottom_text = interval.get("bottomText", "")
if end is None:
with VideoFileClip(video_path) as clip:
end = clip.duration
print(f"Cortando trecho {idx}: {start}s a {end}s")
temp_path = f"temp/{os.path.splitext(filename)[0]}_{idx}.mp4"
cut_video_new_clip(video_path, start, end, temp_path)
out = process_segment(temp_path, top_text, bottom_text, filename, idx)
processed.append(out)
return processed