Athspi commited on
Commit
755f7fe
·
verified ·
1 Parent(s): 8bb0c3a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +60 -52
app.py CHANGED
@@ -1,4 +1,4 @@
1
- # app.py
2
  from flask import Flask, request, send_file, jsonify
3
  import yt_dlp
4
  import os
@@ -8,82 +8,90 @@ import traceback
8
 
9
  app = Flask(__name__)
10
 
11
- DOWNLOAD_FOLDER = '/tmp/downloads'
 
12
  os.makedirs(DOWNLOAD_FOLDER, exist_ok=True)
13
 
14
- @app.route('/')
15
- def index():
 
16
  return jsonify({
17
- "message": "YouTube Downloader API",
18
- "usage": {
19
- "endpoint": "/download",
20
- "method": "POST",
21
- "json_body": {
22
- "url": "YouTube_URL",
23
- "format": "mp3 or mp4"
24
- }
25
  }
26
  })
27
 
28
- @app.route('/download', methods=['POST'])
29
- def download_video():
 
30
  data = request.get_json()
31
 
32
- url = data.get('url')
33
- fmt = data.get('format', 'mp3').lower()
34
 
35
  if not url:
36
- return jsonify({"error": "URL is required"}), 400
37
-
38
- if fmt not in ['mp3', 'mp4']:
39
  return jsonify({"error": "Format must be 'mp3' or 'mp4'"}), 400
40
 
41
  job_id = str(uuid.uuid4())[:8]
42
 
43
- if fmt == 'mp3':
44
- ydl_opts = {
45
- 'format': 'bestaudio/best',
46
- 'outtmpl': os.path.join(DOWNLOAD_FOLDER, f'{job_id}_%(title)s.%(ext)s'),
47
- 'postprocessors': [{
48
- 'key': 'FFmpegExtractAudio',
49
- 'preferredcodec': 'mp3',
50
- 'preferredquality': '192',
51
- }],
52
- 'quiet': True,
53
- 'noplaylist': True,
54
- }
55
- else:
56
- ydl_opts = {
57
- 'format': 'bestvideo+bestaudio/best',
58
- 'outtmpl': os.path.join(DOWNLOAD_FOLDER, f'{job_id}_%(title)s.%(ext)s'),
59
- 'merge_output_format': 'mp4',
60
- 'quiet': True,
61
- 'noplaylist': True,
62
- }
63
 
64
  try:
65
  with yt_dlp.YoutubeDL(ydl_opts) as ydl:
66
  info = ydl.extract_info(url, download=True)
67
- title = info.get('title', 'downloaded_media')
68
- ext = 'mp3' if fmt == 'mp3' else 'mp4'
 
 
 
69
 
70
- search_pattern = os.path.join(DOWNLOAD_FOLDER, f"{job_id}_*.{ext}")
71
- matching_files = glob.glob(search_pattern)
72
- if not matching_files:
73
- return jsonify({"error": "File not found after download."}), 500
74
 
75
- file_path = matching_files[0]
76
  return send_file(
77
- file_path,
78
  as_attachment=True,
79
- download_name=f"{title}.{ext}",
80
- mimetype='audio/mpeg' if fmt == 'mp3' else 'video/mp4'
81
  )
82
 
83
  except Exception as e:
84
  tb = ''.join(traceback.format_exception(None, e, e.__traceback__))
85
- return jsonify({"error": str(e), "traceback": tb}), 500
 
 
 
86
 
87
 
88
- if __name__ == '__main__':
89
- app.run(host='0.0.0.0', port=7860)
 
 
 
 
 
 
 
 
1
+ # app.py - YouTube Downloader API (No UI)
2
  from flask import Flask, request, send_file, jsonify
3
  import yt_dlp
4
  import os
 
8
 
9
  app = Flask(__name__)
10
 
11
+ # Use /tmp (only writable directory on Hugging Face)
12
+ DOWNLOAD_FOLDER = "/tmp/downloads"
13
  os.makedirs(DOWNLOAD_FOLDER, exist_ok=True)
14
 
15
+
16
+ @app.route("/", methods=["GET"])
17
+ def info():
18
  return jsonify({
19
+ "api": "/download",
20
+ "method": "POST",
21
+ "body": {"url": "YouTube_URL", "format": "mp3 or mp4"},
22
+ "example": {
23
+ "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
24
+ "format": "mp3"
 
 
25
  }
26
  })
27
 
28
+
29
+ @app.route("/download", methods=["POST"])
30
+ def download():
31
  data = request.get_json()
32
 
33
+ url = data.get("url")
34
+ fmt = (data.get("format") or "").lower().strip()
35
 
36
  if not url:
37
+ return jsonify({"error": "Missing 'url' in request"}), 400
38
+ if fmt not in ["mp3", "mp4"]:
 
39
  return jsonify({"error": "Format must be 'mp3' or 'mp4'"}), 400
40
 
41
  job_id = str(uuid.uuid4())[:8]
42
 
43
+ # Robust yt-dlp options with retries and delays
44
+ ydl_opts = {
45
+ 'format': 'bestaudio/best' if fmt == 'mp3' else 'bestvideo+bestaudio/best',
46
+ 'outtmpl': os.path.join(DOWNLOAD_FOLDER, f'{job_id}_%(title)s.%(ext)s'),
47
+ 'merge_output_format': 'mp4' if fmt == 'mp4' else None,
48
+ 'noplaylist': True,
49
+ 'quiet': True,
50
+ 'retries': 6,
51
+ 'fragment_retries': 10,
52
+ 'socket_timeout': 15,
53
+ 'http_headers': {
54
+ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 '
55
+ '(KHTML, like Gecko) Chrome/120.0 Safari/537.36'
56
+ },
57
+ 'sleep_interval': 5,
58
+ 'max_sleep_interval': 15,
59
+ 'source_address': None,
60
+ 'extractor_retries': 3,
61
+ }
 
62
 
63
  try:
64
  with yt_dlp.YoutubeDL(ydl_opts) as ydl:
65
  info = ydl.extract_info(url, download=True)
66
+ title = info.get("title", "media").replace("/", "_").replace("\\", "_")
67
+ ext = "mp3" if fmt == "mp3" else "mp4"
68
+
69
+ pattern = os.path.join(DOWNLOAD_FOLDER, f"{job_id}_*.{ext}")
70
+ matches = glob.glob(pattern)
71
 
72
+ if not matches:
73
+ return jsonify({"error": "Download completed but file not found."}), 500
 
 
74
 
 
75
  return send_file(
76
+ matches[0],
77
  as_attachment=True,
78
+ download_name=f"{title}.{ext}"
 
79
  )
80
 
81
  except Exception as e:
82
  tb = ''.join(traceback.format_exception(None, e, e.__traceback__))
83
+ return jsonify({
84
+ "error": str(e),
85
+ "details": tb.splitlines()[-1] # Show last line of error
86
+ }), 500
87
 
88
 
89
+ # Optional: Health check route
90
+ @app.route("/ping", methods=["GET"])
91
+ def ping():
92
+ try:
93
+ import socket
94
+ socket.gethostbyname("www.youtube.com")
95
+ return jsonify({"status": "ok", "dns": "resolved"})
96
+ except Exception as e:
97
+ return jsonify({"status": "fail", "dns_error": str(e)})