qur / app.py
digidau's picture
Update app.py
e61e890 verified
Raw
History Blame Contribute Delete
9.46 kB
import os
import json
import shutil
import gradio as gr
import subprocess
import cv2
import tempfile
from fonts import setup_fonts
from converter import convert_qurancaption_to_qvm
from subtitle import generate_ass
from background import generate_background
from background_video import prepare_background_video
from renderer import render_final_video
from intro import prepend_intro
# --------------------------------------------------
# Startup
# --------------------------------------------------
setup_fonts()
for f in os.listdir("fonts"):
if f.endswith(".ttf") or f.endswith(".otf"):
path = os.path.join(
"fonts",
f
)
print("\n" + "=" * 60)
print("FONT INFO:", f)
print("=" * 60)
try:
result = subprocess.run(
["fc-scan", path],
capture_output=True,
text=True
)
print(result.stdout[:5000])
except Exception as e:
print(e)
os.makedirs("uploads", exist_ok=True)
os.makedirs("temp", exist_ok=True)
os.makedirs("outputs", exist_ok=True)
# --------------------------------------------------
# Fonts
# --------------------------------------------------
def get_available_fonts():
fonts = []
if os.path.exists("fonts"):
for f in os.listdir("fonts"):
if (
f.endswith(".ttf")
or f.endswith(".otf")
):
fonts.append(
f.replace(".ttf", "")
.replace(".otf", "")
)
if not fonts:
fonts = [
"Amiri-Regular"
]
return sorted(fonts)
# --------------------------------------------------
# Main Process
# --------------------------------------------------
def preview_layout(
images,
background_video,
timing_json,
arabic_font,
arabic_y,
translation_gap,
arabic_font_size,
translation_font_size
):
os.makedirs("temp", exist_ok=True)
# =====================================
# BACKGROUND
# =====================================
if images:
background_image = images[0].name
elif background_video:
background_image = "temp/preview_frame.jpg"
subprocess.run(
[
"ffmpeg",
"-y",
"-i",
background_video,
"-vframes",
"1",
background_image
],
check=True
)
else:
raise Exception(
"Upload Images atau Background Video"
)
# =====================================
# JSON -> QVM
# =====================================
qvm_json = "temp/preview_qvm.json"
convert_qurancaption_to_qvm(
timing_json.name,
qvm_json
)
with open(
qvm_json,
"r",
encoding="utf-8"
) as f:
data = json.load(f)
data[0]["start_time"] = 0
data[0]["end_time"] = 999
# =====================================
# ASS
# =====================================
preview_ass = "temp/preview.ass"
generate_ass(
qvm_json,
preview_ass,
arabic_font,
arabic_y,
translation_gap,
arabic_font_size,
translation_font_size
)
print("=" * 60)
print("PREVIEW ASS")
print("=" * 60)
print(
"ASS EXISTS:",
os.path.exists(preview_ass)
)
if os.path.exists(preview_ass):
print(
"ASS SIZE:",
os.path.getsize(preview_ass)
)
with open(
preview_ass,
"r",
encoding="utf-8"
) as f:
print(f.read())
# =====================================
# RENDER 1 FRAME
# =====================================
preview_output = "temp/preview.jpg"
subprocess.run(
[
"ffmpeg",
"-y",
"-loop", "1",
"-i", background_image,
"-vf",
f"ass={preview_ass}:fontsdir=fonts",
"-ss",
"1",
"-frames:v",
"1",
"-update",
"1",
preview_output
],
check=True
)
return preview_output
def process(
images,
background_video,
audio,
timing_json,
arabic_font,
intro_video,
arabic_y,
translation_gap,
arabic_font_size,
translation_font_size
):
print("=" * 60)
print("SELECTED UI FONT")
print("=" * 60)
print(arabic_font)
# ==========================================
# VALIDATION
# ==========================================
if not images and not background_video:
raise Exception(
"Upload Images atau Background Video"
)
for folder in [
"uploads",
"temp"
]:
if os.path.exists(folder):
shutil.rmtree(folder)
os.makedirs(folder)
image_paths = []
if images:
for i, img in enumerate(images):
ext = os.path.splitext(
img.name
)[1]
dst = (
f"uploads/{i:03d}{ext}"
)
shutil.copy(
img.name,
dst
)
image_paths.append(
dst
)
audio_path = (
"uploads/audio.mp3"
)
shutil.copy(
audio,
audio_path
)
json_path = (
"uploads/timing.json"
)
shutil.copy(
timing_json.name,
json_path
)
qvm_json = (
"temp/qvm_timing.json"
)
convert_qurancaption_to_qvm(
json_path,
qvm_json
)
with open(
qvm_json,
"r",
encoding="utf-8"
) as f:
preview = json.load(f)
print(
"TOTAL SUBTITLES:",
len(preview)
)
if preview:
print(
"FIRST:",
preview[0]
)
background_path = (
"temp/background.mp4"
)
if background_video:
prepare_background_video(
background_video,
background_path
)
else:
generate_background(
image_paths,
background_path
)
subtitle_path = (
"temp/subtitles.ass"
)
generate_ass(
qvm_json,
subtitle_path,
arabic_font,
arabic_y,
translation_gap,
arabic_font_size,
translation_font_size
)
main_video = (
"outputs/main.mp4"
)
render_final_video(
background_path,
audio_path,
subtitle_path,
main_video
)
if intro_video:
final_video = "outputs/final.mp4"
prepend_intro(
intro_video.name,
main_video,
final_video
)
return final_video
return main_video
# --------------------------------------------------
# UI
# --------------------------------------------------
with gr.Blocks() as demo:
gr.Markdown(
"""
# Quran Cinematic Video Generator
Upload:
- Images
- Audio MP3
- QuranCaption JSON
Generate cinematic Quran video automatically.
"""
)
intro_video = gr.File(
label="Intro Video (Optional)"
)
images = gr.Files(
label="Upload Images",
file_count="multiple"
)
background_video = gr.Video(
label="Background Video (Optional)"
)
audio = gr.Audio(
type="filepath",
label="Upload Audio MP3"
)
timing_json = gr.File(
label="Upload QuranCaption JSON"
)
arabic_font = gr.Dropdown(
choices=get_available_fonts(),
value=get_available_fonts()[0],
label="Arabic Font"
)
arabic_y = gr.Slider(
minimum=200,
maximum=1200,
value=380,
step=10,
label="Arabic Position Y"
)
translation_gap = gr.Slider(
minimum=0,
maximum=120,
value=25,
step=5,
label="Translation Gap"
)
arabic_font_size = gr.Slider(
80,
180,
value=120,
step=2,
label="Arabic Font Size"
)
translation_font_size = gr.Slider(
20,
60,
value=34,
step=1,
label="Translation Font Size"
)
preview_btn = gr.Button(
"Preview Layout"
)
preview_image = gr.Image(
label="Preview"
)
generate_btn = gr.Button(
"Generate Video",
variant="primary"
)
output_video = gr.Video(
label="Result"
)
preview_btn.click(
fn=preview_layout,
inputs=[
images,
background_video,
timing_json,
arabic_font,
arabic_y,
translation_gap,
arabic_font_size,
translation_font_size
],
outputs=preview_image
)
generate_btn.click(
fn=process,
inputs=[
images,
background_video,
audio,
timing_json,
arabic_font,
intro_video,
arabic_y,
translation_gap,
arabic_font_size,
translation_font_size
],
outputs=output_video
)
demo.launch()