Spaces:
Sleeping
Sleeping
| # app.py - YouTube Downloader API (No UI) | |
| from flask import Flask, request, send_file, jsonify | |
| import yt_dlp | |
| import os | |
| import glob | |
| import uuid | |
| import traceback | |
| app = Flask(__name__) | |
| # Use /tmp (only writable directory on Hugging Face) | |
| DOWNLOAD_FOLDER = "/tmp/downloads" | |
| os.makedirs(DOWNLOAD_FOLDER, exist_ok=True) | |
| def info(): | |
| return jsonify({ | |
| "api": "/download", | |
| "method": "POST", | |
| "body": {"url": "YouTube_URL", "format": "mp3 or mp4"}, | |
| "example": { | |
| "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ", | |
| "format": "mp3" | |
| } | |
| }) | |
| def download(): | |
| data = request.get_json() | |
| url = data.get("url") | |
| fmt = (data.get("format") or "").lower().strip() | |
| if not url: | |
| return jsonify({"error": "Missing 'url' in request"}), 400 | |
| if fmt not in ["mp3", "mp4"]: | |
| return jsonify({"error": "Format must be 'mp3' or 'mp4'"}), 400 | |
| job_id = str(uuid.uuid4())[:8] | |
| # Robust yt-dlp options with retries and delays | |
| ydl_opts = { | |
| 'format': 'bestaudio/best' if fmt == 'mp3' else 'bestvideo+bestaudio/best', | |
| 'outtmpl': os.path.join(DOWNLOAD_FOLDER, f'{job_id}_%(title)s.%(ext)s'), | |
| 'merge_output_format': 'mp4' if fmt == 'mp4' else None, | |
| 'noplaylist': True, | |
| 'quiet': True, | |
| 'retries': 6, | |
| 'fragment_retries': 10, | |
| 'socket_timeout': 15, | |
| 'http_headers': { | |
| 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 ' | |
| '(KHTML, like Gecko) Chrome/120.0 Safari/537.36' | |
| }, | |
| 'sleep_interval': 5, | |
| 'max_sleep_interval': 15, | |
| 'source_address': None, | |
| 'extractor_retries': 3, | |
| } | |
| try: | |
| with yt_dlp.YoutubeDL(ydl_opts) as ydl: | |
| info = ydl.extract_info(url, download=True) | |
| title = info.get("title", "media").replace("/", "_").replace("\\", "_") | |
| ext = "mp3" if fmt == "mp3" else "mp4" | |
| pattern = os.path.join(DOWNLOAD_FOLDER, f"{job_id}_*.{ext}") | |
| matches = glob.glob(pattern) | |
| if not matches: | |
| return jsonify({"error": "Download completed but file not found."}), 500 | |
| return send_file( | |
| matches[0], | |
| as_attachment=True, | |
| download_name=f"{title}.{ext}" | |
| ) | |
| except Exception as e: | |
| tb = ''.join(traceback.format_exception(None, e, e.__traceback__)) | |
| return jsonify({ | |
| "error": str(e), | |
| "details": tb.splitlines()[-1] # Show last line of error | |
| }), 500 | |
| # Optional: Health check route | |
| def ping(): | |
| try: | |
| import socket | |
| socket.gethostbyname("www.youtube.com") | |
| return jsonify({"status": "ok", "dns": "resolved"}) | |
| except Exception as e: | |
| return jsonify({"status": "fail", "dns_error": str(e)}) |