Tt / app.py
Athspi's picture
Update app.py
755f7fe verified
# 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)})