| import gradio as gr |
| import requests |
| import json |
| import time |
| import re |
| import base64 |
| import os |
| import tempfile |
| from pathlib import Path |
| from urllib.parse import quote_plus |
| import html as html_lib |
|
|
| |
|
|
| def extract_api_config_from_website(): |
| """ |
| استخراج خودکار API key و endpoint از سایت downloaderto |
| """ |
| try: |
| print("\n" + "=" * 60) |
| print("🔍 استخراج تنظیمات API از سایت downloaderto...") |
| print("=" * 60) |
| |
| |
| site_url = "https://downloaderto.com/enHF/" |
| headers = { |
| 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36', |
| 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', |
| } |
| |
| response = requests.get(site_url, headers=headers, timeout=15) |
| |
| if response.status_code != 200: |
| print(f"❌ خطا در دریافت صفحه: {response.status_code}") |
| return None |
| |
| html_content = response.text |
| |
| |
| debug_dir = Path(tempfile.gettempdir()) / "debug" |
| debug_dir.mkdir(exist_ok=True) |
| debug_file = debug_dir / "downloaderto_main_page.html" |
| |
| with open(debug_file, 'w', encoding='utf-8') as f: |
| f.write(html_content) |
| print(f"📄 HTML ذخیره شد: {debug_file}") |
| |
| |
| api_key_patterns = [ |
| r'api["\']?\s*[:=]\s*["\']([a-f0-9]{32})["\']', |
| r'apiKey["\']?\s*[:=]\s*["\']([a-f0-9]{32})["\']', |
| r'API_KEY["\']?\s*[:=]\s*["\']([a-f0-9]{32})["\']', |
| r'key["\']?\s*[:=]\s*["\']([a-f0-9]{32})["\']', |
| r'["\']([a-f0-9]{32})["\']', |
| ] |
| |
| api_key = None |
| for pattern in api_key_patterns: |
| matches = re.findall(pattern, html_content, re.IGNORECASE) |
| for match in matches: |
| |
| if len(match) == 32 and all(c in '0123456789abcdef' for c in match): |
| api_key = match |
| print(f"✅ API Key پیدا شد: {api_key}") |
| break |
| if api_key: |
| break |
| |
| |
| api_url_patterns = [ |
| r'(https?://[a-z0-9\.-]+/ajax/download\.php)', |
| r'(https?://[a-z0-9\.-]+/api/download\.php)', |
| r'apiUrl["\']?\s*[:=]\s*["\']([^"\']+)["\']', |
| r'API_URL["\']?\s*[:=]\s*["\']([^"\']+)["\']', |
| r'downloadUrl["\']?\s*[:=]\s*["\']([^"\']+)["\']', |
| ] |
| |
| api_url = None |
| for pattern in api_url_patterns: |
| matches = re.findall(pattern, html_content, re.IGNORECASE) |
| for match in matches: |
| if 'download' in match.lower() and match.startswith('http'): |
| api_url = match |
| print(f"✅ API URL پیدا شد: {api_url}") |
| break |
| if api_url: |
| break |
| |
| |
| js_files = re.findall(r'<script[^>]*src=["\']([^"\']+\.js[^"\']*)["\']', html_content) |
| |
| if not api_key or not api_url: |
| print(f"🔍 جستجو در {len(js_files)} فایل JavaScript...") |
| |
| for js_file in js_files[:5]: |
| try: |
| if not js_file.startswith('http'): |
| if js_file.startswith('/'): |
| js_url = f"https://downloaderto.com{js_file}" |
| else: |
| js_url = f"https://downloaderto.com/{js_file}" |
| else: |
| js_url = js_file |
| |
| print(f"📥 دریافت: {js_url[:60]}...") |
| js_response = requests.get(js_url, headers=headers, timeout=10) |
| |
| if js_response.status_code == 200: |
| js_content = js_response.text |
| |
| |
| if not api_key: |
| for pattern in api_key_patterns: |
| matches = re.findall(pattern, js_content, re.IGNORECASE) |
| for match in matches: |
| if len(match) == 32 and all(c in '0123456789abcdef' for c in match): |
| api_key = match |
| print(f"✅ API Key در JS پیدا شد: {api_key}") |
| break |
| if api_key: |
| break |
| |
| |
| if not api_url: |
| for pattern in api_url_patterns: |
| matches = re.findall(pattern, js_content, re.IGNORECASE) |
| for match in matches: |
| if 'download' in match.lower() and match.startswith('http'): |
| api_url = match |
| print(f"✅ API URL در JS پیدا شد: {api_url}") |
| break |
| if api_url: |
| break |
| |
| if api_key and api_url: |
| break |
| |
| except Exception as e: |
| print(f"⚠️ خطا در پردازش {js_file[:30]}: {str(e)[:30]}") |
| continue |
| |
| if api_key and api_url: |
| print("\n✅ تنظیمات API با موفقیت استخراج شد!") |
| return { |
| 'api_key': api_key, |
| 'api_url': api_url, |
| 'timestamp': time.time() |
| } |
| else: |
| print(f"\n⚠️ استخراج ناقص:") |
| print(f" API Key: {'✅' if api_key else '❌'}") |
| print(f" API URL: {'✅' if api_url else '❌'}") |
| return None |
| |
| except Exception as e: |
| print(f"❌ خطا در استخراج API: {str(e)}") |
| return None |
|
|
| def get_api_config(force_refresh=False): |
| """ |
| دریافت تنظیمات API - از cache یا استخراج جدید |
| """ |
| cache_file = Path(tempfile.gettempdir()) / "downloaderto_api_cache.json" |
| |
| |
| if not force_refresh and cache_file.exists(): |
| try: |
| with open(cache_file, 'r') as f: |
| cached_config = json.load(f) |
| |
| |
| cache_age = time.time() - cached_config.get('timestamp', 0) |
| if cache_age < 24 * 3600: |
| print(f"📦 استفاده از API cache (سن: {cache_age/3600:.1f} ساعت)") |
| return cached_config |
| except: |
| pass |
| |
| |
| config = extract_api_config_from_website() |
| |
| if config: |
| |
| try: |
| with open(cache_file, 'w') as f: |
| json.dump(config, f) |
| print(f"💾 تنظیمات API در cache ذخیره شد") |
| except: |
| pass |
| |
| return config |
| |
| |
| print("⚠️ استفاده از API پیشفرض") |
| return { |
| 'api_key': '2e716c3914a4f931fdad91ad9e14c6b1', |
| 'api_url': 'https://p.lbserver.xyz/ajax/download.php', |
| 'timestamp': time.time() |
| } |
|
|
| |
|
|
| def extract_youtube_video_id(url): |
| """استخراج Video ID از URL یوتیوب""" |
| patterns = [ |
| r'(?:v=|/)([0-9A-Za-z_-]{11}).*', |
| r'(?:embed/)([0-9A-Za-z_-]{11})', |
| r'(?:watch\?v=)([0-9A-Za-z_-]{11})', |
| r'youtu\.be/([0-9A-Za-z_-]{11})', |
| r'shorts/([0-9A-Za-z_-]{11})', |
| ] |
| |
| for pattern in patterns: |
| match = re.search(pattern, url) |
| if match: |
| return match.group(1) |
| return None |
|
|
| def get_title_from_youtube_oembed(video_id): |
| """دریافت عنوان از YouTube oEmbed API""" |
| try: |
| url = f"https://www.youtube.com/oembed?url=https://www.youtube.com/watch?v={video_id}&format=json" |
| print(f"🔍 درخواست به YouTube oEmbed API...") |
| |
| response = requests.get(url, timeout=10) |
| |
| if response.status_code == 200: |
| data = response.json() |
| title = data.get('title', '') |
| |
| if title: |
| title = html_lib.unescape(title) |
| print(f"✅ عنوان دریافت شد: {title}") |
| return title |
| else: |
| print(f"⚠️ YouTube oEmbed خطا: {response.status_code}") |
| |
| except Exception as e: |
| print(f"❌ خطا در YouTube oEmbed: {str(e)}") |
| |
| return None |
|
|
| def get_title_from_noembed(video_id): |
| """دریافت عنوان از noembed.com""" |
| try: |
| url = f"https://noembed.com/embed?url=https://www.youtube.com/watch?v={video_id}" |
| print(f"🔍 درخواست به noembed.com...") |
| |
| response = requests.get(url, timeout=10) |
| |
| if response.status_code == 200: |
| data = response.json() |
| title = data.get('title', '') |
| |
| if title: |
| title = html_lib.unescape(title) |
| print(f"✅ عنوان از noembed: {title}") |
| return title |
| |
| except Exception as e: |
| print(f"❌ خطا در noembed: {str(e)}") |
| |
| return None |
|
|
| def get_video_title(youtube_url): |
| """دریافت عنوان ویدیو""" |
| print("=" * 60) |
| print("🎬 شروع استخراج عنوان ویدیو") |
| print("=" * 60) |
| |
| video_id = extract_youtube_video_id(youtube_url) |
| |
| if not video_id: |
| print("❌ نتوانستم Video ID را استخراج کنم") |
| return f"Video_{int(time.time())}" |
| |
| print(f"📝 Video ID: {video_id}") |
| |
| |
| title = get_title_from_youtube_oembed(video_id) |
| if title and len(title) > 5: |
| return sanitize_title(title) |
| |
| |
| title = get_title_from_noembed(video_id) |
| if title and len(title) > 5: |
| return sanitize_title(title) |
| |
| |
| fallback = f"YouTube_{video_id}" |
| print(f"⚠️ استفاده از عنوان پیشفرض: {fallback}") |
| return fallback |
|
|
| def sanitize_title(title): |
| """تمیز کردن عنوان""" |
| title = html_lib.unescape(title) |
| title = re.sub(r'\s*-\s*YouTube\s*$', '', title, flags=re.IGNORECASE) |
| |
| invalid_chars = '<>:"/\\|?*\n\r\t' |
| for char in invalid_chars: |
| title = title.replace(char, ' ') |
| |
| title = re.sub(r'\s+', ' ', title).strip() |
| |
| if len(title) > 100: |
| title = title[:97] + "..." |
| |
| print(f"🎬 عنوان نهایی: {title}") |
| return title |
|
|
| |
|
|
| def wait_for_download_link(download_id, max_attempts=25, wait_time=2): |
| """صبر کردن تا لینک دانلود آماده شود""" |
| |
| possible_urls = [ |
| f"https://p.savenow.to/download/{download_id}", |
| f"https://p.savenow.to/api/download/{download_id}", |
| f"https://p.lbserver.xyz/download/{download_id}", |
| ] |
| |
| headers = { |
| 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36', |
| 'Accept': 'video/mp4,video/*,*/*;q=0.8', |
| 'Referer': 'https://downloaderto.com/', |
| } |
| |
| print(f"⏳ شروع polling برای Download ID: {download_id}") |
| print(f"📊 حداکثر {max_attempts} تلاش، هر {wait_time} ثانیه یک بار") |
| |
| for attempt in range(1, max_attempts + 1): |
| print(f"\n🔄 تلاش {attempt}/{max_attempts}...") |
| |
| for url in possible_urls: |
| try: |
| response = requests.head(url, headers=headers, timeout=8, allow_redirects=True) |
| |
| if response.status_code in [200, 302, 307]: |
| final_url = response.url if response.history else url |
| file_size = response.headers.get('content-length', 0) |
| |
| if file_size: |
| size_bytes = int(file_size) |
| |
| if size_bytes > 500 * 1024: |
| |
| if size_bytes < 1024*1024: |
| size_info = f"{size_bytes/1024:.1f} KB" |
| elif size_bytes < 1024*1024*1024: |
| size_info = f"{size_bytes/(1024*1024):.1f} MB" |
| else: |
| size_info = f"{size_bytes/(1024*1024*1024):.2f} GB" |
| |
| print(f"✅ لینک آماده شد! حجم: {size_info}") |
| |
| return { |
| 'url': final_url, |
| 'size': size_info, |
| 'size_bytes': size_bytes, |
| 'attempts': attempt |
| } |
| |
| except Exception as e: |
| continue |
| |
| if attempt < max_attempts: |
| time.sleep(wait_time) |
| |
| print(f"❌ پس از {max_attempts} تلاش، لینک پیدا نشد") |
| return None |
|
|
| def download_file(download_url, filename, output_dir): |
| """دانلود فایل""" |
| |
| try: |
| headers = { |
| 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36', |
| 'Referer': 'https://downloaderto.com/', |
| } |
| |
| print(f"📥 شروع دانلود: {filename}") |
| |
| response = requests.get(download_url, headers=headers, stream=True, timeout=60) |
| response.raise_for_status() |
| |
| filepath = output_dir / filename |
| total_size = int(response.headers.get('content-length', 0)) |
| |
| with open(filepath, 'wb') as f: |
| downloaded = 0 |
| for chunk in response.iter_content(chunk_size=8192): |
| if chunk: |
| f.write(chunk) |
| downloaded += len(chunk) |
| |
| if total_size: |
| percent = (downloaded / total_size) * 100 |
| print(f"📥 {percent:.1f}%", end='\r') |
| |
| print(f"\n✅ دانلود کامل: {filepath.name}") |
| |
| actual_size = filepath.stat().st_size |
| if actual_size < 1024*1024: |
| size_display = f"{actual_size/1024:.1f} KB" |
| elif actual_size < 1024*1024*1024: |
| size_display = f"{actual_size/(1024*1024):.1f} MB" |
| else: |
| size_display = f"{actual_size/(1024*1024*1024):.2f} GB" |
| |
| return { |
| 'success': True, |
| 'filepath': str(filepath), |
| 'filename': filepath.name, |
| 'size': size_display |
| } |
| |
| except Exception as e: |
| print(f"❌ خطا در دانلود: {str(e)}") |
| return { |
| 'success': False, |
| 'error': str(e) |
| } |
|
|
| def cleanup_old_files(download_dir, max_files=5): |
| """پاکسازی فایلهای قدیمی""" |
| try: |
| files = list(download_dir.glob("*.mp4")) |
| files.sort(key=lambda x: x.stat().st_ctime, reverse=True) |
| |
| deleted = 0 |
| for filepath in files[max_files:]: |
| try: |
| filepath.unlink() |
| deleted += 1 |
| except: |
| pass |
| |
| if deleted > 0: |
| print(f"🧹 {deleted} فایل قدیمی حذف شد") |
| |
| except: |
| pass |
|
|
| |
|
|
| def download_youtube_video(youtube_url, quality, enable_preview): |
| """دانلود ویدیو یوتیوب با polling و استخراج خودکار API""" |
| |
| quality_map = { |
| "360p": "360", |
| "480p": "480", |
| "720p": "720", |
| "1080p": "1080", |
| "بهترین": "best" |
| } |
| |
| format_code = quality_map.get(quality, "720") |
| |
| |
| api_config = get_api_config(force_refresh=False) |
| api_key = api_config['api_key'] |
| api_url = api_config['api_url'] |
| |
| print(f"\n🔑 API Key: {api_key[:16]}...") |
| print(f"🌐 API URL: {api_url}") |
| |
| |
| video_title = get_video_title(youtube_url) |
| |
| |
| print("\n" + "=" * 60) |
| print("📤 درخواست به API downloaderto") |
| print("=" * 60) |
| |
| params = { |
| 'copyright': '0', |
| 'format': format_code, |
| 'url': youtube_url, |
| 'api': api_key |
| } |
| |
| headers = { |
| 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36', |
| 'Accept': 'application/json', |
| 'Referer': 'https://downloaderto.com/', |
| } |
| |
| try: |
| response = requests.get(api_url, params=params, headers=headers, timeout=30) |
| |
| if response.status_code != 200: |
| print(f"❌ API خطا: {response.status_code}") |
| print("🔄 تلاش برای بهروزرسانی API از سایت...") |
| |
| |
| new_config = get_api_config(force_refresh=True) |
| |
| if new_config and (new_config['api_key'] != api_key or new_config['api_url'] != api_url): |
| print("✅ API جدید پیدا شد! تلاش مجدد...") |
| |
| params['api'] = new_config['api_key'] |
| response = requests.get(new_config['api_url'], params=params, headers=headers, timeout=30) |
| |
| if response.status_code != 200: |
| return None, f"❌ خطای API حتی با تنظیمات جدید: {response.status_code}", None |
| else: |
| return None, f"❌ خطای API: {response.status_code}", None |
| |
| data = response.json() |
| |
| if not data.get('success'): |
| error_msg = data.get('error', 'نامشخص') |
| print(f"❌ API خطا: {error_msg}") |
| |
| |
| if 'api' in error_msg.lower() or 'key' in error_msg.lower() or 'auth' in error_msg.lower(): |
| print("🔄 بهروزرسانی API از سایت...") |
| new_config = get_api_config(force_refresh=True) |
| |
| if new_config: |
| params['api'] = new_config['api_key'] |
| response = requests.get(new_config['api_url'], params=params, headers=headers, timeout=30) |
| data = response.json() |
| |
| if not data.get('success'): |
| return None, f"❌ API خطا حتی با key جدید: {data.get('error', 'نامشخص')}", None |
| else: |
| return None, f"❌ API خطا: {error_msg}", None |
| else: |
| return None, f"❌ API خطا: {error_msg}", None |
| |
| download_id = data.get('id') |
| print(f"✅ Download ID: {download_id}") |
| |
| |
| print("\n" + "=" * 60) |
| print("⏳ در حال آمادهسازی فایل روی سرور...") |
| print("=" * 60) |
| |
| link_info = wait_for_download_link(download_id, max_attempts=25, wait_time=2) |
| |
| if not link_info: |
| manual_url = f"https://downloaderto.com/download/{download_id}" |
| return { |
| 'id': download_id, |
| 'title': video_title, |
| }, f"""⏱️ زمان انتظار تمام شد |
| |
| 📝 عنوان: {video_title} |
| ⚠️ فایل هنوز آماده نشده است |
| |
| 💡 راهحل: |
| 1. چند لحظه صبر کنید و دوباره تلاش کنید |
| 2. یا مستقیماً به این آدرس بروید: |
| {manual_url}""", None |
| |
| print(f"\n✅ فایل آماده شد بعد از {link_info['attempts']} تلاش") |
| |
| |
| print("\n" + "=" * 60) |
| print("💾 دانلود فایل") |
| print("=" * 60) |
| |
| download_dir = Path(tempfile.gettempdir()) / "youtube_downloads" |
| download_dir.mkdir(exist_ok=True) |
| |
| cleanup_old_files(download_dir, max_files=3) |
| |
| safe_title = re.sub(r'[<>:"/\\|?*]', '_', video_title) |
| filename = f"{safe_title}_{quality}.mp4" |
| |
| result = download_file(link_info['url'], filename, download_dir) |
| |
| preview_video = None |
| if enable_preview: |
| if result.get('success') and result.get('filepath'): |
| if os.path.exists(result['filepath']): |
| preview_video = result['filepath'] |
| else: |
| preview_video = link_info['url'] |
| else: |
| preview_video = link_info['url'] |
| |
| if result.get('success'): |
| info_text = f"""✅ دانلود موفق! |
| |
| 📝 عنوان: {video_title} |
| 📊 کیفیت: {quality} |
| 💾 حجم: {result['size']} |
| 📂 نام فایل: {result['filename']} |
| ⏱️ آماده شد بعد از: {link_info['attempts']} تلاش |
| |
| 🔗 لینک اصلی: {link_info['url']}""" |
| |
| return { |
| 'title': video_title, |
| 'file': result['filepath'], |
| 'filename': result['filename'], |
| 'size': result['size'], |
| 'url': link_info['url'] |
| }, info_text, preview_video |
| |
| else: |
| info_text = f"""⚠️ دانلود خودکار ناموفق بود |
| |
| 📝 عنوان: {video_title} |
| 📊 کیفیت: {quality} |
| 💾 حجم: {link_info['size']} |
| ⏱️ آماده شد بعد از: {link_info['attempts']} تلاش |
| |
| 🔗 لینک مستقیم: {link_info['url']} |
| 💡 لینک را کپی کرده و در مرورگر باز کنید""" |
| |
| return { |
| 'title': video_title, |
| 'size': link_info['size'], |
| 'url': link_info['url'] |
| }, info_text, preview_video |
| |
| except Exception as e: |
| return None, f"❌ خطا: {str(e)}", None |
|
|
| |
|
|
| with gr.Blocks(title="YouTube Downloader") as demo: |
| gr.Markdown("# 🎬 YouTube Video Downloader") |
| gr.Markdown("### دانلود هوشمند با استخراج خودکار API") |
| |
| with gr.Row(): |
| with gr.Column(scale=1): |
| url_input = gr.Textbox( |
| label="🔗 لینک یوتیوب", |
| placeholder="https://www.youtube.com/watch?v=...", |
| lines=2 |
| ) |
| |
| quality_select = gr.Dropdown( |
| label="📊 کیفیت", |
| choices=["360p", "480p", "720p", "1080p", "بهترین"], |
| value="720p" |
| ) |
| |
| preview_checkbox = gr.Checkbox( |
| label="🎥 نمایش پیشنمایش ویدیو", |
| value=False, |
| info="فعال کردن نمایش ویدیو (ممکن است کند شود)" |
| ) |
| |
| download_btn = gr.Button("⬇️ دانلود", variant="primary", size="lg") |
| |
| refresh_api_btn = gr.Button("🔄 بهروزرسانی API از سایت", variant="secondary") |
| |
| gr.Markdown(""" |
| ### ✨ ویژگیهای جدید: |
| - 🤖 **استخراج خودکار API** |
| - 🔄 **بهروزرسانی هوشمند** |
| - ⏳ **Polling تا آماده شدن** |
| - 🎬 **عنوان خودکار** |
| - 💾 **Cache تنظیمات** |
| |
| ### 🔧 نحوه کار: |
| 1. برنامه API را از سایت استخراج میکند |
| 2. اگر API خطا داد، خودکار بهروزرسانی میکند |
| 3. تنظیمات را 24 ساعت cache میکند |
| """) |
| |
| with gr.Column(scale=2): |
| info_output = gr.Textbox( |
| label="📋 اطلاعات و پیشرفت", |
| lines=15, |
| interactive=False |
| ) |
| |
| with gr.Row(): |
| size_output = gr.Textbox(label="💾 حجم", scale=1) |
| status_output = gr.Textbox(label="✅ وضعیت", scale=1) |
| |
| link_output = gr.Textbox( |
| label="🔗 لینک دانلود مستقیم", |
| interactive=True |
| ) |
| |
| file_output = gr.File(label="📁 فایل دانلود شده") |
| |
| video_output = gr.Video( |
| label="🎥 پیشنمایش", |
| height=400, |
| visible=True |
| ) |
| |
| |
| gr.Markdown("### 🧪 تست سریع") |
| with gr.Row(): |
| test1_btn = gr.Button("تست ویدیو کوتاه", variant="secondary") |
| test2_btn = gr.Button("تست YouTube Short", variant="secondary") |
| |
| @download_btn.click( |
| inputs=[url_input, quality_select, preview_checkbox], |
| outputs=[info_output, size_output, status_output, link_output, file_output, video_output] |
| ) |
| def handle_download(url, quality, enable_preview): |
| if not url: |
| return "❌ لطفاً لینک یوتیوب را وارد کنید", "", "❌ خطا", "", None, None |
| |
| yield "🚀 شروع پردازش...\n⏳ لطفاً صبر کنید...", "", "⏳ در حال پردازش", "", None, None |
| |
| result, info, preview = download_youtube_video(url, quality, enable_preview) |
| |
| if not result: |
| yield info, "", "❌ خطا", "", None, None |
| return |
| |
| size = result.get('size', '') |
| link = result.get('url', '') |
| file = result.get('file') |
| |
| if file and os.path.exists(file): |
| status = "✅ موفق" |
| else: |
| status = "⚠️ فقط لینک" |
| |
| yield info, size, status, link, file, preview |
| |
| @refresh_api_btn.click(outputs=[info_output]) |
| def refresh_api(): |
| result = "🔄 شروع بهروزرسانی API...\n\n" |
| config = get_api_config(force_refresh=True) |
| |
| if config: |
| result += f"✅ API با موفقیت بهروزرسانی شد!\n\n" |
| result += f"🔑 API Key: {config['api_key'][:16]}...\n" |
| result += f"🌐 API URL: {config['api_url']}\n" |
| result += f"⏰ زمان: {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(config['timestamp']))}" |
| else: |
| result += "❌ خطا در بهروزرسانی API" |
| |
| return result |
| |
| @test1_btn.click(outputs=[url_input, quality_select, preview_checkbox]) |
| def test1(): |
| return "https://www.youtube.com/watch?v=jNQXAC9IVRw", "480p", False |
| |
| @test2_btn.click(outputs=[url_input, quality_select, preview_checkbox]) |
| def test2(): |
| return "https://www.youtube.com/shorts/cPuS6WPZjWI", "720p", False |
| |
| |
| with gr.Accordion("📖 راهنمای استفاده", open=False): |
| gr.Markdown(""" |
| ### استخراج خودکار API: |
| |
| - ✅ برنامه خودش API را از سایت downloaderto میخواند |
| - ✅ اگر API عوض شد، خودکار آن را پیدا میکند |
| - ✅ تنظیمات را 24 ساعت نگه میدارد |
| - ✅ در صورت خطا، بهروزرسانی میکند |
| |
| ### دکمه "بهروزرسانی API": |
| |
| اگر مشکلی پیش آمد، روی این دکمه کلیک کنید تا: |
| - API جدید را از سایت بخواند |
| - Cache قدیمی را پاک کند |
| - تنظیمات جدید را نمایش دهد |
| |
| ### نکات: |
| |
| - برنامه هوشمند است و خودش مشکلات را حل میکند |
| - نیازی به تنظیم دستی نیست |
| - همه چیز خودکار است! |
| """) |
|
|
| if __name__ == "__main__": |
| demo.launch( |
| server_name="0.0.0.0", |
| server_port=7860, |
| show_error=True, |
| theme=gr.themes.Soft() |
| ) |