script2video / app.py
diwash-barla's picture
old recovery
423659f
# ==============================================================================
# app.py - [FINAL CORRECTED VERSION FOR HUGGING FACE v2]
# CHANGE: init_db() ko app startup par call kiya gaya hai.
# ==============================================================================
import os
from dotenv import load_dotenv
# Ye line aapke local .env file se variables load karegi (Hugging Face par iska koi asar nahi)
load_dotenv()
import uuid
import threading
from flask import Flask, request, jsonify, render_template_string, send_from_directory
from werkzeug.utils import secure_filename
# engine.py se zaroori functions AUR FOLDER PATHS import karein
from engine import (
init_db, create_task, get_task, run_ai_engine_worker,
generate_script_with_ai,
UPLOAD_FOLDER,
OUTPUT_FOLDER
)
# ==============================================================================
# 1. HTML Templates
# ==============================================================================
MODERN_CSS_BASE = """<link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin><link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;600;700&display=swap" rel="stylesheet"><style>:root{--bg-color:#1a1a2e;--card-bg-color:rgba(22,22,46,0.7);--primary-color:#e94560;--secondary-color:#0f3460;--text-color:#f0f0f0;--text-muted-color:#a0a0a0;--border-color:rgba(233,69,96,0.2);--gradient:linear-gradient(90deg,#e94560,#a63f82)}body{font-family:'Poppins',sans-serif;background-color:var(--bg-color);background-image:linear-gradient(315deg,var(--bg-color) 0%,var(--secondary-color) 74%);color:var(--text-color);padding-top:20px;padding-bottom:20px}.card{background:var(--card-bg-color);border:1px solid var(--border-color);border-radius:16px;backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);box-shadow:0 8px 32px 0 rgba(0,0,0,0.37);animation:fadeInUp 0.5s ease-out}h1,h2,h3,h4,h5,h6{background:var(--gradient);-webkit-background-clip:text;-webkit-text-fill-color:transparent;font-weight:700}.text-muted{color:var(--text-muted-color) !important}.form-control,.form-select{background-color:rgba(0,0,0,0.2);color:var(--text-color);border:1px solid var(--border-color);border-radius:8px}.form-control:focus,.form-select:focus{background-color:rgba(0,0,0,0.3);color:var(--text-color);border-color:var(--primary-color);box-shadow:0 0 0 .25rem rgba(233,69,96,0.25)}.form-control::placeholder{color:var(--text-muted-color);opacity:.7}.btn{border:none;border-radius:8px;font-weight:600;padding:12px 24px;transition:transform .1s ease-out,box-shadow .2s ease-out,filter .2s ease-out}.btn:hover{transform:translateY(-3px);box-shadow:0 5px 15px rgba(0,0,0,.3)}.btn:active{transform:translateY(0) scale(.98);filter:brightness(1.2)}.btn-primary{background:var(--gradient);color:#fff}.btn-primary:active{box-shadow:0 0 5px var(--primary-color),0 0 25px var(--primary-color),0 0 50px var(--primary-color)}.btn-secondary:active,.btn-dark:active{box-shadow:0 0 5px rgba(255,255,255,.8),0 0 25px rgba(255,255,255,.5)}@keyframes fadeInUp{from{opacity:0;transform:translateY(20px)}to{opacity:1;transform:translateY(0)}}</style>"""
HTML_HOME = """<!DOCTYPE html><html lang="hi"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Sparkling Gyan AI - होम</title><link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet"><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">""" + MODERN_CSS_BASE + """<style>.ai-generator-controls{border:1px solid var(--border-color);border-radius:12px;padding:20px;margin-top:20px;background-color:rgba(0,0,0,.1)}</style></head><body><div class="container my-5"><div class="card p-4 p-md-5"><div class="d-flex justify-content-between align-items-center mb-3"><h1 class="mb-0">✨ Sparkling Gyan AI</h1><a href="/history" class="btn btn-secondary"><i class="fas fa-history"></i> हिस्ट्री देखें</a></div><p class="text-center text-muted mb-4">एक उन्नत, ऑटोमेटिक वीडियो जेनरेटर</p><form id="upload-form" action="/process" method="POST" enctype="multipart/form-data"><div class="mb-3"><label for="script_text" class="form-label fs-5 fw-bold text-light">१. कहानी लिखें</label><textarea class="form-control" id="script_text" name="script_text" rows="8" placeholder="यहाँ कोई टॉपिक लिखें (जैसे 'चंद्रमा के रहस्य') और नीचे 'AI से स्क्रिप्ट बनाएँ' बटन दबाएँ, या अपनी पूरी स्क्रिप्ट यहाँ पेस्ट करें..."></textarea></div><div class="ai-generator-controls"><div class="row align-items-end"><div class="col-md-5"><label for="video-length-select" class="form-label text-light">वीडियो की लंबाई</label><select id="video-length-select" class="form-select"><option value="short">छोटी (~30 सेकंड)</option><option value="medium" selected>मध्यम (~1 मिनट)</option><option value="long">लंबी (~2 मिनट)</option></select></div><div class="col-md-7 d-grid"><button type="button" id="generate-script-btn" class="btn btn-dark"><i class="fas fa-magic"></i> AI से स्क्रिप्ट बनाएँ</button></div></div><div id="ai-status" class="mt-2"></div></div><div class="text-center text-muted my-3">या</div><div class="mb-3"><label for="script_file" class="form-label">एक ऑडियो स्क्रिप्ट फ़ाइल अपलोड करें</label><input class="form-control" type="file" id="script_file" name="script_file" accept="audio/*"></div><h5 class="mt-4 mb-3 text-light">२. अतिरिक्त विकल्प</h5><div class="row mb-3"><div class="col-md-6"><label class="form-label">वीडियो ओरिएंटेशन</label><div class="form-check"><input class="form-check-input" type="radio" name="orientation" id="orientation_horizontal" value="horizontal" checked><label class="form-check-label" for="orientation_horizontal">Landscape (16:9)</label></div><div class="form-check"><input class="form-check-input" type="radio" name="orientation" id="orientation_vertical" value="vertical"><label class="form-check-label" for="orientation_vertical">Portrait (9:16)</label></div></div><div class="col-md-6"><label for="max_clip_length" class="form-label">अधिकतम क्लिप लंबाई (सेकंड)</label><input type="number" class="form-control" id="max_clip_length" name="max_clip_length" value="15" min="3"></div></div><div class="form-check mb-4"><input class="form-check-input" type="checkbox" value="true" id="mute_final_video" name="mute_final_video"><label class="form-check-label" for="mute_final_video">ऑडियो/वॉयसओवर ट्रैक को म्यूट करें</label></div><div class="d-grid mt-4"><button type="submit" class="btn btn-primary btn-lg">🚀 वीडियो बनाना शुरू करें!</button></div></form></div></div><script>document.addEventListener("DOMContentLoaded",()=>{const e=document.getElementById("generate-script-btn"),t=document.getElementById("script_text"),o=document.getElementById("video-length-select"),n=document.getElementById("ai-status");e.addEventListener("click",async()=>{const a=t.value.trim();if(!a)return void(n.innerHTML='<p class="text-danger">कृपया स्क्रिप्ट बनाने के लिए कोई टॉपिक लिखें।</p>');const i=o.value;e.disabled=!0,e.innerHTML='<i class="fas fa-spinner fa-spin"></i> जेनरेट हो रहा है...',n.innerHTML="<p>🧠 AI आपके लिए एक बेहतरीन स्क्रिप्ट लिख रहा है, कृपया प्रतीक्षा करें...</p>";try{const r=await fetch("/generate-script",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({topic:a,video_length:i})}),c=await r.json();if(!r.ok)throw new Error(c.error||"सर्वर से कोई अज्ञात त्रुटि हुई।");t.value=c.script,n.innerHTML='<p style="color: #2ea043;">✅ AI ने स्क्रिप्ट तैयार कर दी है! अब आप वीडियो बना सकते हैं।</p>'}catch(d){console.error("Script Generation Error:",d),n.innerHTML=`<p class="text-danger">स्क्रिप्ट बनाने में विफल: ${d.message}</p>`}finally{e.disabled=!1,e.innerHTML='<i class="fas fa-magic"></i> AI से स्क्रिप्ट बनाएँ'}})})</script></body></html>"""
HTML_PROCESSING = """<!DOCTYPE html><html lang="hi"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>प्रोसेसिंग...</title><link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">""" + MODERN_CSS_BASE + """<style>body{display:flex;align-items:center;justify-content:center;min-height:100vh}#log-output{white-space:pre-wrap;background-color:rgba(0,0,0,.3);font-family:monospace;max-height:400px;overflow-y:auto;border-radius:8px;border:1px solid var(--border-color)}.progress-bar{background:var(--gradient)}.progress{height:25px;border-radius:8px;background-color:rgba(0,0,0,.3)}</style></head><body><div class="container"><div class="card p-4 p-md-5"><h2 class="text-center">मिशन कंट्रोल</h2><p id="status-text" class="text-center text-muted">प्रक्रिया शुरू हो रही है...</p><div class="progress mb-3" role="progressbar"><div class="progress-bar progress-bar-striped progress-bar-animated" id="progress-bar" style="width:0%">0%</div></div><pre id="log-output" class="p-3"></pre></div></div><script>const taskId="{{task_id}}",progressBar=document.getElementById("progress-bar"),statusText=document.getElementById("status-text"),logOutput=document.getElementById("log-output"),pollingInterval=setInterval(async()=>{try{const e=await fetch(`/progress/${taskId}`),t=await e.json(),o=t.log.trim().split("\\n").pop();statusText.innerText=o||"प्रतीक्षा में...",progressBar.style.width=t.progress+"%",progressBar.innerText=t.progress+"%",logOutput.innerText=t.log,logOutput.scrollTop=logOutput.scrollHeight,"complete"===t.status?(clearInterval(pollingInterval),window.location.href=`/result/${t.output_filename}`):"error"===t.status&&(clearInterval(pollingInterval),statusText.innerText="कोई त्रुटि हुई! विवरण के लिए लॉग देखें।",progressBar.classList.remove("bg-success"),progressBar.classList.add("bg-danger"))}catch(e){clearInterval(pollingInterval),statusText.innerText="सर्वर से कनेक्शन टूट गया।"}},1500)</script></body></html>"""
HTML_RESULT = """<!DOCTYPE html><html lang="hi"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>आपकी वीडियो तैयार है!</title><link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet"><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">""" + MODERN_CSS_BASE + """<style>body{display:flex;align-items:center;justify-content:center;min-height:100vh;text-align:center}video{width:100%;max-width:800px;border-radius:12px;border:1px solid var(--border-color)}</style></head><body><div class="container"><div class="card p-4 p-md-5"><h1 class="mb-3">🎉 मिशन पूरा हुआ!</h1><p class="text-muted mb-4">आपकी फाइनल वीडियो नीचे है।</p><video controls autoplay muted loop><source src="/outputs/{{ filename }}" type="video/mp4"> आपका ब्राउज़र वीडियो टैग को सपोर्ट नहीं करता। </video><div class="d-grid gap-2 d-md-flex justify-content-md-center mt-4"><a href="/outputs/{{ filename }}" class="btn btn-primary" download><i class="fas fa-download"></i> वीडियो डाउनलोड करें</a> <a href="/" class="btn btn-secondary">एक और वीडियो बनाएँ</a></div></div></div></body></html>"""
HTML_HISTORY = """<!DOCTYPE html><html lang="hi"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>वीडियो हिस्ट्री</title><link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet"><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">""" + MODERN_CSS_BASE + """<style>.history-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(350px,1fr));gap:20px}.card{transition:transform .3s ease}.card:hover{transform:translateY(-5px)}video{width:100%;border-radius:8px;margin-top:10px;border:1px solid var(--border-color)}.actions a{margin-right:10px;margin-bottom:10px}</style></head><body><div class="container"><a href="/" class="btn btn-secondary mb-4"><i class="fas fa-arrow-left"></i> वापस मुख्य पेज पर</a><h1 class="text-center mb-5">बनाए गए वीडियो की हिस्ट्री</h1> {% if items %}<div class="history-grid"> {% for item in items %}<div class="card"><div class="card-body"><h5 class="card-title">टास्क आईडी:</h5><p class="card-text text-muted" style="font-size:.8em">{{ item.task_id }}</p><video controls preload="metadata"><source src="/outputs/{{ item.video }}" type="video/mp4"></video><div class="actions mt-3"><a href="/outputs/{{ item.video }}" class="btn btn-primary" download><i class="fas fa-download"></i> डाउनलोड</a> {% if item.report %}<a href="/outputs/{{ item.report }}" class="btn btn-dark" target="_blank"><i class="fas fa-file-alt"></i> रिपोर्ट देखें</a> {% endif %}</div></div></div> {% endfor %}</div> {% else %}<div class="text-center card p-5"><h2>कोई हिस्ट्री नहीं मिली</h2><p class="text-muted">आपने अभी तक कोई वीडियो नहीं बनाया है।</p></div> {% endif %}</div></body></html>"""
# ==============================================================================
# Flask Application Setup
# ==============================================================================
app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
app.config['OUTPUT_FOLDER'] = OUTPUT_FOLDER
# <<<--- YAHAN EK LINE JODI GAYI HAI --- >>>
# Database table banayein agar pehle se nahi bani hai
init_db()
# <<<--- BADLAV YAHAN KHATM HOTA HAI --- >>>
@app.route('/')
def home():
return render_template_string(HTML_HOME)
@app.route('/process', methods=['POST'])
def process_video():
task_id = str(uuid.uuid4())
create_task(task_id)
script_text = request.form.get('script_text')
script_file = request.files.get('script_file')
orientation = request.form.get('orientation', 'horizontal')
max_clip_length = int(request.form.get('max_clip_length', 15))
mute_final_video = request.form.get('mute_final_video') == 'true'
script_file_path = None
if script_file and script_file.filename:
filename = secure_filename(script_file.filename)
task_upload_dir = os.path.join(app.config['UPLOAD_FOLDER'], task_id)
os.makedirs(task_upload_dir, exist_ok=True)
script_file_path = os.path.join(task_upload_dir, filename)
script_file.save(script_file_path)
if not script_text and not script_file_path:
return "Please provide a script text or an audio file.", 400
thread = threading.Thread(
target=run_ai_engine_worker,
args=(task_id, script_text, script_file_path, orientation, max_clip_length, mute_final_video)
)
thread.start()
return render_template_string(HTML_PROCESSING, task_id=task_id)
@app.route('/generate-script', methods=['POST'])
def generate_script():
data = request.get_json()
topic = data.get('topic')
video_length = data.get('video_length')
if not topic or not video_length:
return jsonify({'error': 'Topic and video length are required.'}), 400
try:
generated_script = generate_script_with_ai(topic, video_length)
return jsonify({'script': generated_script})
except Exception as e:
print(f"Error during script generation: {e}")
return jsonify({'error': f"AI से संपर्क करने में विफल: {str(e)}"}), 500
@app.route('/progress/<task_id>')
def progress(task_id):
task = get_task(task_id)
if not task:
return jsonify({'status': 'error', 'log': 'Task not found.'})
return jsonify(dict(task))
@app.route('/result/<filename>')
def result(filename):
return render_template_string(HTML_RESULT, filename=filename)
@app.route('/outputs/<path:filename>')
def serve_output_file(filename):
return send_from_directory(app.config['OUTPUT_FOLDER'], filename)
@app.route('/history')
def history():
output_dir = app.config['OUTPUT_FOLDER']
history_items = []
try:
all_files = sorted(os.listdir(output_dir), reverse=True)
for filename in all_files:
if filename.endswith('_final_video.mp4'):
task_id = filename.replace('_final_video.mp4', '')
report_filename = f"{task_id}_report.json"
item = {
'video': filename,
'task_id': task_id,
'report': report_filename if report_filename in all_files else None
}
history_items.append(item)
except FileNotFoundError:
print(f"'{output_dir}' directory not found.")
return render_template_string(HTML_HISTORY, items=history_items)
# Is block ko ab production mein ignore kar diya jayega
if __name__ == '__main__':
init_db()
app.run(host='0.0.0.0', port=5000, debug=True)