jpjp9292 commited on
Commit
5119228
β€’
1 Parent(s): 68f2114

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +267 -267
app.py CHANGED
@@ -1,105 +1,105 @@
1
 
2
- # import streamlit as st
3
- # import yt_dlp
4
- # import os
5
- # from pathlib import Path
6
 
7
- # # Set page config
8
- # st.set_page_config(page_title="YouTube Video Downloader", page_icon="πŸ“Ί")
9
 
10
- # # Set the title of the app
11
- # st.title("YouTube Video Downloader πŸ“Ί")
12
 
13
- # # Create output directory if it doesn't exist
14
- # output_dir = Path("downloads")
15
- # output_dir.mkdir(exist_ok=True)
16
 
17
- # # Input field for YouTube video URL
18
- # video_url = st.text_input("Enter YouTube Video URL:", placeholder="https://www.youtube.com/watch?v=...")
19
 
20
- # # Function to download video
21
- # def download_video(url):
22
- # try:
23
- # ydl_opts = {
24
- # 'format': 'bestvideo+bestaudio/best',
25
- # 'outtmpl': str(output_dir / '%(title)s.%(ext)s'),
26
- # 'merge_output_format': 'webm',
27
- # # Add these options to mimic browser behavior
28
- # 'quiet': True,
29
- # 'no_warnings': True,
30
- # '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',
31
- # 'referer': 'https://www.youtube.com/',
32
- # 'cookiefile': 'youtube.com_cookies.txt', # μΏ ν‚€ 파일 μ‚¬μš©
33
- # 'socket_timeout': 30,
34
- # 'http_chunk_size': 10485760, # 10MB
35
- # }
36
 
37
- # # Progress placeholder
38
- # progress_bar = st.progress(0)
39
- # status_text = st.empty()
40
 
41
- # def progress_hook(d):
42
- # if d['status'] == 'downloading':
43
- # try:
44
- # progress = d['downloaded_bytes'] / d['total_bytes']
45
- # progress_bar.progress(progress)
46
- # status_text.text(f"Downloading: {progress:.1%}")
47
- # except:
48
- # status_text.text("Downloading... (size unknown)")
49
- # elif d['status'] == 'finished':
50
- # progress_bar.progress(1.0)
51
- # status_text.text("Processing...")
52
 
53
- # ydl_opts['progress_hooks'] = [progress_hook]
54
 
55
- # with yt_dlp.YoutubeDL(ydl_opts) as ydl:
56
- # info = ydl.extract_info(url, download=True)
57
- # filename = ydl.prepare_filename(info)
58
- # return filename
59
 
60
- # except Exception as e:
61
- # st.error(f"An error occurred: {str(e)}")
62
- # return None
63
 
64
- # # Download button
65
- # if st.button("Download"):
66
- # if video_url:
67
- # try:
68
- # with st.spinner("Preparing download..."):
69
- # downloaded_file_path = download_video(video_url)
70
 
71
- # if downloaded_file_path and os.path.exists(downloaded_file_path):
72
- # with open(downloaded_file_path, 'rb') as file:
73
- # st.download_button(
74
- # label="Click here to download",
75
- # data=file,
76
- # file_name=os.path.basename(downloaded_file_path),
77
- # mime="application/octet-stream"
78
- # )
79
- # st.success("βœ… Download ready!")
80
- # else:
81
- # st.error("❌ Download failed. Please try again.")
82
- # except Exception as e:
83
- # st.error(f"❌ An error occurred: {str(e)}")
84
- # else:
85
- # st.warning("⚠️ Please enter a valid YouTube URL.")
86
 
87
- # # Help section
88
- # with st.expander("ℹ️ Help & Troubleshooting"):
89
- # st.markdown("""
90
- # **If you're experiencing download issues:**
91
 
92
- # 1. Cookie Authentication Method:
93
- # - Install a browser extension like "Get cookies.txt"
94
- # - Visit YouTube and ensure you're logged in
95
- # - Export cookies to 'youtube.com_cookies.txt'
96
- # - Place the file in the same directory as this app
97
 
98
- # 2. Alternative Solutions:
99
- # - Try different videos
100
- # - Check if the video is public/not age-restricted
101
- # - Try again later if YouTube is blocking requests
102
- # """)
103
 
104
 
105
  # import streamlit as st
@@ -212,211 +212,211 @@
212
  # """)
213
 
214
 
215
- import streamlit as st
216
- import yt_dlp
217
- import os
218
- from pathlib import Path
219
- from http.cookiejar import MozillaCookieJar
220
- import random
221
- import time
222
 
223
- # Constants
224
- COOKIES_FILE = 'youtube.com_cookies.txt'
225
- OUTPUT_DIR = Path("downloads")
226
 
227
- # Set page config
228
- st.set_page_config(page_title="YouTube Video Downloader", page_icon="πŸ“Ί")
229
- st.title("YouTube Video Downloader πŸ“Ί")
230
 
231
- # Create output directory if it doesn't exist
232
- OUTPUT_DIR.mkdir(exist_ok=True)
233
-
234
- def get_random_user_agent():
235
- """랜덀 User-Agent 생성"""
236
- user_agents = [
237
- 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36',
238
- 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:122.0) Gecko/20100101 Firefox/122.0',
239
- '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',
240
- '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'
241
- ]
242
- return random.choice(user_agents)
243
-
244
- def validate_cookies():
245
- """μΏ ν‚€ 파일 μœ νš¨μ„± 검사"""
246
- if not os.path.exists(COOKIES_FILE):
247
- return False, "Cookie file not found"
248
 
249
- try:
250
- cookie_jar = MozillaCookieJar(COOKIES_FILE)
251
- cookie_jar.load()
252
- return True, "Cookies loaded successfully"
253
- except Exception as e:
254
- return False, f"Cookie error: {str(e)}"
255
 
256
- def download_with_retry(url, ydl_opts, max_retries=3, delay=5):
257
- """μž¬μ‹œλ„ 둜직이 ν¬ν•¨λœ λ‹€μš΄λ‘œλ“œ ν•¨μˆ˜"""
258
- last_error = None
259
 
260
- for attempt in range(max_retries):
261
- try:
262
- # 맀 μ‹œλ„λ§ˆλ‹€ μƒˆλ‘œμš΄ User-Agent μ‚¬μš©
263
- ydl_opts['headers']['User-Agent'] = get_random_user_agent()
264
 
265
- with yt_dlp.YoutubeDL(ydl_opts) as ydl:
266
- info = ydl.extract_info(url, download=True)
267
- filename = ydl.prepare_filename(info)
268
- return filename, info
269
 
270
- except yt_dlp.utils.DownloadError as e:
271
- last_error = e
272
- if "Sign in to confirm you're not a bot" in str(e):
273
- if attempt < max_retries - 1:
274
- st.warning(f"Bot detection encountered. Retrying in {delay} seconds... (Attempt {attempt + 1}/{max_retries})")
275
- time.sleep(delay)
276
- delay *= 2 # μ§€μˆ˜ λ°±μ˜€ν”„
277
- continue
278
- elif "HTTP Error 429" in str(e):
279
- if attempt < max_retries - 1:
280
- st.warning(f"Rate limit detected. Waiting {delay} seconds... (Attempt {attempt + 1}/{max_retries})")
281
- time.sleep(delay * 2) # 레이트 λ¦¬λ°‹μ˜ 경우 더 κΈ΄ λŒ€κΈ° μ‹œκ°„
282
- continue
283
- except Exception as e:
284
- last_error = e
285
- if attempt < max_retries - 1:
286
- st.warning(f"Error occurred. Retrying... (Attempt {attempt + 1}/{max_retries})")
287
- time.sleep(delay)
288
- continue
289
 
290
- # λͺ¨λ“  μž¬μ‹œλ„ μ‹€νŒ¨ ν›„
291
- raise last_error or Exception("Download failed after all retries")
292
 
293
- def download_video(url):
294
- try:
295
- ydl_opts = {
296
- 'format': 'bestvideo[ext=mp4]+bestaudio[ext=m4a]/best[ext=mp4]/best',
297
- 'outtmpl': str(OUTPUT_DIR / '%(title)s.%(ext)s'),
298
- 'merge_output_format': 'mp4',
299
 
300
- # μΏ ν‚€ μ„€μ •
301
- 'cookiefile': COOKIES_FILE,
302
 
303
- # ν–₯μƒλœ μ˜΅μ…˜
304
- 'quiet': True,
305
- 'no_warnings': True,
306
- 'extract_flat': False,
307
- 'socket_timeout': 30,
308
- 'retries': 10,
309
- 'fragment_retries': 10,
310
 
311
- # ν–₯μƒλœ 헀더
312
- 'headers': {
313
- 'User-Agent': get_random_user_agent(),
314
- 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8',
315
- 'Accept-Language': 'en-US,en;q=0.5',
316
- 'Accept-Encoding': 'gzip, deflate, br',
317
- 'Connection': 'keep-alive',
318
- 'Upgrade-Insecure-Requests': '1',
319
- 'Sec-Fetch-Dest': 'document',
320
- 'Sec-Fetch-Mode': 'navigate',
321
- 'Sec-Fetch-Site': 'none',
322
- 'Sec-Fetch-User': '?1',
323
- 'DNT': '1',
324
- 'Sec-GPC': '1',
325
- 'Pragma': 'no-cache',
326
- 'Cache-Control': 'no-cache',
327
- },
328
 
329
- # μΆ”κ°€ μ˜΅μ…˜
330
- 'age_limit': None,
331
- 'geo_bypass': True,
332
- 'geo_bypass_country': 'US',
333
- 'sleep_interval': 2,
334
- 'max_sleep_interval': 5,
335
- }
336
 
337
- # 진행 상황 ν‘œμ‹œ
338
- progress_bar = st.progress(0)
339
- status_text = st.empty()
340
 
341
- def progress_hook(d):
342
- if d['status'] == 'downloading':
343
- try:
344
- progress = d['downloaded_bytes'] / d['total_bytes']
345
- progress_bar.progress(progress)
346
- status_text.text(f"Downloading: {progress:.1%} | Speed: {d.get('speed_str', 'N/A')} | ETA: {d.get('eta_str', 'N/A')}")
347
- except:
348
- status_text.text(f"Downloading... Speed: {d.get('speed_str', 'N/A')}")
349
- elif d['status'] == 'finished':
350
- progress_bar.progress(1.0)
351
- status_text.text("Processing downloaded files...")
352
 
353
- ydl_opts['progress_hooks'] = [progress_hook]
354
 
355
- return download_with_retry(url, ydl_opts)
356
 
357
- except Exception as e:
358
- raise Exception(f"Download error: {str(e)}")
359
 
360
- # μΏ ν‚€ μƒνƒœ 확인
361
- cookie_valid, cookie_message = validate_cookies()
362
- if not cookie_valid:
363
- st.warning(f"⚠️ Cookie Issue: {cookie_message}")
364
- else:
365
- st.success("βœ… Cookies loaded successfully")
366
 
367
- # λΉ„λ””μ˜€ URL μž…λ ₯
368
- video_url = st.text_input("Enter YouTube Video URL:", placeholder="https://www.youtube.com/watch?v=...")
369
 
370
- # λ‹€μš΄λ‘œλ“œ λ²„νŠΌ
371
- if st.button("Download Video"):
372
- if not video_url:
373
- st.warning("⚠️ Please enter a valid YouTube URL.")
374
- elif not cookie_valid:
375
- st.error("❌ Cannot proceed without valid cookies.")
376
- else:
377
- try:
378
- with st.spinner("Preparing download..."):
379
- downloaded_file_path, video_info = download_video(video_url)
380
 
381
- if downloaded_file_path and os.path.exists(downloaded_file_path):
382
- file_size = os.path.getsize(downloaded_file_path)
383
- st.info(f"""
384
- **Download Complete!**
385
- - File: {os.path.basename(downloaded_file_path)}
386
- - Size: {file_size / (1024*1024):.1f} MB
387
- """)
388
 
389
- with open(downloaded_file_path, 'rb') as file:
390
- st.download_button(
391
- label="πŸ’Ύ Download Video",
392
- data=file,
393
- file_name=os.path.basename(downloaded_file_path),
394
- mime="video/mp4"
395
- )
396
- else:
397
- st.error("❌ Download failed. Please try again.")
398
- except Exception as e:
399
- st.error(f"❌ {str(e)}")
400
-
401
- # Help μ„Ήμ…˜
402
- with st.expander("ℹ️ Troubleshooting Guide"):
403
- st.markdown(f"""
404
- **Current Status:**
405
- - Cookie File: {'βœ… Found' if os.path.exists(COOKIES_FILE) else '❌ Not Found'}
406
- - Cookie Validity: {'βœ… Valid' if cookie_valid else '❌ Invalid'}
407
 
408
- **Common Issues:**
409
- 1. Bot Detection:
410
- - The app will automatically retry with different settings
411
- - Each retry uses a random User-Agent and headers
412
 
413
- 2. Cookie Problems:
414
- - Current cookie file: {COOKIES_FILE}
415
- - Make sure the cookie file is up to date
416
- - Export fresh cookies when issues persist
417
 
418
- 3. Download Fails:
419
- - Try again after a few minutes
420
- - Check if the video is available in your region
421
- - Verify that the video is public
422
- """)
 
1
 
2
+ import streamlit as st
3
+ import yt_dlp
4
+ import os
5
+ from pathlib import Path
6
 
7
+ # Set page config
8
+ st.set_page_config(page_title="YouTube Video Downloader", page_icon="πŸ“Ί")
9
 
10
+ # Set the title of the app
11
+ st.title("YouTube Video Downloader πŸ“Ί")
12
 
13
+ # Create output directory if it doesn't exist
14
+ output_dir = Path("downloads")
15
+ output_dir.mkdir(exist_ok=True)
16
 
17
+ # Input field for YouTube video URL
18
+ video_url = st.text_input("Enter YouTube Video URL:", placeholder="https://www.youtube.com/watch?v=...")
19
 
20
+ # Function to download video
21
+ def download_video(url):
22
+ try:
23
+ ydl_opts = {
24
+ 'format': 'bestvideo+bestaudio/best',
25
+ 'outtmpl': str(output_dir / '%(title)s.%(ext)s'),
26
+ 'merge_output_format': 'webm',
27
+ # Add these options to mimic browser behavior
28
+ 'quiet': True,
29
+ 'no_warnings': True,
30
+ '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',
31
+ 'referer': 'https://www.youtube.com/',
32
+ 'cookiefile': 'youtube.com_cookies.txt', # μΏ ν‚€ 파일 μ‚¬μš©
33
+ 'socket_timeout': 30,
34
+ 'http_chunk_size': 10485760, # 10MB
35
+ }
36
 
37
+ # Progress placeholder
38
+ progress_bar = st.progress(0)
39
+ status_text = st.empty()
40
 
41
+ def progress_hook(d):
42
+ if d['status'] == 'downloading':
43
+ try:
44
+ progress = d['downloaded_bytes'] / d['total_bytes']
45
+ progress_bar.progress(progress)
46
+ status_text.text(f"Downloading: {progress:.1%}")
47
+ except:
48
+ status_text.text("Downloading... (size unknown)")
49
+ elif d['status'] == 'finished':
50
+ progress_bar.progress(1.0)
51
+ status_text.text("Processing...")
52
 
53
+ ydl_opts['progress_hooks'] = [progress_hook]
54
 
55
+ with yt_dlp.YoutubeDL(ydl_opts) as ydl:
56
+ info = ydl.extract_info(url, download=True)
57
+ filename = ydl.prepare_filename(info)
58
+ return filename
59
 
60
+ except Exception as e:
61
+ st.error(f"An error occurred: {str(e)}")
62
+ return None
63
 
64
+ # Download button
65
+ if st.button("Download"):
66
+ if video_url:
67
+ try:
68
+ with st.spinner("Preparing download..."):
69
+ downloaded_file_path = download_video(video_url)
70
 
71
+ if downloaded_file_path and os.path.exists(downloaded_file_path):
72
+ with open(downloaded_file_path, 'rb') as file:
73
+ st.download_button(
74
+ label="Click here to download",
75
+ data=file,
76
+ file_name=os.path.basename(downloaded_file_path),
77
+ mime="application/octet-stream"
78
+ )
79
+ st.success("βœ… Download ready!")
80
+ else:
81
+ st.error("❌ Download failed. Please try again.")
82
+ except Exception as e:
83
+ st.error(f"❌ An error occurred: {str(e)}")
84
+ else:
85
+ st.warning("⚠️ Please enter a valid YouTube URL.")
86
 
87
+ # Help section
88
+ with st.expander("ℹ️ Help & Troubleshooting"):
89
+ st.markdown("""
90
+ **If you're experiencing download issues:**
91
 
92
+ 1. Cookie Authentication Method:
93
+ - Install a browser extension like "Get cookies.txt"
94
+ - Visit YouTube and ensure you're logged in
95
+ - Export cookies to 'youtube.com_cookies.txt'
96
+ - Place the file in the same directory as this app
97
 
98
+ 2. Alternative Solutions:
99
+ - Try different videos
100
+ - Check if the video is public/not age-restricted
101
+ - Try again later if YouTube is blocking requests
102
+ """)
103
 
104
 
105
  # import streamlit as st
 
212
  # """)
213
 
214
 
215
+ # import streamlit as st
216
+ # import yt_dlp
217
+ # import os
218
+ # from pathlib import Path
219
+ # from http.cookiejar import MozillaCookieJar
220
+ # import random
221
+ # import time
222
 
223
+ # # Constants
224
+ # COOKIES_FILE = 'youtube.com_cookies.txt'
225
+ # OUTPUT_DIR = Path("downloads")
226
 
227
+ # # Set page config
228
+ # st.set_page_config(page_title="YouTube Video Downloader", page_icon="πŸ“Ί")
229
+ # st.title("YouTube Video Downloader πŸ“Ί")
230
 
231
+ # # Create output directory if it doesn't exist
232
+ # OUTPUT_DIR.mkdir(exist_ok=True)
233
+
234
+ # def get_random_user_agent():
235
+ # """랜덀 User-Agent 생성"""
236
+ # user_agents = [
237
+ # 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36',
238
+ # 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:122.0) Gecko/20100101 Firefox/122.0',
239
+ # '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',
240
+ # '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'
241
+ # ]
242
+ # return random.choice(user_agents)
243
+
244
+ # def validate_cookies():
245
+ # """μΏ ν‚€ 파일 μœ νš¨μ„± 검사"""
246
+ # if not os.path.exists(COOKIES_FILE):
247
+ # return False, "Cookie file not found"
248
 
249
+ # try:
250
+ # cookie_jar = MozillaCookieJar(COOKIES_FILE)
251
+ # cookie_jar.load()
252
+ # return True, "Cookies loaded successfully"
253
+ # except Exception as e:
254
+ # return False, f"Cookie error: {str(e)}"
255
 
256
+ # def download_with_retry(url, ydl_opts, max_retries=3, delay=5):
257
+ # """μž¬μ‹œλ„ 둜직이 ν¬ν•¨λœ λ‹€μš΄λ‘œλ“œ ν•¨μˆ˜"""
258
+ # last_error = None
259
 
260
+ # for attempt in range(max_retries):
261
+ # try:
262
+ # # 맀 μ‹œλ„λ§ˆλ‹€ μƒˆλ‘œμš΄ User-Agent μ‚¬μš©
263
+ # ydl_opts['headers']['User-Agent'] = get_random_user_agent()
264
 
265
+ # with yt_dlp.YoutubeDL(ydl_opts) as ydl:
266
+ # info = ydl.extract_info(url, download=True)
267
+ # filename = ydl.prepare_filename(info)
268
+ # return filename, info
269
 
270
+ # except yt_dlp.utils.DownloadError as e:
271
+ # last_error = e
272
+ # if "Sign in to confirm you're not a bot" in str(e):
273
+ # if attempt < max_retries - 1:
274
+ # st.warning(f"Bot detection encountered. Retrying in {delay} seconds... (Attempt {attempt + 1}/{max_retries})")
275
+ # time.sleep(delay)
276
+ # delay *= 2 # μ§€μˆ˜ λ°±μ˜€ν”„
277
+ # continue
278
+ # elif "HTTP Error 429" in str(e):
279
+ # if attempt < max_retries - 1:
280
+ # st.warning(f"Rate limit detected. Waiting {delay} seconds... (Attempt {attempt + 1}/{max_retries})")
281
+ # time.sleep(delay * 2) # 레이트 λ¦¬λ°‹μ˜ 경우 더 κΈ΄ λŒ€κΈ° μ‹œκ°„
282
+ # continue
283
+ # except Exception as e:
284
+ # last_error = e
285
+ # if attempt < max_retries - 1:
286
+ # st.warning(f"Error occurred. Retrying... (Attempt {attempt + 1}/{max_retries})")
287
+ # time.sleep(delay)
288
+ # continue
289
 
290
+ # # λͺ¨λ“  μž¬μ‹œλ„ μ‹€νŒ¨ ν›„
291
+ # raise last_error or Exception("Download failed after all retries")
292
 
293
+ # def download_video(url):
294
+ # try:
295
+ # ydl_opts = {
296
+ # 'format': 'bestvideo[ext=mp4]+bestaudio[ext=m4a]/best[ext=mp4]/best',
297
+ # 'outtmpl': str(OUTPUT_DIR / '%(title)s.%(ext)s'),
298
+ # 'merge_output_format': 'mp4',
299
 
300
+ # # μΏ ν‚€ μ„€μ •
301
+ # 'cookiefile': COOKIES_FILE,
302
 
303
+ # # ν–₯μƒλœ μ˜΅μ…˜
304
+ # 'quiet': True,
305
+ # 'no_warnings': True,
306
+ # 'extract_flat': False,
307
+ # 'socket_timeout': 30,
308
+ # 'retries': 10,
309
+ # 'fragment_retries': 10,
310
 
311
+ # # ν–₯μƒλœ 헀더
312
+ # 'headers': {
313
+ # 'User-Agent': get_random_user_agent(),
314
+ # 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8',
315
+ # 'Accept-Language': 'en-US,en;q=0.5',
316
+ # 'Accept-Encoding': 'gzip, deflate, br',
317
+ # 'Connection': 'keep-alive',
318
+ # 'Upgrade-Insecure-Requests': '1',
319
+ # 'Sec-Fetch-Dest': 'document',
320
+ # 'Sec-Fetch-Mode': 'navigate',
321
+ # 'Sec-Fetch-Site': 'none',
322
+ # 'Sec-Fetch-User': '?1',
323
+ # 'DNT': '1',
324
+ # 'Sec-GPC': '1',
325
+ # 'Pragma': 'no-cache',
326
+ # 'Cache-Control': 'no-cache',
327
+ # },
328
 
329
+ # # μΆ”κ°€ μ˜΅μ…˜
330
+ # 'age_limit': None,
331
+ # 'geo_bypass': True,
332
+ # 'geo_bypass_country': 'US',
333
+ # 'sleep_interval': 2,
334
+ # 'max_sleep_interval': 5,
335
+ # }
336
 
337
+ # # 진행 상황 ν‘œμ‹œ
338
+ # progress_bar = st.progress(0)
339
+ # status_text = st.empty()
340
 
341
+ # def progress_hook(d):
342
+ # if d['status'] == 'downloading':
343
+ # try:
344
+ # progress = d['downloaded_bytes'] / d['total_bytes']
345
+ # progress_bar.progress(progress)
346
+ # status_text.text(f"Downloading: {progress:.1%} | Speed: {d.get('speed_str', 'N/A')} | ETA: {d.get('eta_str', 'N/A')}")
347
+ # except:
348
+ # status_text.text(f"Downloading... Speed: {d.get('speed_str', 'N/A')}")
349
+ # elif d['status'] == 'finished':
350
+ # progress_bar.progress(1.0)
351
+ # status_text.text("Processing downloaded files...")
352
 
353
+ # ydl_opts['progress_hooks'] = [progress_hook]
354
 
355
+ # return download_with_retry(url, ydl_opts)
356
 
357
+ # except Exception as e:
358
+ # raise Exception(f"Download error: {str(e)}")
359
 
360
+ # # μΏ ν‚€ μƒνƒœ 확인
361
+ # cookie_valid, cookie_message = validate_cookies()
362
+ # if not cookie_valid:
363
+ # st.warning(f"⚠️ Cookie Issue: {cookie_message}")
364
+ # else:
365
+ # st.success("βœ… Cookies loaded successfully")
366
 
367
+ # # λΉ„λ””μ˜€ URL μž…λ ₯
368
+ # video_url = st.text_input("Enter YouTube Video URL:", placeholder="https://www.youtube.com/watch?v=...")
369
 
370
+ # # λ‹€μš΄λ‘œλ“œ λ²„νŠΌ
371
+ # if st.button("Download Video"):
372
+ # if not video_url:
373
+ # st.warning("⚠️ Please enter a valid YouTube URL.")
374
+ # elif not cookie_valid:
375
+ # st.error("❌ Cannot proceed without valid cookies.")
376
+ # else:
377
+ # try:
378
+ # with st.spinner("Preparing download..."):
379
+ # downloaded_file_path, video_info = download_video(video_url)
380
 
381
+ # if downloaded_file_path and os.path.exists(downloaded_file_path):
382
+ # file_size = os.path.getsize(downloaded_file_path)
383
+ # st.info(f"""
384
+ # **Download Complete!**
385
+ # - File: {os.path.basename(downloaded_file_path)}
386
+ # - Size: {file_size / (1024*1024):.1f} MB
387
+ # """)
388
 
389
+ # with open(downloaded_file_path, 'rb') as file:
390
+ # st.download_button(
391
+ # label="πŸ’Ύ Download Video",
392
+ # data=file,
393
+ # file_name=os.path.basename(downloaded_file_path),
394
+ # mime="video/mp4"
395
+ # )
396
+ # else:
397
+ # st.error("❌ Download failed. Please try again.")
398
+ # except Exception as e:
399
+ # st.error(f"❌ {str(e)}")
400
+
401
+ # # Help μ„Ήμ…˜
402
+ # with st.expander("ℹ️ Troubleshooting Guide"):
403
+ # st.markdown(f"""
404
+ # **Current Status:**
405
+ # - Cookie File: {'βœ… Found' if os.path.exists(COOKIES_FILE) else '❌ Not Found'}
406
+ # - Cookie Validity: {'βœ… Valid' if cookie_valid else '❌ Invalid'}
407
 
408
+ # **Common Issues:**
409
+ # 1. Bot Detection:
410
+ # - The app will automatically retry with different settings
411
+ # - Each retry uses a random User-Agent and headers
412
 
413
+ # 2. Cookie Problems:
414
+ # - Current cookie file: {COOKIES_FILE}
415
+ # - Make sure the cookie file is up to date
416
+ # - Export fresh cookies when issues persist
417
 
418
+ # 3. Download Fails:
419
+ # - Try again after a few minutes
420
+ # - Check if the video is available in your region
421
+ # - Verify that the video is public
422
+ # """)