# 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) @app.route("/", methods=["GET"]) 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" } }) @app.route("/download", methods=["POST"]) 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 @app.route("/ping", methods=["GET"]) 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)})