Shirpi commited on
Commit
8cec65f
·
verified ·
1 Parent(s): 8bab098

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +131 -37
app.py CHANGED
@@ -1,9 +1,71 @@
1
- from flask import Flask, jsonify, request
2
  import yt_dlp
3
  import os
 
 
4
 
5
  app = Flask(__name__)
6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
  @app.route('/audio', methods=['GET'])
8
  def get_audio():
9
  query = request.args.get('q', '')
@@ -11,50 +73,82 @@ def get_audio():
11
  return jsonify({'error': 'No query'}), 400
12
 
13
  try:
14
- ydl_opts = {
15
- 'format': 'bestaudio/best',
16
- 'quiet': True,
17
- 'no_warnings': True,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
 
20
- with yt_dlp.YoutubeDL(ydl_opts) as ydl:
21
- info = ydl.extract_info(
22
- f'ytsearch1:{query}',
23
- download=False
24
- )
25
- if 'entries' in info:
26
- video = info['entries'][0]
27
- else:
28
- video = info
29
-
30
- formats = video.get('formats', [])
31
- audio_formats = [
32
- f for f in formats
33
- if f.get('acodec') != 'none'
34
- and f.get('vcodec') == 'none'
35
- ]
36
-
37
- if not audio_formats:
38
- return jsonify({'error': 'No audio'}), 404
39
-
40
- best = max(
41
- audio_formats,
42
- key=lambda f: f.get('abr', 0) or 0
43
- )
44
-
45
- return jsonify({
46
- 'url': best['url'],
47
- 'title': video.get('title', ''),
48
- 'bitrate': int(best.get('abr', 0) or 0),
49
- })
50
 
51
  except Exception as e:
52
  return jsonify({'error': str(e)}), 500
53
 
54
  @app.route('/health', methods=['GET'])
55
  def health():
56
- return jsonify({'status': 'ok'})
57
 
58
  if __name__ == '__main__':
59
  port = int(os.environ.get('PORT', 7860))
60
- app.run(host='0.0.0.0', port=port)
 
1
+ from flask import Flask, jsonify, request, send_file, Response
2
  import yt_dlp
3
  import os
4
+ import tempfile
5
+ import threading
6
 
7
  app = Flask(__name__)
8
 
9
+ # Cache — same song again search பண்ணா fast!
10
+ _cache = {}
11
+ _cache_lock = threading.Lock()
12
+
13
+ def _get_audio_url(query):
14
+ with _cache_lock:
15
+ if query in _cache:
16
+ return _cache[query]
17
+
18
+ ydl_opts = {
19
+ 'format': 'bestaudio[ext=m4a]/bestaudio/best',
20
+ 'quiet': True,
21
+ 'no_warnings': True,
22
+ 'socket_timeout': 10,
23
+ }
24
+
25
+ with yt_dlp.YoutubeDL(ydl_opts) as ydl:
26
+ info = ydl.extract_info(
27
+ f'ytsearch1:{query}',
28
+ download=False
29
+ )
30
+ if 'entries' in info and info['entries']:
31
+ video = info['entries'][0]
32
+ else:
33
+ video = info
34
+
35
+ formats = video.get('formats', [])
36
+
37
+ # m4a format prefer பண்றோம் — faster!
38
+ m4a = [f for f in formats
39
+ if f.get('ext') == 'm4a'
40
+ and f.get('acodec') != 'none'
41
+ and f.get('vcodec') == 'none']
42
+
43
+ if m4a:
44
+ best = max(m4a, key=lambda f: f.get('abr', 0) or 0)
45
+ else:
46
+ audio = [f for f in formats
47
+ if f.get('acodec') != 'none'
48
+ and f.get('vcodec') == 'none']
49
+ if not audio:
50
+ return None
51
+ best = max(audio, key=lambda f: f.get('abr', 0) or 0)
52
+
53
+ result = {
54
+ 'url': best['url'],
55
+ 'title': video.get('title', ''),
56
+ 'bitrate': int(best.get('abr', 0) or 128),
57
+ 'ext': best.get('ext', 'm4a'),
58
+ }
59
+
60
+ with _cache_lock:
61
+ _cache[query] = result
62
+ # Cache 50 songs மட்டும்
63
+ if len(_cache) > 50:
64
+ oldest = next(iter(_cache))
65
+ del _cache[oldest]
66
+
67
+ return result
68
+
69
  @app.route('/audio', methods=['GET'])
70
  def get_audio():
71
  query = request.args.get('q', '')
 
73
  return jsonify({'error': 'No query'}), 400
74
 
75
  try:
76
+ result = _get_audio_url(query)
77
+ if not result:
78
+ return jsonify({'error': 'Not found'}), 404
79
+ return jsonify(result)
80
+ except Exception as e:
81
+ return jsonify({'error': str(e)}), 500
82
+
83
+ @app.route('/stream', methods=['GET'])
84
+ def stream_audio():
85
+ query = request.args.get('q', '')
86
+ if not query:
87
+ return jsonify({'error': 'No query'}), 400
88
+
89
+ try:
90
+ result = _get_audio_url(query)
91
+ if not result:
92
+ return jsonify({'error': 'Not found'}), 404
93
+
94
+ import requests as req
95
+
96
+ # Range header support — resume download!
97
+ range_header = request.headers.get('Range', '')
98
+ headers = {
99
+ 'User-Agent': 'Mozilla/5.0 (Linux; Android 13; Pixel 7) AppleWebKit/537.36',
100
+ 'Accept': '*/*',
101
+ 'Accept-Encoding': 'identity',
102
+ 'Referer': 'https://www.youtube.com/',
103
  }
104
+ if range_header:
105
+ headers['Range'] = range_header
106
+
107
+ yt_resp = req.get(
108
+ result['url'],
109
+ headers=headers,
110
+ stream=True,
111
+ timeout=30,
112
+ )
113
+
114
+ ext = result.get('ext', 'm4a')
115
+ content_type = 'audio/mp4' if ext == 'm4a' else 'audio/mpeg'
116
+
117
+ response_headers = {
118
+ 'Content-Type': content_type,
119
+ 'Accept-Ranges': 'bytes',
120
+ 'X-Song-Title': result.get('title', ''),
121
+ 'X-Bitrate': str(result.get('bitrate', 128)),
122
+ }
123
+
124
+ if 'Content-Length' in yt_resp.headers:
125
+ response_headers['Content-Length'] = \
126
+ yt_resp.headers['Content-Length']
127
+
128
+ if 'Content-Range' in yt_resp.headers:
129
+ response_headers['Content-Range'] = \
130
+ yt_resp.headers['Content-Range']
131
+
132
+ def generate():
133
+ for chunk in yt_resp.iter_content(chunk_size=65536):
134
+ if chunk:
135
+ yield chunk
136
+
137
+ status = yt_resp.status_code
138
 
139
+ return Response(
140
+ generate(),
141
+ status=status,
142
+ headers=response_headers,
143
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
144
 
145
  except Exception as e:
146
  return jsonify({'error': str(e)}), 500
147
 
148
  @app.route('/health', methods=['GET'])
149
  def health():
150
+ return jsonify({'status': 'ok', 'cache': len(_cache)})
151
 
152
  if __name__ == '__main__':
153
  port = int(os.environ.get('PORT', 7860))
154
+ app.run(host='0.0.0.0', port=port, threaded=True)