Spaces:
Sleeping
Sleeping
| import os | |
| import time | |
| import uuid | |
| import threading | |
| from flask import Flask, request, jsonify, send_file, render_template_string | |
| from flask_cors import CORS | |
| import yt_dlp | |
| app = Flask(__name__) | |
| CORS(app) | |
| DOWNLOAD_FOLDER = 'downloads' | |
| os.makedirs(DOWNLOAD_FOLDER, exist_ok=True) | |
| # ========================================== | |
| # سیستم پاکسازی خودکار فایلهای قدیمی | |
| # ========================================== | |
| def cleanup_old_files(): | |
| """این تابع هر یک ساعت اجرا میشود و فایلهای قدیمیتر از 1 ساعت را پاک میکند تا حافظه پر نشود""" | |
| while True: | |
| try: | |
| current_time = time.time() | |
| for filename in os.listdir(DOWNLOAD_FOLDER): | |
| file_path = os.path.join(DOWNLOAD_FOLDER, filename) | |
| if os.path.isfile(file_path): | |
| # اگر فایل قدیمیتر از 3600 ثانیه (1 ساعت) بود، حذف شود | |
| if current_time - os.path.getctime(file_path) > 3600: | |
| os.remove(file_path) | |
| except Exception as e: | |
| print(f"Cleanup error: {e}") | |
| time.sleep(3600) | |
| # راهاندازی نخ پاکسازی در پسزمینه | |
| cleanup_thread = threading.Thread(target=cleanup_old_files, daemon=True) | |
| cleanup_thread.start() | |
| # ========================================== | |
| # رابط کاربری ساده برای تست (رابط وب) | |
| # ========================================== | |
| HTML_TEMPLATE = """ | |
| <!DOCTYPE html> | |
| <html lang="fa" dir="rtl"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>دانلودر اختصاصی ویدیو</title> | |
| <style> | |
| body { font-family: Tahoma, Arial, sans-serif; background-color: #f3f4f6; display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; } | |
| .container { background: #fff; padding: 30px; border-radius: 12px; box-shadow: 0 4px 6px rgba(0,0,0,0.1); max-width: 500px; width: 100%; text-align: center; } | |
| input { width: 90%; padding: 12px; margin: 15px 0; border: 1px solid #ccc; border-radius: 8px; font-size: 16px; direction: ltr; } | |
| button { background: #3b82f6; color: white; border: none; padding: 12px 24px; font-size: 16px; border-radius: 8px; cursor: pointer; transition: 0.3s; width: 100%; font-weight: bold; } | |
| button:hover { background: #2563eb; } | |
| button:disabled { background: #9ca3af; cursor: not-allowed; } | |
| #status { margin-top: 15px; font-size: 14px; color: #4b5563; } | |
| .video-container { margin-top: 20px; display: none; } | |
| video { width: 100%; border-radius: 8px; } | |
| .dl-btn { background: #10b981; margin-top: 10px; display: inline-block; text-decoration: none; padding: 10px 20px; color: white; border-radius: 8px; } | |
| .dl-btn:hover { background: #059669; } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="container"> | |
| <h2>📥 دانلودر ویدیو (YouTube / Instagram)</h2> | |
| <p>لینک ویدیو را در کادر زیر قرار دهید:</p> | |
| <input type="text" id="urlInput" placeholder="https://www.youtube.com/watch?v=..."> | |
| <button id="downloadBtn" onclick="startDownload()">دریافت ویدیو</button> | |
| <div id="status"></div> | |
| <div class="video-container" id="videoContainer"> | |
| <video id="videoPlayer" controls></video> | |
| <br> | |
| <a id="downloadLink" class="dl-btn" href="#" download>ذخیره ویدیو در دستگاه</a> | |
| </div> | |
| </div> | |
| <script> | |
| async function startDownload() { | |
| const url = document.getElementById('urlInput').value; | |
| const btn = document.getElementById('downloadBtn'); | |
| const status = document.getElementById('status'); | |
| const videoContainer = document.getElementById('videoContainer'); | |
| const videoPlayer = document.getElementById('videoPlayer'); | |
| const downloadLink = document.getElementById('downloadLink'); | |
| if (!url) return alert('لطفا لینک را وارد کنید'); | |
| btn.disabled = true; | |
| status.innerHTML = "⏳ در حال استخراج و دانلود ویدیو از سرور اصلی... لطفاً صبور باشید."; | |
| videoContainer.style.display = 'none'; | |
| try { | |
| const response = await fetch('/api/download', { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify({ url: url }) | |
| }); | |
| if (!response.ok) { | |
| const data = await response.json(); | |
| throw new Error(data.error || 'خطا در دانلود ویدیو'); | |
| } | |
| // دریافت فایل به صورت Blob | |
| const blob = await response.blob(); | |
| const videoUrl = URL.createObjectURL(blob); | |
| videoPlayer.src = videoUrl; | |
| downloadLink.href = videoUrl; | |
| downloadLink.download = "video.mp4"; | |
| status.innerHTML = "✅ ویدیو با موفقیت دریافت شد!"; | |
| videoContainer.style.display = 'block'; | |
| } catch (error) { | |
| status.innerHTML = "❌ خطا: " + error.message; | |
| } finally { | |
| btn.disabled = false; | |
| } | |
| } | |
| </script> | |
| </body> | |
| </html> | |
| """ | |
| def index(): | |
| return render_template_string(HTML_TEMPLATE) | |
| # ========================================== | |
| # API دانلود ویدیو | |
| # ========================================== | |
| def download_video(): | |
| data = request.get_json() | |
| if not data or 'url' not in data: | |
| return jsonify({'error': 'URL is required'}), 400 | |
| url = data['url'] | |
| # شناسه یکتا برای فایل خروجی | |
| file_id = str(uuid.uuid4()) | |
| output_template = os.path.join(DOWNLOAD_FOLDER, f"{file_id}.%(ext)s") | |
| # تنظیمات yt-dlp همراه با سیستم بایپس یوتیوب | |
| ydl_opts = { | |
| 'format': 'bestvideo[ext=mp4]+bestaudio[ext=m4a]/best[ext=mp4]/best', | |
| 'outtmpl': output_template, | |
| 'quiet': True, | |
| 'no_warnings': True, | |
| 'merge_output_format': 'mp4', | |
| # 1. آدرس فایل کوکی که باید در کنار app.py آپلود شود | |
| 'cookiefile': 'cookies.txt', | |
| # 2. تغییر کلاینت به موبایل/وب برای کاهش حساسیت رباتهای یوتیوب | |
| 'extractor_args': {'youtube': {'player_client': ['android', 'web']}}, | |
| } | |
| try: | |
| with yt_dlp.YoutubeDL(ydl_opts) as ydl: | |
| # استخراج اطلاعات و دانلود | |
| info_dict = ydl.extract_info(url, download=True) | |
| downloaded_ext = info_dict.get('ext', 'mp4') | |
| final_filename = f"{file_id}.{downloaded_ext}" | |
| if not os.path.exists(os.path.join(DOWNLOAD_FOLDER, final_filename)): | |
| final_filename = f"{file_id}.mp4" | |
| final_path = os.path.join(DOWNLOAD_FOLDER, final_filename) | |
| if os.path.exists(final_path): | |
| return send_file(final_path, as_attachment=True, download_name=f"video_{int(time.time())}.mp4") | |
| else: | |
| return jsonify({'error': 'فایل پس از دانلود یافت نشد.'}), 500 | |
| except Exception as e: | |
| error_msg = str(e) | |
| if "Sign in to confirm" in error_msg or "Private video" in error_msg: | |
| return jsonify({'error': 'آیپی سرور مسدود شده است. لطفا مطمئن شوید فایل cookies.txt را در روت پروژه آپلود کردهاید.'}), 403 | |
| return jsonify({'error': f'خطا در دانلود: {error_msg}'}), 500 | |
| if __name__ == '__main__': | |
| port = int(os.environ.get('PORT', 7860)) | |
| app.run(host='0.0.0.0', port=port) | |