|
|
import gradio as gr |
|
|
import requests |
|
|
import os |
|
|
from moviepy.editor import VideoFileClip, concatenate_videoclips |
|
|
import tempfile |
|
|
|
|
|
|
|
|
PIXABAY_API_KEY = os.getenv("PIXABAY_API_KEY") |
|
|
|
|
|
if not PIXABAY_API_KEY: |
|
|
raise ValueError("لطفاً PIXABAY_API_KEY را در Secrets فضای خود قرار دهید!") |
|
|
|
|
|
VIDEO_SEARCH_URL = "https://pixabay.com/api/videos/" |
|
|
CLIP_DURATION = 3.0 |
|
|
|
|
|
def download_video(url, temp_dir): |
|
|
"""دانلود ویدیو و برگرداندن مسیر فایل""" |
|
|
response = requests.get(url, stream=True) |
|
|
if response.status_code != 200: |
|
|
return None |
|
|
|
|
|
temp_path = os.path.join(temp_dir, "clip.mp4") |
|
|
with open(temp_path, "wb") as f: |
|
|
for chunk in response.iter_content(chunk_size=8192): |
|
|
f.write(chunk) |
|
|
return temp_path |
|
|
|
|
|
def search_pixabay_videos(query, per_page=10): |
|
|
params = { |
|
|
"key": PIXABAY_API_KEY, |
|
|
"q": query, |
|
|
"video_type": "all", |
|
|
"per_page": per_page, |
|
|
"min_duration": 4, |
|
|
"safesearch": "true" |
|
|
} |
|
|
|
|
|
try: |
|
|
r = requests.get(VIDEO_SEARCH_URL, params=params, timeout=15) |
|
|
r.raise_for_status() |
|
|
data = r.json() |
|
|
|
|
|
videos = [] |
|
|
for hit in data.get("hits", []): |
|
|
|
|
|
vid = hit.get("videos", {}) |
|
|
if "large" in vid and vid["large"]["url"]: |
|
|
videos.append(vid["large"]["url"]) |
|
|
elif "medium" in vid and vid["medium"]["url"]: |
|
|
videos.append(vid["medium"]["url"]) |
|
|
|
|
|
return videos[:8] |
|
|
except Exception as e: |
|
|
print(e) |
|
|
return [] |
|
|
|
|
|
def create_montage(keywords): |
|
|
if not keywords.strip(): |
|
|
return None, "لطفاً کلمات کلیدی وارد کنید" |
|
|
|
|
|
query = keywords.strip().replace(",", " ").replace(" ", " ") |
|
|
|
|
|
with tempfile.TemporaryDirectory() as tmpdirname: |
|
|
video_urls = search_pixabay_videos(query) |
|
|
|
|
|
if not video_urls: |
|
|
return None, f"هیچ ویدیویی برای '{query}' پیدا نشد." |
|
|
|
|
|
clips = [] |
|
|
count = 0 |
|
|
|
|
|
for url in video_urls: |
|
|
try: |
|
|
path = download_video(url, tmpdirname) |
|
|
if not path: |
|
|
continue |
|
|
|
|
|
clip = VideoFileClip(path) |
|
|
|
|
|
|
|
|
if clip.duration < CLIP_DURATION: |
|
|
clip.close() |
|
|
continue |
|
|
|
|
|
|
|
|
start = max(0, (clip.duration - CLIP_DURATION) / 2) |
|
|
subclip = clip.subclip(start, start + CLIP_DURATION) |
|
|
|
|
|
clips.append(subclip) |
|
|
count += 1 |
|
|
|
|
|
clip.close() |
|
|
if count >= 5: |
|
|
break |
|
|
|
|
|
except Exception as e: |
|
|
print(f"خطا در پردازش ویدیو: {e}") |
|
|
continue |
|
|
|
|
|
if not clips: |
|
|
return None, "متأسفانه نتوانستیم ویدیوهای مناسب دانلود/برش دهیم." |
|
|
|
|
|
try: |
|
|
final_clip = concatenate_videoclips(clips, method="compose") |
|
|
|
|
|
|
|
|
output_path = os.path.join(tmpdirname, "montage.mp4") |
|
|
final_clip.write_videofile(output_path, |
|
|
codec="libx264", |
|
|
audio_codec="aac", |
|
|
fps=24, |
|
|
preset="medium", |
|
|
threads=4, |
|
|
logger=None) |
|
|
|
|
|
return output_path, f"ویدیو با موفقیت ساخته شد ({len(clips)} قطعه × ۳ ثانیه)" |
|
|
|
|
|
except Exception as e: |
|
|
return None, f"خطا در ساخت ویدیو: {str(e)}" |
|
|
|
|
|
finally: |
|
|
|
|
|
for c in clips: |
|
|
c.close() |
|
|
if 'final_clip' in locals(): |
|
|
final_clip.close() |
|
|
|
|
|
|
|
|
with gr.Blocks(title="Pixabay Video Montage") as demo: |
|
|
gr.Markdown("# ساخت مونتاژ ویدیو از Pixabay\nکلمات کلیدی را وارد کنید (مثال: nature, sunset, drone)") |
|
|
|
|
|
textbox = gr.Textbox(label="کلمات کلیدی (با کاما یا فاصله جدا کنید)", placeholder="ocean waves, beach, sunset") |
|
|
|
|
|
btn = gr.Button("ساخت ویدیو (۳ ثانیه از هر کلیپ)") |
|
|
|
|
|
output_video = gr.Video(label="ویدیوی نهایی") |
|
|
status = gr.Textbox(label="وضعیت") |
|
|
|
|
|
btn.click( |
|
|
fn=create_montage, |
|
|
inputs=textbox, |
|
|
outputs=[output_video, status], |
|
|
api_name="make_montage" |
|
|
) |
|
|
|
|
|
if __name__ == "__main__": |
|
|
demo.launch() |