test / app.py
lexicalspace's picture
Update app.py
07bb909 verified
import yt_dlp
import os
import gradio as gr
import random
import threading
import requests
from gradio_client import Client
from huggingface_hub import InferenceClient
# =====================================================
# 🎨 CSS & UI CONFIGURATION (RESPONSIVE)
# =====================================================
custom_css = """
/* Hide the footer for a cleaner look */
footer {visibility: hidden}
/* PC Styles */
.container { max-width: 1200px; margin: auto; padding-top: 20px; }
.card { border-radius: 12px; border: 1px solid #e5e7eb; padding: 20px; margin-bottom: 20px; }
/* Mobile Response */
@media (max-width: 768px) {
.gradio-container { padding: 5px !important; }
.group-container { padding: 10px !important; }
button { min-height: 50px; }
}
/* Error Message Styling */
.error-box {
text-align: center;
padding: 50px;
background: #FEF2F2;
border: 2px solid #F87171;
border-radius: 10px;
color: #991B1B;
font-size: 1.2rem;
font-weight: bold;
}
"""
# =====================================================
# βš™οΈ CONFIG & CLIENTS
# =====================================================
PROXY_SPACE_URL = "lexicalspace/Proxy-Server"
OUTPUT_DIR = "downloads"
os.makedirs(OUTPUT_DIR, exist_ok=True)
MAX_CONCURRENCY = 6
MAX_QUEUE = 20
# SEO Config
HF_TOKEN = os.getenv("HF_TOKEN")
SEO_MODEL_ID = "Qwen/Qwen2.5-Coder-32B-Instruct"
try:
seo_client = InferenceClient(api_key=HF_TOKEN)
except:
seo_client = None
# =====================================================
# πŸ”„ GLOBAL STATE
# =====================================================
active_jobs = {}
job_lock = threading.Lock()
# =====================================================
# πŸ“₯ PART 1: DOWNLOADER LOGIC
# =====================================================
def get_proxy_batch():
try:
client = Client(PROXY_SPACE_URL)
return [f"http://{p}" for p in client.predict(api_name="/get_proxies_api")[0][:6]]
except: return []
def get_best_proxy():
try:
proxies = Client(PROXY_SPACE_URL).predict(api_name="/get_proxies_api")[0]
return f"http://{random.choice(proxies)}" if proxies else None
except: return None
def build_format_selector(quality_mode):
if "Audio" in quality_mode: return {'format': 'bestaudio/best', 'postprocessors': [{'key': 'FFmpegExtractAudio','preferredcodec': 'mp3'}], 'ext': 'mp3'}
if "4K" in quality_mode: return {'format': 'bestvideo[ext=mp4]+bestaudio[ext=m4a]/best[ext=mp4]/best', 'ext': 'mp4'}
height = 1080 if "1080" in quality_mode else 720 if "720" in quality_mode else 360
return {'format': f'bestvideo[height<={height}][ext=mp4]+bestaudio[ext=m4a]/best[height<={height}][ext=mp4]', 'ext': 'mp4'}
def fetch_metadata(url):
if not url: return None, "⚠️ Please enter a URL.", None, None, None
try:
data = requests.get(f"https://noembed.com/embed?url={url}", timeout=5).json()
if "error" in data: return None, f"❌ Error: {data['error']}", None, None, None
title = data.get('title', 'Media File')
desc = f"### 🎬 {title}\n**πŸ‘€ Author:** {data.get('author_name', 'Unknown')}"
safe_title = "".join([c for c in title if c.isalnum() or c in " ._-"])
return data.get('thumbnail_url'), desc, gr.update(interactive=True), safe_title, "βœ… Ready"
except Exception as e:
return None, f"❌ Error: {str(e)}", None, None, None
def run_downloader(engine_mode, url, quality, custom_name, cookies, progress=gr.Progress()):
if not url: return None, "⚠️ No URL provided", ""
# Engine Selection
engine_name = "V2 (Fast)" if "V2" in engine_mode or ("Auto" in engine_mode and len(url) > 60) else "V1 (Stable)"
with job_lock:
if active_jobs.get(url) == "running": return None, "β›” Already running", ""
active_jobs[url] = "running"
log = [f"πŸš€ Engine: {engine_name} | Q: {quality}"]
opts = build_format_selector(quality)
fname = f"{custom_name}.%(ext)s" if custom_name else '%(title)s.%(ext)s'
# Setup Proxy & Client
proxies = get_proxy_batch() if "V1" in engine_name else ([get_best_proxy()] if get_best_proxy() else [None])
strategies = ["android", "web", "ios"]
success, final_file = False, None
cookie_path = f"cookies_{random.randint(1000,9999)}.txt" if cookies else None
if cookies:
with open(cookie_path, "w") as f: f.write(cookies)
for proxy in proxies:
if success: break
for strat in strategies:
if success: break
ydl_opts = {
'outtmpl': os.path.join(OUTPUT_DIR, fname),
'quiet': True, 'noplaylist': True,
'extractor_args': {'youtube': {'player_client': [strat]}},
**opts
}
if proxy: ydl_opts['proxy'] = proxy
if cookie_path: ydl_opts['cookiefile'] = cookie_path
try:
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
info = ydl.extract_info(url, download=True)
fpath = ydl.prepare_filename(info)
final_file = fpath.rsplit('.', 1)[0] + '.' + opts['ext']
if os.path.exists(final_file): success = True
except: continue
if cookie_path and os.path.exists(cookie_path): os.remove(cookie_path)
with job_lock: active_jobs.pop(url, None)
return (final_file, "βœ… Download Complete", "\n".join(log)) if success else (None, "❌ Failed", "\n".join(log))
# =====================================================
# ⚑ PART 2: SEO LOGIC
# =====================================================
def generate_seo(code, ftype):
if not seo_client: return "πŸ”’ Error: HF_TOKEN missing in Secrets."
if not code: return "⚠️ Please paste code."
prompt = f"Analyze this {ftype} code. Return valid JSON-LD and SEO Title/Description/Keywords."
try:
return seo_client.chat_completion("Qwen/Qwen2.5-Coder-32B-Instruct", messages=[{"role": "user", "content": prompt}], max_tokens=1000).choices[0].message.content
except Exception as e: return f"❌ Error: {str(e)}"
# =====================================================
# 🧭 PART 3: ROUTING & UI
# =====================================================
def router(request: gr.Request):
"""
Determines which tool to show based on ?mode= URL parameter.
"""
params = request.query_params
mode = params.get("mode")
# STRICT MODE: If mode is missing or wrong, show Error Page
if not mode:
# Default fallback (Optional: Change to 'dl_col' if you want a default tool)
return gr.update(visible=True), gr.update(visible=False), gr.update(visible=False)
if mode == "downloader":
return gr.update(visible=True), gr.update(visible=False), gr.update(visible=False)
elif mode == "seo":
return gr.update(visible=False), gr.update(visible=True), gr.update(visible=False)
else:
# WRONG URL -> Error Page
return gr.update(visible=False), gr.update(visible=False), gr.update(visible=True)
# =====================================================
# πŸ–₯️ MAIN APP
# =====================================================
with gr.Blocks(css=custom_css, title="Lexical Space Tools", theme=gr.themes.Soft()) as app:
# --- 1. DOWNLOADER UI ---
with gr.Column(visible=False, elem_classes="container") as dl_col:
gr.Markdown("# πŸš€ UltraMax Downloader")
with gr.Group(elem_classes="card"):
with gr.Row():
url_input = gr.Textbox(label="URL", placeholder="Paste link...", scale=3, show_label=False)
fetch_btn = gr.Button("πŸ” Fetch", variant="primary", scale=1)
with gr.Row():
thumb = gr.Image(label="Preview", height=150, interactive=False, show_label=False)
with gr.Column():
desc = gr.Markdown("### ⏳ Ready")
status = gr.Label(value="Idle", show_label=False)
with gr.Accordion("βš™οΈ Settings (Quality / Rename)", open=False):
with gr.Row():
qual = gr.Dropdown(["Audio Only (Best)", "Video (1080p)", "Video (720p)"], value="Audio Only (Best)", label="Quality")
eng = gr.Radio(["Auto", "V1 (Stable)", "V2 (Fast)"], value="Auto", label="Engine")
fname = gr.Textbox(label="Filename (Optional)", placeholder="MyFile")
cookies = gr.Textbox(label="Cookies", placeholder="Netscape format...", lines=1)
dl_btn = gr.Button("⬇️ Download Now", variant="stop", interactive=False, size="lg")
with gr.Group(elem_classes="card"):
out_file = gr.File(label="Download")
out_log = gr.Textbox(label="Logs", lines=2)
# Events
fetch_btn.click(fetch_metadata, [url_input], [thumb, desc, dl_btn, fname, status])
dl_btn.click(run_downloader, [eng, url_input, qual, fname, cookies], [out_file, status, out_log])
# --- 2. SEO UI ---
with gr.Column(visible=False, elem_classes="container") as seo_col:
gr.Markdown("# ⚑ AI SEO Generator")
with gr.Group(elem_classes="card"):
ftype = gr.Radio(["python", "html"], label="Type", value="python")
code_in = gr.Code(language="python", lines=15, label="Code")
seo_btn = gr.Button("✨ Generate", variant="primary")
res = gr.Markdown(label="Result")
ftype.change(lambda x: gr.Code(language=x), inputs=ftype, outputs=code_in)
seo_btn.click(generate_seo, [code_in, ftype], [res])
# --- 3. ERROR / 404 UI ---
with gr.Column(visible=False, elem_classes="container") as error_col:
gr.HTML("""
<div class="error-box">
<h1>⚠️ Access Denied</h1>
<p>No tool is available at this URL.</p>
<p style="font-size: 1rem; color: #555;">Please check your link or contact the administrator.</p>
</div>
""")
# --- ROUTING HANDLER ---
# Checks URL on load and toggles visibility of columns
app.load(fn=router, inputs=None, outputs=[dl_col, seo_col, error_col])
if __name__ == "__main__":
app.queue(max_size=20).launch()