Clipping / routers /presets.py
aliSaac510's picture
fix defaults
8c64cc3
from fastapi import APIRouter, HTTPException, File, UploadFile, Form
import os
import json
import shutil
from schemas import SubtitlePreset
from typing import List, Optional
router = APIRouter()
# إعداد المسارات لتكون دائماً داخل مجلد Clipping
ROUTER_DIR = os.path.dirname(os.path.abspath(__file__))
CLIPPING_DIR = os.path.dirname(ROUTER_DIR)
PRESETS_DIR = os.path.join(CLIPPING_DIR, "temp_videos", "presets")
FONTS_DIR = os.path.join(CLIPPING_DIR, "temp_videos", "fonts")
os.makedirs(PRESETS_DIR, exist_ok=True)
os.makedirs(FONTS_DIR, exist_ok=True)
@router.post("/save", summary="Create or Update a Custom Preset")
async def save_preset(
name: str = Form(..., description="Unique name for the preset"),
font_name: str = Form("Arial", description="Font family name"),
font_size: int = Form(48, description="Font size in pixels"),
primary_color: str = Form("#FFFFFF", description="Text color (Hex)"),
secondary_color: str = Form("#FFFF00", description="Highlight color (Hex)"),
outline_color: str = Form("#000000", description="Outline color (Hex)"),
back_color: str = Form("#000000", description="Background box color (Hex)"),
outline_width: float = Form(2.0),
shadow_depth: float = Form(1.0),
alignment: int = Form(2, description="2=Bottom Center, 5=Middle Center, 8=Top Center"),
margin_v: int = Form(100, description="Vertical margin from edge"),
pop_up_scale: float = Form(1.2, description="Animation scale factor"),
highlight_mode: str = Form("karaoke", description="karaoke or instant"),
back_box_enabled: bool = Form(True),
display_mode: str = Form("word", description="word or sentence"),
max_words_per_line: int = Form(3, description="Max words per line in sentence mode"),
uppercase: bool = Form(False, description="Convert English text to ALL CAPS"),
letter_spacing: float = Form(0.0, description="Spacing between characters"),
line_spacing: int = Form(0, description="Vertical spacing between lines"),
background_opacity: float = Form(1.0, description="Opacity of background box (0.0 to 1.0)"),
glow_intensity: int = Form(0, description="Intensity of glow effect (0 to 10)"),
rotation_angle: float = Form(0.0, description="Rotation angle in degrees"),
margin_h: int = Form(20, description="Horizontal margin"),
custom_font_file: Optional[UploadFile] = File(None, description="Optional: Upload a .ttf or .otf font file")
):
"""
يتيح هذا الـ Endpoint إنشاء تصميم كامل (Preset).
يمكنك رفع خط خاص (Custom Font) وسيتم حفظه واستخدامه تلقائياً.
"""
try:
# 1. معالجة الخط الخاص إذا تم رفعه
final_font_name = font_name
if custom_font_file:
font_ext = os.path.splitext(custom_font_file.filename)[1]
if font_ext.lower() not in ['.ttf', '.otf']:
raise HTTPException(status_code=400, detail="Only .ttf and .otf fonts are supported")
font_path = os.path.join(FONTS_DIR, custom_font_file.filename)
with open(font_path, "wb") as buffer:
shutil.copyfileobj(custom_font_file.file, buffer)
# نستخدم اسم الملف المرفوع كاسم للخط
final_font_name = os.path.splitext(custom_font_file.filename)[0]
# 2. تجهيز بيانات الـ Preset
preset_dict = {
"name": name,
"font_name": final_font_name,
"font_size": font_size,
"primary_color": primary_color,
"secondary_color": secondary_color,
"outline_color": outline_color,
"back_color": back_color,
"outline_width": outline_width,
"shadow_depth": shadow_depth,
"alignment": alignment,
"margin_v": margin_v,
"pop_up_scale": pop_up_scale,
"highlight_mode": highlight_mode,
"back_box_enabled": back_box_enabled,
"display_mode": display_mode,
"max_words_per_line": max_words_per_line,
"uppercase": uppercase,
"letter_spacing": letter_spacing,
"line_spacing": line_spacing,
"background_opacity": background_opacity,
"glow_intensity": glow_intensity,
"rotation_angle": rotation_angle,
"margin_h": margin_h
}
# 3. حفظ ملف الـ JSON
file_path = os.path.join(PRESETS_DIR, f"{name}.json")
with open(file_path, "w", encoding="utf-8") as f:
json.dump(preset_dict, f, indent=4)
return {
"status": "success",
"message": f"Preset '{name}' saved successfully.",
"data": preset_dict
}
except Exception as e:
raise HTTPException(status_code=500, detail=f"Failed to save preset: {str(e)}")
@router.get("/list", summary="List all saved presets")
async def list_presets():
if not os.path.exists(PRESETS_DIR):
return []
return [f.replace(".json", "") for f in os.listdir(PRESETS_DIR) if f.endswith(".json")]
@router.delete("/delete/{name}", summary="Delete a preset")
async def delete_preset(name: str):
file_path = os.path.join(PRESETS_DIR, f"{name}.json")
if os.path.exists(file_path):
os.remove(file_path)
return {"status": "success", "message": f"Preset '{name}' deleted."}
raise HTTPException(status_code=404, detail="Preset not found")
def load_preset_by_name(name: str) -> Optional[SubtitlePreset]:
"""Helper function to load a preset for processing"""
file_path = os.path.join(PRESETS_DIR, f"{name}.json")
if os.path.exists(file_path):
with open(file_path, "r", encoding="utf-8") as f:
data = json.load(f)
return SubtitlePreset(**data)
return None