jpjp9292 commited on
Commit
cb64250
1 Parent(s): e89cd34

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +380 -338
app.py CHANGED
@@ -109,407 +109,449 @@
109
  # """)
110
 
111
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
112
  import streamlit as st
113
  import yt_dlp
114
  import os
115
  from pathlib import Path
116
  import time
 
117
  from datetime import datetime
118
  import json
 
 
 
119
 
120
  # Set page config
121
  st.set_page_config(page_title="YouTube Video Downloader", page_icon="📺", layout="wide")
122
-
123
- # Set the title of the app
124
  st.title("YouTube Video Downloader 📺")
125
 
126
- # Create output directory if it doesn't exist
127
- output_dir = Path("downloads")
128
- output_dir.mkdir(exist_ok=True)
129
-
130
- # Authentication settings
131
- AUTH_CONFIG = {
132
- 'COOKIES_FILE': 'youtube.com_cookies.txt',
133
- 'TOKEN_FILE': 'auth_token.json'
134
- }
135
-
136
- def load_auth_token():
137
- try:
138
- if os.path.exists(AUTH_CONFIG['TOKEN_FILE']):
139
- with open(AUTH_CONFIG['TOKEN_FILE'], 'r') as f:
140
- data = json.load(f)
141
- if datetime.fromisoformat(data['expires']) > datetime.now():
142
- return data['token']
143
- except Exception:
144
- pass
145
- return None
146
-
147
- def save_auth_token(token):
148
- with open(AUTH_CONFIG['TOKEN_FILE'], 'w') as f:
149
- json.dump({
150
- 'token': token,
151
- 'expires': (datetime.now().isoformat())
152
- }, f)
153
-
154
- def setup_ydl_options(has_cookies):
155
- opts = {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
156
  'format': 'bestvideo[ext=mp4]+bestaudio[ext=m4a]/best[ext=mp4]/best',
157
- 'outtmpl': str(output_dir / '%(title)s.%(ext)s'),
158
  'merge_output_format': 'mp4',
159
  'quiet': True,
160
  'no_warnings': True,
161
  'extract_flat': False,
162
- 'user_agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
163
- 'referer': 'https://www.youtube.com/',
164
- 'http_chunk_size': 10485760,
165
- 'retries': 3,
166
  'file_access_retries': 3,
167
  'fragment_retries': 3,
 
168
  'skip_unavailable_fragments': True,
169
  'abort_on_unavailable_fragment': False,
170
  }
171
 
172
- # Add cookies file if available
173
- if has_cookies:
174
- opts['cookiefile'] = AUTH_CONFIG['COOKIES_FILE']
175
 
176
- # Add authentication token if available
177
- token = load_auth_token()
178
- if token:
179
- opts['headers'] = {
180
- 'Authorization': f'Bearer {token}'
181
- }
182
 
183
- return opts
184
 
185
- def download_video(url, progress_callback):
186
- max_retries = 3
187
- retry_count = 0
188
-
189
- while retry_count < max_retries:
190
  try:
191
- has_cookies = os.path.exists(AUTH_CONFIG['COOKIES_FILE'])
192
- ydl_opts = setup_ydl_options(has_cookies)
193
 
194
- # Add progress hooks
195
  def progress_hook(d):
196
  if d['status'] == 'downloading':
197
  try:
198
  progress = d['downloaded_bytes'] / d['total_bytes']
199
  progress_callback(progress, f"Downloading: {progress:.1%}")
200
  except:
201
- progress_callback(0, "Downloading... (size unknown)")
202
  elif d['status'] == 'finished':
203
  progress_callback(1.0, "Processing...")
204
-
205
  ydl_opts['progress_hooks'] = [progress_hook]
206
 
 
 
 
 
 
 
 
 
 
207
  with yt_dlp.YoutubeDL(ydl_opts) as ydl:
208
  info = ydl.extract_info(url, download=True)
209
  filename = ydl.prepare_filename(info)
210
  return filename, None
211
 
212
  except yt_dlp.utils.ExtractorError as e:
213
- if "Sign in to confirm you're not a bot" in str(e):
214
- retry_count += 1
215
- if retry_count < max_retries:
216
- time.sleep(5) # Wait before retry
217
- continue
218
- return None, "Bot detection triggered. Please try again later or use authentication."
219
- return None, f"Video extraction failed: {str(e)}"
220
 
221
  except Exception as e:
222
- return None, f"Download failed: {str(e)}"
 
 
 
 
 
 
223
 
224
  return None, "Maximum retries reached. Please try again later."
225
 
226
- # Main UI
227
- col1, col2 = st.columns([2, 1])
228
-
229
- with col1:
230
- video_url = st.text_input("Enter YouTube Video URL:", placeholder="https://www.youtube.com/watch?v=...")
231
 
232
- if st.button("Download", type="primary"):
233
- if video_url:
234
- progress_bar = st.progress(0)
235
- status_text = st.empty()
236
-
237
- def update_progress(progress, status):
238
- progress_bar.progress(progress)
239
- status_text.text(status)
240
-
241
- downloaded_file, error = download_video(video_url, update_progress)
242
-
243
- if downloaded_file and os.path.exists(downloaded_file):
244
- with open(downloaded_file, 'rb') as file:
245
- st.download_button(
246
- label="⬇️ Download Video",
247
- data=file,
248
- file_name=os.path.basename(downloaded_file),
249
- mime="video/mp4"
250
- )
251
- st.success("✅ Download ready!")
 
 
 
 
 
 
252
  else:
253
- st.error(f" {error}")
254
- else:
255
- st.warning("⚠️ Please enter a valid YouTube URL.")
 
256
 
257
- with col2:
258
  with st.expander("⚙️ Settings & Information"):
259
- st.markdown("""
260
- **Authentication Status:**
261
- """)
262
 
263
- if os.path.exists(AUTH_CONFIG['COOKIES_FILE']):
264
- st.success("✅ Cookie authentication available")
 
 
 
 
 
 
265
  else:
266
  st.warning("""
267
  ⚠️ No cookie authentication configured
268
 
269
  To enable cookie authentication:
270
- 1. Export cookies from your browser
271
- 2. Save as 'youtube.com_cookies.txt' in the app directory
 
272
  """)
273
-
274
- st.markdown("""
275
- **Download Options:**
276
- - Videos are downloaded in best available quality
277
- - Format: MP4 (when available)
278
- - Automatic quality selection
279
- """)
280
-
281
- with st.expander("❓ Help & Troubleshooting"):
282
- st.markdown("""
283
- **Common Issues & Solutions:**
284
-
285
- 1. **Bot Detection:**
286
- - The app implements automatic retries
287
- - Use cookie authentication for better success rate
288
- - Wait a few minutes between attempts
289
-
290
- 2. **Download Fails:**
291
- - Verify the video is public
292
- - Check your internet connection
293
- - Try a different video
294
- - Clear browser cache and cookies
295
-
296
- 3. **Quality Issues:**
297
- - The app automatically selects the best available quality
298
- - Some videos may have quality restrictions
299
- - Premium content may require authentication
300
-
301
- **Need More Help?**
302
- - Check if the video is available in your region
303
- - Verify YouTube's terms of service
304
- - Consider using cookie authentication
305
- """)
306
-
307
-
308
- # import streamlit as st
309
- # import yt_dlp
310
- # import os
311
- # from pathlib import Path
312
- # from http.cookiejar import MozillaCookieJar
313
- # import random
314
- # import time
315
-
316
- # # Constants
317
- # COOKIES_FILE = 'youtube.com_cookies.txt'
318
- # OUTPUT_DIR = Path("downloads")
319
-
320
- # # Set page config
321
- # st.set_page_config(page_title="YouTube Video Downloader", page_icon="📺")
322
- # st.title("YouTube Video Downloader 📺")
323
-
324
- # # Create output directory if it doesn't exist
325
- # OUTPUT_DIR.mkdir(exist_ok=True)
326
-
327
- # def get_random_user_agent():
328
- # """랜덤 User-Agent 생성"""
329
- # user_agents = [
330
- # 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36',
331
- # 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:122.0) Gecko/20100101 Firefox/122.0',
332
- # 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.2.1 Safari/605.1.15',
333
- # 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0'
334
- # ]
335
- # return random.choice(user_agents)
336
-
337
- # def validate_cookies():
338
- # """쿠키 파일 유효성 검사"""
339
- # if not os.path.exists(COOKIES_FILE):
340
- # return False, "Cookie file not found"
341
-
342
- # try:
343
- # cookie_jar = MozillaCookieJar(COOKIES_FILE)
344
- # cookie_jar.load()
345
- # return True, "Cookies loaded successfully"
346
- # except Exception as e:
347
- # return False, f"Cookie error: {str(e)}"
348
-
349
- # def download_with_retry(url, ydl_opts, max_retries=3, delay=5):
350
- # """재시도 로직이 포함된 다운로드 함수"""
351
- # last_error = None
352
-
353
- # for attempt in range(max_retries):
354
- # try:
355
- # # 매 시도마다 새로운 User-Agent 사용
356
- # ydl_opts['headers']['User-Agent'] = get_random_user_agent()
357
-
358
- # with yt_dlp.YoutubeDL(ydl_opts) as ydl:
359
- # info = ydl.extract_info(url, download=True)
360
- # filename = ydl.prepare_filename(info)
361
- # return filename, info
362
-
363
- # except yt_dlp.utils.DownloadError as e:
364
- # last_error = e
365
- # if "Sign in to confirm you're not a bot" in str(e):
366
- # if attempt < max_retries - 1:
367
- # st.warning(f"Bot detection encountered. Retrying in {delay} seconds... (Attempt {attempt + 1}/{max_retries})")
368
- # time.sleep(delay)
369
- # delay *= 2 # 지수 백오프
370
- # continue
371
- # elif "HTTP Error 429" in str(e):
372
- # if attempt < max_retries - 1:
373
- # st.warning(f"Rate limit detected. Waiting {delay} seconds... (Attempt {attempt + 1}/{max_retries})")
374
- # time.sleep(delay * 2) # 레이트 리밋의 경우 더 긴 대기 시간
375
- # continue
376
- # except Exception as e:
377
- # last_error = e
378
- # if attempt < max_retries - 1:
379
- # st.warning(f"Error occurred. Retrying... (Attempt {attempt + 1}/{max_retries})")
380
- # time.sleep(delay)
381
- # continue
382
-
383
- # # 모든 재시도 실패 후
384
- # raise last_error or Exception("Download failed after all retries")
385
-
386
- # def download_video(url):
387
- # try:
388
- # ydl_opts = {
389
- # 'format': 'bestvideo[ext=mp4]+bestaudio[ext=m4a]/best[ext=mp4]/best',
390
- # 'outtmpl': str(OUTPUT_DIR / '%(title)s.%(ext)s'),
391
- # 'merge_output_format': 'mp4',
392
-
393
- # # 쿠키 설정
394
- # 'cookiefile': COOKIES_FILE,
395
-
396
- # # 향상된 옵션
397
- # 'quiet': True,
398
- # 'no_warnings': True,
399
- # 'extract_flat': False,
400
- # 'socket_timeout': 30,
401
- # 'retries': 10,
402
- # 'fragment_retries': 10,
403
-
404
- # # 향상된 헤더
405
- # 'headers': {
406
- # 'User-Agent': get_random_user_agent(),
407
- # 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8',
408
- # 'Accept-Language': 'en-US,en;q=0.5',
409
- # 'Accept-Encoding': 'gzip, deflate, br',
410
- # 'Connection': 'keep-alive',
411
- # 'Upgrade-Insecure-Requests': '1',
412
- # 'Sec-Fetch-Dest': 'document',
413
- # 'Sec-Fetch-Mode': 'navigate',
414
- # 'Sec-Fetch-Site': 'none',
415
- # 'Sec-Fetch-User': '?1',
416
- # 'DNT': '1',
417
- # 'Sec-GPC': '1',
418
- # 'Pragma': 'no-cache',
419
- # 'Cache-Control': 'no-cache',
420
- # },
421
 
422
- # # 추가 옵션
423
- # 'age_limit': None,
424
- # 'geo_bypass': True,
425
- # 'geo_bypass_country': 'US',
426
- # 'sleep_interval': 2,
427
- # 'max_sleep_interval': 5,
428
- # }
 
 
 
 
 
 
 
 
429
 
430
- # # 진행 상황 표시
431
- # progress_bar = st.progress(0)
432
- # status_text = st.empty()
 
 
433
 
434
- # def progress_hook(d):
435
- # if d['status'] == 'downloading':
436
- # try:
437
- # progress = d['downloaded_bytes'] / d['total_bytes']
438
- # progress_bar.progress(progress)
439
- # status_text.text(f"Downloading: {progress:.1%} | Speed: {d.get('speed_str', 'N/A')} | ETA: {d.get('eta_str', 'N/A')}")
440
- # except:
441
- # status_text.text(f"Downloading... Speed: {d.get('speed_str', 'N/A')}")
442
- # elif d['status'] == 'finished':
443
- # progress_bar.progress(1.0)
444
- # status_text.text("Processing downloaded files...")
445
-
446
- # ydl_opts['progress_hooks'] = [progress_hook]
447
 
448
- # return download_with_retry(url, ydl_opts)
449
-
450
- # except Exception as e:
451
- # raise Exception(f"Download error: {str(e)}")
452
-
453
- # # 쿠키 상태 확인
454
- # cookie_valid, cookie_message = validate_cookies()
455
- # if not cookie_valid:
456
- # st.warning(f"⚠️ Cookie Issue: {cookie_message}")
457
- # else:
458
- # st.success("✅ Cookies loaded successfully")
459
 
460
- # # 비디오 URL 입력
461
- # video_url = st.text_input("Enter YouTube Video URL:", placeholder="https://www.youtube.com/watch?v=...")
 
 
462
 
463
- # # 다운로드 버튼
464
- # if st.button("Download Video"):
465
- # if not video_url:
466
- # st.warning("⚠️ Please enter a valid YouTube URL.")
467
- # elif not cookie_valid:
468
- # st.error("❌ Cannot proceed without valid cookies.")
469
- # else:
470
- # try:
471
- # with st.spinner("Preparing download..."):
472
- # downloaded_file_path, video_info = download_video(video_url)
473
-
474
- # if downloaded_file_path and os.path.exists(downloaded_file_path):
475
- # file_size = os.path.getsize(downloaded_file_path)
476
- # st.info(f"""
477
- # **Download Complete!**
478
- # - File: {os.path.basename(downloaded_file_path)}
479
- # - Size: {file_size / (1024*1024):.1f} MB
480
- # """)
481
-
482
- # with open(downloaded_file_path, 'rb') as file:
483
- # st.download_button(
484
- # label="💾 Download Video",
485
- # data=file,
486
- # file_name=os.path.basename(downloaded_file_path),
487
- # mime="video/mp4"
488
- # )
489
- # else:
490
- # st.error("❌ Download failed. Please try again.")
491
- # except Exception as e:
492
- # st.error(f"❌ {str(e)}")
493
-
494
- # # Help 섹션
495
- # with st.expander("ℹ️ Troubleshooting Guide"):
496
- # st.markdown(f"""
497
- # **Current Status:**
498
- # - Cookie File: {'✅ Found' if os.path.exists(COOKIES_FILE) else '❌ Not Found'}
499
- # - Cookie Validity: {'✅ Valid' if cookie_valid else '❌ Invalid'}
500
-
501
- # **Common Issues:**
502
- # 1. Bot Detection:
503
- # - The app will automatically retry with different settings
504
- # - Each retry uses a random User-Agent and headers
505
-
506
- # 2. Cookie Problems:
507
- # - Current cookie file: {COOKIES_FILE}
508
- # - Make sure the cookie file is up to date
509
- # - Export fresh cookies when issues persist
510
-
511
- # 3. Download Fails:
512
- # - Try again after a few minutes
513
- # - Check if the video is available in your region
514
- # - Verify that the video is public
515
- # """)
 
109
  # """)
110
 
111
 
112
+ # import streamlit as st
113
+ # import yt_dlp
114
+ # import os
115
+ # from pathlib import Path
116
+ # import time
117
+ # from datetime import datetime
118
+ # import json
119
+
120
+ # # Set page config
121
+ # st.set_page_config(page_title="YouTube Video Downloader", page_icon="📺", layout="wide")
122
+
123
+ # # Set the title of the app
124
+ # st.title("YouTube Video Downloader 📺")
125
+
126
+ # # Create output directory if it doesn't exist
127
+ # output_dir = Path("downloads")
128
+ # output_dir.mkdir(exist_ok=True)
129
+
130
+ # # Authentication settings
131
+ # AUTH_CONFIG = {
132
+ # 'COOKIES_FILE': 'youtube.com_cookies.txt',
133
+ # 'TOKEN_FILE': 'auth_token.json'
134
+ # }
135
+
136
+ # def load_auth_token():
137
+ # try:
138
+ # if os.path.exists(AUTH_CONFIG['TOKEN_FILE']):
139
+ # with open(AUTH_CONFIG['TOKEN_FILE'], 'r') as f:
140
+ # data = json.load(f)
141
+ # if datetime.fromisoformat(data['expires']) > datetime.now():
142
+ # return data['token']
143
+ # except Exception:
144
+ # pass
145
+ # return None
146
+
147
+ # def save_auth_token(token):
148
+ # with open(AUTH_CONFIG['TOKEN_FILE'], 'w') as f:
149
+ # json.dump({
150
+ # 'token': token,
151
+ # 'expires': (datetime.now().isoformat())
152
+ # }, f)
153
+
154
+ # def setup_ydl_options(has_cookies):
155
+ # opts = {
156
+ # 'format': 'bestvideo[ext=mp4]+bestaudio[ext=m4a]/best[ext=mp4]/best',
157
+ # 'outtmpl': str(output_dir / '%(title)s.%(ext)s'),
158
+ # 'merge_output_format': 'mp4',
159
+ # 'quiet': True,
160
+ # 'no_warnings': True,
161
+ # 'extract_flat': False,
162
+ # 'user_agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
163
+ # 'referer': 'https://www.youtube.com/',
164
+ # 'http_chunk_size': 10485760,
165
+ # 'retries': 3,
166
+ # 'file_access_retries': 3,
167
+ # 'fragment_retries': 3,
168
+ # 'skip_unavailable_fragments': True,
169
+ # 'abort_on_unavailable_fragment': False,
170
+ # }
171
+
172
+ # # Add cookies file if available
173
+ # if has_cookies:
174
+ # opts['cookiefile'] = AUTH_CONFIG['COOKIES_FILE']
175
+
176
+ # # Add authentication token if available
177
+ # token = load_auth_token()
178
+ # if token:
179
+ # opts['headers'] = {
180
+ # 'Authorization': f'Bearer {token}'
181
+ # }
182
+
183
+ # return opts
184
+
185
+ # def download_video(url, progress_callback):
186
+ # max_retries = 3
187
+ # retry_count = 0
188
+
189
+ # while retry_count < max_retries:
190
+ # try:
191
+ # has_cookies = os.path.exists(AUTH_CONFIG['COOKIES_FILE'])
192
+ # ydl_opts = setup_ydl_options(has_cookies)
193
+
194
+ # # Add progress hooks
195
+ # def progress_hook(d):
196
+ # if d['status'] == 'downloading':
197
+ # try:
198
+ # progress = d['downloaded_bytes'] / d['total_bytes']
199
+ # progress_callback(progress, f"Downloading: {progress:.1%}")
200
+ # except:
201
+ # progress_callback(0, "Downloading... (size unknown)")
202
+ # elif d['status'] == 'finished':
203
+ # progress_callback(1.0, "Processing...")
204
+
205
+ # ydl_opts['progress_hooks'] = [progress_hook]
206
+
207
+ # with yt_dlp.YoutubeDL(ydl_opts) as ydl:
208
+ # info = ydl.extract_info(url, download=True)
209
+ # filename = ydl.prepare_filename(info)
210
+ # return filename, None
211
+
212
+ # except yt_dlp.utils.ExtractorError as e:
213
+ # if "Sign in to confirm you're not a bot" in str(e):
214
+ # retry_count += 1
215
+ # if retry_count < max_retries:
216
+ # time.sleep(5) # Wait before retry
217
+ # continue
218
+ # return None, "Bot detection triggered. Please try again later or use authentication."
219
+ # return None, f"Video extraction failed: {str(e)}"
220
+
221
+ # except Exception as e:
222
+ # return None, f"Download failed: {str(e)}"
223
+
224
+ # return None, "Maximum retries reached. Please try again later."
225
+
226
+ # # Main UI
227
+ # col1, col2 = st.columns([2, 1])
228
+
229
+ # with col1:
230
+ # video_url = st.text_input("Enter YouTube Video URL:", placeholder="https://www.youtube.com/watch?v=...")
231
+
232
+ # if st.button("Download", type="primary"):
233
+ # if video_url:
234
+ # progress_bar = st.progress(0)
235
+ # status_text = st.empty()
236
+
237
+ # def update_progress(progress, status):
238
+ # progress_bar.progress(progress)
239
+ # status_text.text(status)
240
+
241
+ # downloaded_file, error = download_video(video_url, update_progress)
242
+
243
+ # if downloaded_file and os.path.exists(downloaded_file):
244
+ # with open(downloaded_file, 'rb') as file:
245
+ # st.download_button(
246
+ # label="⬇️ Download Video",
247
+ # data=file,
248
+ # file_name=os.path.basename(downloaded_file),
249
+ # mime="video/mp4"
250
+ # )
251
+ # st.success("✅ Download ready!")
252
+ # else:
253
+ # st.error(f"❌ {error}")
254
+ # else:
255
+ # st.warning("⚠️ Please enter a valid YouTube URL.")
256
+
257
+ # with col2:
258
+ # with st.expander("⚙️ Settings & Information"):
259
+ # st.markdown("""
260
+ # **Authentication Status:**
261
+ # """)
262
+
263
+ # if os.path.exists(AUTH_CONFIG['COOKIES_FILE']):
264
+ # st.success("✅ Cookie authentication available")
265
+ # else:
266
+ # st.warning("""
267
+ # ⚠️ No cookie authentication configured
268
+
269
+ # To enable cookie authentication:
270
+ # 1. Export cookies from your browser
271
+ # 2. Save as 'youtube.com_cookies.txt' in the app directory
272
+ # """)
273
+
274
+ # st.markdown("""
275
+ # **Download Options:**
276
+ # - Videos are downloaded in best available quality
277
+ # - Format: MP4 (when available)
278
+ # - Automatic quality selection
279
+ # """)
280
+
281
+ # with st.expander("❓ Help & Troubleshooting"):
282
+ # st.markdown("""
283
+ # **Common Issues & Solutions:**
284
+
285
+ # 1. **Bot Detection:**
286
+ # - The app implements automatic retries
287
+ # - Use cookie authentication for better success rate
288
+ # - Wait a few minutes between attempts
289
+
290
+ # 2. **Download Fails:**
291
+ # - Verify the video is public
292
+ # - Check your internet connection
293
+ # - Try a different video
294
+ # - Clear browser cache and cookies
295
+
296
+ # 3. **Quality Issues:**
297
+ # - The app automatically selects the best available quality
298
+ # - Some videos may have quality restrictions
299
+ # - Premium content may require authentication
300
+
301
+ # **Need More Help?**
302
+ # - Check if the video is available in your region
303
+ # - Verify YouTube's terms of service
304
+ # - Consider using cookie authentication
305
+ # """)
306
+
307
+
308
  import streamlit as st
309
  import yt_dlp
310
  import os
311
  from pathlib import Path
312
  import time
313
+ import random
314
  from datetime import datetime
315
  import json
316
+ import requests
317
+ from fake_useragent import UserAgent
318
+ import http.cookiejar as cookielib
319
 
320
  # Set page config
321
  st.set_page_config(page_title="YouTube Video Downloader", page_icon="📺", layout="wide")
 
 
322
  st.title("YouTube Video Downloader 📺")
323
 
324
+ # Constants and Configurations
325
+ class Config:
326
+ OUTPUT_DIR = Path("downloads")
327
+ COOKIES_FILE = 'youtube.com_cookies.txt'
328
+ TOKEN_FILE = 'auth_token.json'
329
+ MAX_RETRIES = 5
330
+ RETRY_DELAYS = [3, 5, 10, 15, 30] # Progressive delays in seconds
331
+
332
+ HEADERS_POOL = [
333
+ {
334
+ 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
335
+ 'Accept-Language': 'en-US,en;q=0.5',
336
+ 'DNT': '1',
337
+ 'Connection': 'keep-alive',
338
+ 'Upgrade-Insecure-Requests': '1',
339
+ },
340
+ {
341
+ 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
342
+ 'Accept-Language': 'en-US,en;q=0.9',
343
+ 'DNT': '1',
344
+ 'Connection': 'keep-alive',
345
+ }
346
+ ]
347
+
348
+ # Create output directory
349
+ Config.OUTPUT_DIR.mkdir(exist_ok=True)
350
+
351
+ # Session management
352
+ class SessionManager:
353
+ @staticmethod
354
+ def create_cookie_jar():
355
+ return cookielib.MozillaCookieJar(Config.COOKIES_FILE)
356
+
357
+ @staticmethod
358
+ def get_random_headers():
359
+ ua = UserAgent()
360
+ headers = random.choice(Config.HEADERS_POOL).copy()
361
+ headers['User-Agent'] = ua.random
362
+ return headers
363
+
364
+ @staticmethod
365
+ def get_session():
366
+ session = requests.Session()
367
+ if os.path.exists(Config.COOKIES_FILE):
368
+ session.cookies = SessionManager.create_cookie_jar()
369
+ session.cookies.load(ignore_discard=True, ignore_expires=True)
370
+ session.headers.update(SessionManager.get_random_headers())
371
+ return session
372
+
373
+ def get_download_options(session_headers=None):
374
+ """Enhanced download options with anti-bot measures"""
375
+ options = {
376
  'format': 'bestvideo[ext=mp4]+bestaudio[ext=m4a]/best[ext=mp4]/best',
377
+ 'outtmpl': str(Config.OUTPUT_DIR / '%(title)s.%(ext)s'),
378
  'merge_output_format': 'mp4',
379
  'quiet': True,
380
  'no_warnings': True,
381
  'extract_flat': False,
382
+ 'nocheckcertificate': True,
383
+ 'http_chunk_size': random.randint(10000000, 15000000), # Random chunk size
384
+ 'retries': Config.MAX_RETRIES,
 
385
  'file_access_retries': 3,
386
  'fragment_retries': 3,
387
+ 'retry_sleep_functions': {'http': lambda n: random.uniform(1, 5) * n},
388
  'skip_unavailable_fragments': True,
389
  'abort_on_unavailable_fragment': False,
390
  }
391
 
392
+ if os.path.exists(Config.COOKIES_FILE):
393
+ options['cookiefile'] = Config.COOKIES_FILE
 
394
 
395
+ if session_headers:
396
+ options['headers'] = session_headers
 
 
 
 
397
 
398
+ return options
399
 
400
+ def download_with_retry(url, progress_callback):
401
+ """Enhanced download function with multiple retry strategies"""
402
+ for attempt in range(Config.MAX_RETRIES):
 
 
403
  try:
404
+ session = SessionManager.get_session()
405
+ ydl_opts = get_download_options(session.headers)
406
 
 
407
  def progress_hook(d):
408
  if d['status'] == 'downloading':
409
  try:
410
  progress = d['downloaded_bytes'] / d['total_bytes']
411
  progress_callback(progress, f"Downloading: {progress:.1%}")
412
  except:
413
+ progress_callback(-1, f"Downloading... (Attempt {attempt + 1}/{Config.MAX_RETRIES})")
414
  elif d['status'] == 'finished':
415
  progress_callback(1.0, "Processing...")
416
+
417
  ydl_opts['progress_hooks'] = [progress_hook]
418
 
419
+ # Pre-check video availability
420
+ with yt_dlp.YoutubeDL({'quiet': True}) as ydl:
421
+ try:
422
+ ydl.extract_info(url, download=False)
423
+ except Exception as e:
424
+ if "This video is not available" in str(e):
425
+ return None, "Video is not available. It might be private or deleted."
426
+
427
+ # Actual download
428
  with yt_dlp.YoutubeDL(ydl_opts) as ydl:
429
  info = ydl.extract_info(url, download=True)
430
  filename = ydl.prepare_filename(info)
431
  return filename, None
432
 
433
  except yt_dlp.utils.ExtractorError as e:
434
+ error_msg = str(e)
435
+ if "Sign in to confirm you're not a bot" in error_msg:
436
+ delay = Config.RETRY_DELAYS[min(attempt, len(Config.RETRY_DELAYS)-1)]
437
+ progress_callback(-1, f"Bot detection encountered. Waiting {delay}s before retry...")
438
+ time.sleep(delay)
439
+ continue
440
+ return None, f"Video extraction failed: {error_msg}"
441
 
442
  except Exception as e:
443
+ error_msg = str(e)
444
+ if attempt < Config.MAX_RETRIES - 1:
445
+ delay = Config.RETRY_DELAYS[attempt]
446
+ progress_callback(-1, f"Download failed. Retrying in {delay}s... ({attempt + 1}/{Config.MAX_RETRIES})")
447
+ time.sleep(delay)
448
+ continue
449
+ return None, f"Download failed after {Config.MAX_RETRIES} attempts: {error_msg}"
450
 
451
  return None, "Maximum retries reached. Please try again later."
452
 
453
+ # UI Components
454
+ def render_main_ui():
455
+ col1, col2 = st.columns([2, 1])
 
 
456
 
457
+ with col1:
458
+ video_url = st.text_input("Enter YouTube Video URL:", placeholder="https://www.youtube.com/watch?v=...")
459
+
460
+ if st.button("Download", type="primary"):
461
+ if video_url:
462
+ progress_bar = st.progress(0)
463
+ status_text = st.empty()
464
+
465
+ def update_progress(progress, status):
466
+ if progress >= 0:
467
+ progress_bar.progress(progress)
468
+ status_text.text(status)
469
+
470
+ downloaded_file, error = download_with_retry(video_url, update_progress)
471
+
472
+ if downloaded_file and os.path.exists(downloaded_file):
473
+ with open(downloaded_file, 'rb') as file:
474
+ st.download_button(
475
+ label="⬇️ Download Video",
476
+ data=file,
477
+ file_name=os.path.basename(downloaded_file),
478
+ mime="video/mp4"
479
+ )
480
+ st.success("✅ Download completed successfully!")
481
+ else:
482
+ st.error(f"❌ {error}")
483
  else:
484
+ st.warning("⚠️ Please enter a valid YouTube URL.")
485
+
486
+ with col2:
487
+ render_settings_panel()
488
 
489
+ def render_settings_panel():
490
  with st.expander("⚙️ Settings & Information"):
491
+ st.markdown("**Authentication Status:**")
 
 
492
 
493
+ if os.path.exists(Config.COOKIES_FILE):
494
+ st.success("✅ Cookie authentication active")
495
+ if st.button("Remove Cookie File"):
496
+ try:
497
+ os.remove(Config.COOKIES_FILE)
498
+ st.rerun()
499
+ except Exception as e:
500
+ st.error(f"Failed to remove cookie file: {str(e)}")
501
  else:
502
  st.warning("""
503
  ⚠️ No cookie authentication configured
504
 
505
  To enable cookie authentication:
506
+ 1. Install a browser extension to export cookies
507
+ 2. Export cookies from YouTube (while logged in)
508
+ 3. Save as 'youtube.com_cookies.txt' in the app directory
509
  """)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
510
 
511
+ # Cookie file uploader
512
+ uploaded_file = st.file_uploader("Upload Cookie File", type=['txt'])
513
+ if uploaded_file is not None:
514
+ try:
515
+ with open(Config.COOKIES_FILE, 'wb') as f:
516
+ f.write(uploaded_file.getvalue())
517
+ st.success("✅ Cookie file uploaded successfully!")
518
+ st.rerun()
519
+ except Exception as e:
520
+ st.error(f"Failed to save cookie file: {str(e)}")
521
+
522
+ def render_help_section():
523
+ with st.expander("❓ Help & Troubleshooting"):
524
+ st.markdown("""
525
+ **Common Issues & Solutions:**
526
 
527
+ 1. **Bot Detection Issues:**
528
+ - The app now implements multiple retry strategies
529
+ - Uses random delays between attempts
530
+ - Rotates User-Agents automatically
531
+ - Uses cookie authentication when available
532
 
533
+ 2. **Download Problems:**
534
+ - Check if the video is public and available
535
+ - Verify your internet connection
536
+ - Try uploading a fresh cookie file
537
+ - Clear browser cache and try again
 
 
 
 
 
 
 
 
538
 
539
+ 3. **Quality Settings:**
540
+ - Automatically selects best available quality
541
+ - Prioritizes MP4 format when available
542
+ - Handles premium content with proper authentication
543
+
544
+ **Need More Help?**
545
+ - Make sure you're using an up-to-date browser
546
+ - Check if the video is available in your region
547
+ - Consider using a VPN if region-blocked
548
+ - Ensure your cookie file is recent and valid
549
+ """)
550
 
551
+ # Main App
552
+ def main():
553
+ render_main_ui()
554
+ render_help_section()
555
 
556
+ if __name__ == "__main__":
557
+ main()