Offex commited on
Commit
a4ebadd
·
verified ·
1 Parent(s): 9086879

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +40 -182
app.py CHANGED
@@ -2,11 +2,10 @@ from flask import Flask, render_template_string, request, send_file, after_this_
2
  import yt_dlp
3
  import os
4
  import uuid
5
- import re
6
 
7
  app = Flask(__name__)
8
 
9
- # --- UNIVERSAL DOWNLOADER UI ---
10
  HTML_CODE = """
11
  <!DOCTYPE html>
12
  <html lang="en">
@@ -17,148 +16,43 @@ HTML_CODE = """
17
  <script src="https://cdn.tailwindcss.com"></script>
18
  <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
19
  <style>
20
- @import url('https://fonts.googleapis.com/css2?family=Outfit:wght@300;500;700&display=swap');
21
-
22
- body { font-family: 'Outfit', sans-serif; background-color: #000; overflow-x: hidden; }
23
-
24
- .bg-animate {
25
- position: fixed; top: 0; left: 0; width: 100%; height: 100%; z-index: -1;
26
- background: linear-gradient(-45deg, #ff0000, #2b2b2b, #00f2ea, #000000);
27
- background-size: 400% 400%;
28
- animation: gradientBG 15s ease infinite;
29
- opacity: 0.3;
30
- }
31
- @keyframes gradientBG {
32
- 0% { background-position: 0% 50%; }
33
- 50% { background-position: 100% 50%; }
34
- 100% { background-position: 0% 50%; }
35
- }
36
-
37
- .glass {
38
- background: rgba(15, 15, 15, 0.8);
39
- backdrop-filter: blur(20px);
40
- border: 1px solid rgba(255, 255, 255, 0.1);
41
- box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.5);
42
- }
43
-
44
- .input-glow:focus {
45
- box-shadow: 0 0 25px rgba(255, 0, 0, 0.4);
46
- border-color: #ff0000;
47
- }
48
-
49
- .btn-grad {
50
- background-image: linear-gradient(to right, #E50914 0%, #DB4437 51%, #E50914 100%);
51
- transition: 0.5s;
52
- background-size: 200% auto;
53
- }
54
- .btn-grad:hover { background-position: right center; transform: scale(1.02); }
55
-
56
- .loader {
57
- border: 3px solid rgba(255,255,255,0.3);
58
- border-radius: 50%;
59
- border-top: 3px solid #fff;
60
- width: 20px; height: 20px;
61
- animation: spin 1s linear infinite;
62
- display: none;
63
- }
64
  @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
65
-
66
- .fade-in-up { animation: fadeInUp 0.5s ease-out forwards; opacity: 0; transform: translateY(20px); }
67
- @keyframes fadeInUp { to { opacity: 1; transform: translateY(0); } }
68
  </style>
69
  </head>
70
- <body class="text-white flex flex-col items-center justify-center min-h-screen p-4">
71
-
72
- <div class="bg-animate"></div>
73
-
74
- <div class="glass w-full max-w-lg rounded-3xl p-8 relative overflow-hidden">
75
-
76
- <div class="absolute top-4 right-4 flex gap-3 opacity-50">
77
- <i class="fa-brands fa-youtube text-2xl text-red-500"></i>
78
- <i class="fa-brands fa-tiktok text-2xl text-pink-500"></i>
79
- <i class="fa-brands fa-instagram text-2xl text-purple-500"></i>
80
- </div>
81
-
82
- <div class="text-center mb-8 relative z-10">
83
- <h1 class="text-4xl font-bold mb-1 tracking-tight">
84
- <span class="text-white">Uni</span><span class="text-red-500">Loader</span>
85
- </h1>
86
- <p class="text-gray-400 text-sm">YouTube • TikTok • Instagram • Shorts</p>
87
- </div>
88
-
89
- <form method="POST" action="/fetch" onsubmit="showLoader()" class="space-y-5 relative z-10">
90
- <div class="relative">
91
- <i class="fa-solid fa-link absolute left-4 top-4 text-gray-400"></i>
92
- <input type="text" name="url"
93
- class="input-glow w-full bg-black/50 border border-gray-700 rounded-xl py-3.5 pl-12 pr-4 text-white placeholder-gray-500 focus:outline-none transition-all duration-300"
94
- placeholder="Paste YouTube or TikTok Link..." required>
95
- </div>
96
-
97
- <button type="submit" id="searchBtn" class="btn-grad w-full py-4 rounded-xl font-bold text-lg shadow-lg flex justify-center items-center gap-2">
98
- <span>FETCH VIDEO</span>
99
- <div class="loader" id="spinner"></div>
100
  </button>
101
  </form>
102
 
103
- {% if title %}
104
- <div class="mt-6 p-4 bg-gray-800/50 rounded-xl border border-gray-700 fade-in-up">
105
- <div class="text-sm text-gray-400 mb-1">Video Found:</div>
106
- <div class="font-bold text-white truncate">{{ title }}</div>
107
- <div class="text-xs text-blue-400 mt-1"><i class="fa-solid fa-clock"></i> {{ duration }}</div>
108
- </div>
109
- {% endif %}
110
-
111
  {% if formats %}
112
- <div class="mt-6 space-y-3 relative z-10">
113
- <div class="text-xs text-gray-400 uppercase tracking-widest text-center mb-4">Select Quality</div>
114
-
115
  {% for f in formats %}
116
- <div class="fade-in-up" style="animation-delay: {{ loop.index0 * 50 }}ms">
117
- <form method="POST" action="/download" onsubmit="showDownloadLoader(this)">
118
- <input type="hidden" name="url" value="{{ url }}">
119
- <input type="hidden" name="format_id" value="{{ f.id }}">
120
-
121
- <button type="submit" class="group w-full bg-gray-900/80 hover:bg-gray-800 border border-gray-700 hover:border-red-500 p-4 rounded-xl transition-all duration-300 flex justify-between items-center">
122
- <div class="text-left">
123
- <div class="flex items-center gap-2">
124
- <span class="font-bold text-xl text-white">{{ f.label }}</span>
125
- {% if '4K' in f.label or '1080' in f.label %}
126
- <span class="bg-red-600 text-white text-[10px] px-2 py-0.5 rounded font-bold">HD</span>
127
- {% endif %}
128
- </div>
129
- <div class="text-xs text-gray-500 mt-1">{{ f.ext }} • {{ f.size }}</div>
130
- </div>
131
- <div class="w-10 h-10 rounded-full bg-gray-800 group-hover:bg-red-600 flex items-center justify-center transition-all">
132
- <i class="fa-solid fa-download text-sm text-gray-400 group-hover:text-white"></i>
133
- </div>
134
- </button>
135
- </form>
136
- </div>
137
  {% endfor %}
138
  </div>
139
  {% endif %}
140
-
141
  {% if error %}
142
- <div class="mt-6 p-4 bg-red-900/30 border border-red-500 rounded-xl flex items-start gap-3 fade-in-up">
143
- <i class="fa-solid fa-circle-exclamation text-red-500 mt-1"></i>
144
- <div class="text-red-200 text-sm break-all">{{ error }}</div>
145
- </div>
146
  {% endif %}
147
-
148
  </div>
149
-
150
- <script>
151
- function showLoader() {
152
- const btn = document.getElementById('searchBtn');
153
- btn.style.opacity = '0.8';
154
- btn.innerHTML = 'Analyzing... <div class="loader" style="display:inline-block"></div>';
155
- }
156
- function showDownloadLoader(form) {
157
- const btn = form.querySelector('button');
158
- const icon = btn.querySelector('i');
159
- icon.className = 'fa-solid fa-circle-notch fa-spin';
160
- }
161
- </script>
162
  </body>
163
  </html>
164
  """
@@ -171,82 +65,46 @@ def index():
171
  def fetch():
172
  url = request.form.get("url")
173
  try:
174
- # --- FIX: FORCE IPV4 & DISABLE CACHE ---
175
- # Ye settings server ko force karengi ki wo YouTube ko sahi se connect kare
176
  opts = {
177
- 'quiet': True,
178
- 'nocheckcertificate': True,
179
- 'force_ipv4': True, # YEH HAI MAIN JADOO
180
- 'cachedir': False, # Cache disable taaki purana error na aaye
181
- 'user_agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36',
182
  }
183
-
184
  with yt_dlp.YoutubeDL(opts) as ydl:
185
  info = ydl.extract_info(url, download=False)
186
  formats = []
187
  seen = set()
188
-
189
- video_title = info.get('title', 'Unknown Video')
190
- duration = info.get('duration_string', 'Unknown')
191
-
192
  for f in info.get('formats', []):
193
  if f.get('vcodec') != 'none':
194
  h = f.get('height', 0)
195
- if not h: continue
196
-
197
- if h >= 2160: label = "4K Ultra HD"
198
- elif h >= 1440: label = "2K Quad HD"
199
- elif h >= 1080: label = "1080p Full HD"
200
- elif h >= 720: label = "720p HD"
201
- elif h >= 480: label = "480p SD"
202
- else: label = f"{h}p Low"
203
-
204
- if label not in seen:
205
- size = f.get('filesize') or f.get('filesize_approx') or 0
206
- size_str = f"{round(size/1e6,1)} MB" if size > 0 else "N/A"
207
-
208
- formats.append({
209
- 'id': f['format_id'],
210
- 'label': label,
211
- 'size': size_str,
212
- 'h': h,
213
- 'ext': f['ext'].upper()
214
- })
215
- seen.add(label)
216
-
217
- formats.sort(key=lambda x: x['h'], reverse=True)
218
- return render_template_string(HTML_CODE, formats=formats, url=url, title=video_title, duration=duration)
219
-
220
  except Exception as e:
221
- return render_template_string(HTML_CODE, error=f"Error: {str(e)}")
222
 
223
  @app.route("/download", methods=["POST"])
224
  def dl():
225
  url = request.form.get("url")
226
  fid = request.form.get("format_id")
227
- name = f"Video_{uuid.uuid4().hex[:5]}.mp4"
228
-
229
  try:
230
- # Yahan bhi FIX lagaya hai
231
- opts = {
232
- 'format': f'{fid}+bestaudio/best',
233
- 'outtmpl': name,
234
- 'merge_output_format': 'mp4',
235
- 'nocheckcertificate': True,
236
- 'force_ipv4': True, # IMPORTANT
237
- 'cachedir': False,
238
- }
239
-
240
  with yt_dlp.YoutubeDL(opts) as ydl:
241
  ydl.download([url])
242
-
243
  @after_this_request
244
  def cln(r):
245
  if os.path.exists(name): os.remove(name)
246
  return r
247
  return send_file(name, as_attachment=True)
248
  except Exception as e:
249
- return f"Download Failed: {str(e)}"
250
 
251
  if __name__ == "__main__":
252
  app.run(host='0.0.0.0', port=7860)
 
2
  import yt_dlp
3
  import os
4
  import uuid
 
5
 
6
  app = Flask(__name__)
7
 
8
+ # --- UI CODE ---
9
  HTML_CODE = """
10
  <!DOCTYPE html>
11
  <html lang="en">
 
16
  <script src="https://cdn.tailwindcss.com"></script>
17
  <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
18
  <style>
19
+ body { font-family: sans-serif; background-color: #0f172a; color: white; }
20
+ .glass { background: rgba(30, 41, 59, 0.8); backdrop-filter: blur(12px); border: 1px solid rgba(255,255,255,0.1); border-radius: 16px; }
21
+ .loader { border: 3px solid #f3f3f3; border-top: 3px solid #3498db; border-radius: 50%; width: 20px; height: 20px; animation: spin 1s linear infinite; display: none; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
  @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
 
 
 
23
  </style>
24
  </head>
25
+ <body class="flex items-center justify-center min-h-screen p-4">
26
+ <div class="glass w-full max-w-md p-6 shadow-2xl">
27
+ <h1 class="text-3xl font-bold text-center mb-6 text-blue-400">UniLoader <span class="text-xs text-gray-400">v2.0</span></h1>
28
+
29
+ <form method="POST" action="/fetch" onsubmit="document.getElementById('spinner').style.display='inline-block'" class="space-y-4">
30
+ <input type="text" name="url" class="w-full bg-gray-900 border border-gray-700 rounded-lg p-3 focus:outline-none focus:border-blue-500" placeholder="Paste Link (YouTube/TikTok)..." required>
31
+ <button type="submit" class="w-full bg-blue-600 hover:bg-blue-700 p-3 rounded-lg font-bold flex justify-center items-center gap-2">
32
+ <span>Search</span> <div class="loader" id="spinner"></div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
  </button>
34
  </form>
35
 
 
 
 
 
 
 
 
 
36
  {% if formats %}
37
+ <div class="mt-6 space-y-2">
38
+ <p class="text-xs text-gray-400 text-center uppercase">Select Quality</p>
 
39
  {% for f in formats %}
40
+ <form method="POST" action="/download">
41
+ <input type="hidden" name="url" value="{{ url }}">
42
+ <input type="hidden" name="format_id" value="{{ f.id }}">
43
+ <button type="submit" class="w-full bg-gray-800 hover:bg-gray-700 p-3 rounded-lg flex justify-between items-center border border-gray-700">
44
+ <span class="font-bold">{{ f.label }}</span>
45
+ <span class="text-xs text-gray-400">{{ f.ext }}</span>
46
+ </button>
47
+ </form>
 
 
 
 
 
 
 
 
 
 
 
 
 
48
  {% endfor %}
49
  </div>
50
  {% endif %}
51
+
52
  {% if error %}
53
+ <div class="mt-4 p-3 bg-red-900/50 text-red-200 text-sm rounded border border-red-500 text-center">{{ error }}</div>
 
 
 
54
  {% endif %}
 
55
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
56
  </body>
57
  </html>
58
  """
 
65
  def fetch():
66
  url = request.form.get("url")
67
  try:
68
+ # Latest yt-dlp needs minimal config
 
69
  opts = {
70
+ 'quiet': True,
71
+ 'no_warnings': True,
72
+ 'nocheckcertificate': True,
 
 
73
  }
 
74
  with yt_dlp.YoutubeDL(opts) as ydl:
75
  info = ydl.extract_info(url, download=False)
76
  formats = []
77
  seen = set()
 
 
 
 
78
  for f in info.get('formats', []):
79
  if f.get('vcodec') != 'none':
80
  h = f.get('height', 0)
81
+ if h:
82
+ label = f"{h}p"
83
+ if h >= 1080: label += " HD"
84
+ if label not in seen:
85
+ formats.append({'id': f['format_id'], 'label': label, 'ext': f['ext']})
86
+ seen.add(label)
87
+ formats.sort(key=lambda x: int(x['label'].split('p')[0]), reverse=True)
88
+ return render_template_string(HTML_CODE, formats=formats, url=url)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
89
  except Exception as e:
90
+ return render_template_string(HTML_CODE, error=str(e))
91
 
92
  @app.route("/download", methods=["POST"])
93
  def dl():
94
  url = request.form.get("url")
95
  fid = request.form.get("format_id")
96
+ name = f"Vid_{uuid.uuid4().hex[:5]}.mp4"
 
97
  try:
98
+ opts = {'format': f'{fid}+bestaudio/best', 'outtmpl': name, 'merge_output_format': 'mp4'}
 
 
 
 
 
 
 
 
 
99
  with yt_dlp.YoutubeDL(opts) as ydl:
100
  ydl.download([url])
 
101
  @after_this_request
102
  def cln(r):
103
  if os.path.exists(name): os.remove(name)
104
  return r
105
  return send_file(name, as_attachment=True)
106
  except Exception as e:
107
+ return f"Error: {str(e)}"
108
 
109
  if __name__ == "__main__":
110
  app.run(host='0.0.0.0', port=7860)