Spaces:
Paused
Paused
| from fastapi import FastAPI, Request | |
| from fastapi.responses import HTMLResponse | |
| from fastapi.staticfiles import StaticFiles | |
| import requests | |
| import textwrap | |
| from moviepy.editor import ImageClip, ColorClip, CompositeVideoClip | |
| from PIL import Image, ImageDraw, ImageFont | |
| import numpy as np | |
| import uuid | |
| import os | |
| import time | |
| import glob | |
| # إنشاء المجلدات اللازمة | |
| os.makedirs("static/videos", exist_ok=True) | |
| app = FastAPI( | |
| title="Auto Quotes Video & SEO API", | |
| description="API لإنشاء فيديوهات الحكم مع توليد بيانات اليوتيوب (العنوان، الوصف، والوسوم).", | |
| version="1.2.0" | |
| ) | |
| app.mount("/static", StaticFiles(directory="static"), name="static") | |
| def cleanup_old_videos(): | |
| now = time.time() | |
| for f in glob.glob("static/videos/*.mp4"): | |
| if os.stat(f).st_mtime < now - 600: | |
| try: | |
| os.remove(f) | |
| except: | |
| pass | |
| def fetch_random_quote_data(): | |
| """جلب الحكمة وتوليد بيانات اليوتيوب لها""" | |
| try: | |
| response = requests.get("https://dummyjson.com/quotes/random", timeout=5) | |
| data = response.json() | |
| quote = data["quote"] | |
| author = data["author"] | |
| except: | |
| quote = "The best way to predict the future is to invent it." | |
| author = "Alan Kay" | |
| full_text = f'"{quote}"\n\n— {author}' | |
| # توليد بيانات اليوتيوب (SEO) | |
| title = f"Life Changing Wisdom: {quote[:40]}... #Shorts" | |
| description = f"Inspirational quote by {author}.\n\nText: {quote}\n\n#wisdom #quotes #motivation #inspiration #shorts" | |
| tags = ["wisdom", "motivation", "quotes", author.replace(" ", ""), "inspirational", "shorts", "life-lessons"] | |
| return { | |
| "text": full_text, | |
| "title": title, | |
| "description": description, | |
| "tags": tags | |
| } | |
| def create_text_image(text, size=(720, 1280), font_size=45): | |
| img = Image.new('RGBA', size, (0, 0, 0, 0)) | |
| draw = ImageDraw.Draw(img) | |
| try: | |
| font = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", font_size) | |
| except: | |
| font = ImageFont.load_default() | |
| lines = textwrap.wrap(text, width=25) | |
| y_text = size[1] / 3 | |
| for line in lines: | |
| bbox = draw.textbbox((0, 0), line, font=font) | |
| w = bbox[2] - bbox[0] | |
| h = bbox[3] - bbox[1] | |
| x = (size[0] - w) / 2 | |
| draw.text((x+3, y_text+3), line, font=font, fill=(0, 0, 0, 150)) | |
| draw.text((x, y_text), line, font=font, fill=(255, 255, 255, 255)) | |
| y_text += h + 20 | |
| return np.array(img) | |
| def generate_video_file(quote_text): | |
| filename = f"quote_{uuid.uuid4().hex}.mp4" | |
| output_path = os.path.join("static", "videos", filename) | |
| bg = ColorClip(size=(720, 1280), color=(15, 23, 42)).set_duration(6) | |
| text_arr = create_text_image(quote_text) | |
| txt_clip = ImageClip(text_arr).set_duration(6) | |
| video = CompositeVideoClip([bg, txt_clip]) | |
| video.write_videofile(output_path, fps=24, codec="libx264", audio=False) | |
| return filename | |
| def index_page(): | |
| return """ | |
| <!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Auto YouTube Content API</title> | |
| <style> | |
| body { font-family: 'Segoe UI', sans-serif; background-color: #0f172a; color: white; text-align: center; padding: 30px; } | |
| h1 { color: #38bdf8; } | |
| .btn { background-color: #38bdf8; color: #0f172a; border: none; padding: 12px 24px; font-size: 16px; font-weight: bold; border-radius: 8px; cursor: pointer; margin: 10px; transition: 0.3s; } | |
| .btn:hover { background-color: #0284c7; color: white; } | |
| .content-box { background: #1e293b; padding: 20px; border-radius: 12px; margin-top: 20px; text-align: left; display: none; max-width: 600px; margin-left: auto; margin-right: auto; } | |
| .meta-item { margin-bottom: 15px; border-bottom: 1px solid #334155; padding-bottom: 10px; } | |
| .meta-title { color: #94a3b8; font-size: 14px; font-weight: bold; text-transform: uppercase; } | |
| video { border-radius: 15px; width: 300px; margin-top: 20px; } | |
| code { color: #38bdf8; background: #0f172a; padding: 2px 5px; border-radius: 4px; } | |
| </style> | |
| </head> | |
| <body> | |
| <h1>🚀 YouTube Auto-Content Creator</h1> | |
| <p>Get the video, title, description, and tags in one click.</p> | |
| <button class="btn" onclick="generate()" id="gen-btn">🎬 Generate Full Package</button> | |
| <a href="/docs" target="_blank"><button class="btn" style="background:#475569; color:white;">📖 API Docs</button></a> | |
| <div id="loader" style="display:none;">⏳ Processing...</div> | |
| <div id="result" class="content-box"> | |
| <div class="meta-item"> | |
| <div class="meta-title">Video Link</div> | |
| <div id="res-url" style="word-break: break-all; color: #10b981;"></div> | |
| </div> | |
| <div class="meta-item"> | |
| <div class="meta-title">YouTube Title</div> | |
| <div id="res-title"></div> | |
| </div> | |
| <div class="meta-item"> | |
| <div class="meta-title">Description</div> | |
| <div id="res-desc" style="white-space: pre-wrap;"></div> | |
| </div> | |
| <div class="meta-item"> | |
| <div class="meta-title">Tags</div> | |
| <div id="res-tags" style="color: #fbbf24;"></div> | |
| </div> | |
| <center><video id="res-video" controls></video></center> | |
| </div> | |
| <script> | |
| async function generate() { | |
| const btn = document.getElementById('gen-btn'); | |
| const loader = document.getElementById('loader'); | |
| const result = document.getElementById('result'); | |
| btn.disabled = true; | |
| loader.style.display = "block"; | |
| result.style.display = "none"; | |
| try { | |
| const response = await fetch('/api/generate'); | |
| const data = await response.json(); | |
| if (data.status === "success") { | |
| document.getElementById('res-url').innerText = data.video_url; | |
| document.getElementById('res-title').innerText = data.youtube_data.title; | |
| document.getElementById('res-desc').innerText = data.youtube_data.description; | |
| document.getElementById('res-tags').innerText = data.youtube_data.tags.join(", "); | |
| document.getElementById('res-video').src = data.video_url; | |
| result.style.display = "block"; | |
| } | |
| } catch (e) { alert("Error!"); } | |
| finally { | |
| btn.disabled = false; | |
| loader.style.display = "none"; | |
| } | |
| } | |
| </script> | |
| </body> | |
| </html> | |
| """ | |
| def api_generate_content(request: Request): | |
| cleanup_old_videos() | |
| # جلب البيانات والنص | |
| quote_data = fetch_random_quote_data() | |
| # إنشاء الفيديو | |
| video_filename = generate_video_file(quote_data["text"]) | |
| base_url = str(request.base_url).replace("http://", "https://") | |
| video_url = f"{base_url}static/videos/{video_filename}" | |
| return { | |
| "status": "success", | |
| "video_url": video_url, | |
| "youtube_data": { | |
| "title": quote_data["title"], | |
| "description": quote_data["description"], | |
| "tags": quote_data["tags"] | |
| } | |
| } | |