# import streamlit as st # import yt_dlp # import os # from pathlib import Path # # Set page config # st.set_page_config(page_title="YouTube Video Downloader", page_icon="πŸ“Ί") # # Set the title of the app # st.title("YouTube Video Downloader πŸ“Ί") # # Create output directory if it doesn't exist # output_dir = Path("downloads") # output_dir.mkdir(exist_ok=True) # # Input field for YouTube video URL # video_url = st.text_input("Enter YouTube Video URL:", placeholder="https://www.youtube.com/watch?v=...") # # Function to download video # def download_video(url): # try: # ydl_opts = { # 'format': 'bestvideo+bestaudio/best', # 'outtmpl': str(output_dir / '%(title)s.%(ext)s'), # 'merge_output_format': 'webm', # # Add these options to mimic browser behavior # 'quiet': True, # 'no_warnings': True, # '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', # 'referer': 'https://www.youtube.com/', # 'cookiefile': 'youtube.com_cookies.txt', # μΏ ν‚€ 파일 μ‚¬μš© # 'socket_timeout': 30, # 'http_chunk_size': 10485760, # 10MB # } # # Progress placeholder # progress_bar = st.progress(0) # status_text = st.empty() # def progress_hook(d): # if d['status'] == 'downloading': # try: # progress = d['downloaded_bytes'] / d['total_bytes'] # progress_bar.progress(progress) # status_text.text(f"Downloading: {progress:.1%}") # except: # status_text.text("Downloading... (size unknown)") # elif d['status'] == 'finished': # progress_bar.progress(1.0) # status_text.text("Processing...") # ydl_opts['progress_hooks'] = [progress_hook] # with yt_dlp.YoutubeDL(ydl_opts) as ydl: # info = ydl.extract_info(url, download=True) # filename = ydl.prepare_filename(info) # return filename # except Exception as e: # st.error(f"An error occurred: {str(e)}") # return None # # Download button # if st.button("Download"): # if video_url: # try: # with st.spinner("Preparing download..."): # downloaded_file_path = download_video(video_url) # if downloaded_file_path and os.path.exists(downloaded_file_path): # with open(downloaded_file_path, 'rb') as file: # st.download_button( # label="Click here to download", # data=file, # file_name=os.path.basename(downloaded_file_path), # mime="application/octet-stream" # ) # st.success("βœ… Download ready!") # else: # st.error("❌ Download failed. Please try again.") # except Exception as e: # st.error(f"❌ An error occurred: {str(e)}") # else: # st.warning("⚠️ Please enter a valid YouTube URL.") # # Help section # with st.expander("ℹ️ Help & Troubleshooting"): # st.markdown(""" # **If you're experiencing download issues:** # 1. Cookie Authentication Method: # - Install a browser extension like "Get cookies.txt" # - Visit YouTube and ensure you're logged in # - Export cookies to 'youtube.com_cookies.txt' # - Place the file in the same directory as this app # 2. Alternative Solutions: # - Try different videos # - Check if the video is public/not age-restricted # - Try again later if YouTube is blocking requests # """) # import streamlit as st # import yt_dlp # import os # from pathlib import Path # # Set page config # st.set_page_config(page_title="YouTube Video Downloader", page_icon="πŸ“Ί") # # Set the title of the app # st.title("YouTube Video Downloader πŸ“Ί") # # Create output directory if it doesn't exist # output_dir = Path("downloads") # output_dir.mkdir(exist_ok=True) # # Check if cookies file exists # COOKIES_FILE = 'youtube.com_cookies.txt' # has_cookies = os.path.exists(COOKIES_FILE) # if has_cookies: # st.success("βœ… Cookie file detected") # else: # st.warning("⚠️ No cookie file found - Some videos might be restricted") # # Input field for YouTube video URL # video_url = st.text_input("Enter YouTube Video URL:", placeholder="https://www.youtube.com/watch?v=...") # def download_video(url): # try: # ydl_opts = { # 'format': 'bestvideo+bestaudio/best', # 'outtmpl': str(output_dir / '%(title)s.%(ext)s'), # 'merge_output_format': 'webm', # 'quiet': True, # 'no_warnings': True, # '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', # 'referer': 'https://www.youtube.com/', # 'http_chunk_size': 10485760 # } # # Add cookies file if available # if has_cookies: # ydl_opts['cookiefile'] = COOKIES_FILE # # Progress placeholder # progress_bar = st.progress(0) # status_text = st.empty() # def progress_hook(d): # if d['status'] == 'downloading': # try: # progress = d['downloaded_bytes'] / d['total_bytes'] # progress_bar.progress(progress) # status_text.text(f"Downloading: {progress:.1%}") # except: # status_text.text("Downloading... (size unknown)") # elif d['status'] == 'finished': # progress_bar.progress(1.0) # status_text.text("Processing...") # ydl_opts['progress_hooks'] = [progress_hook] # with yt_dlp.YoutubeDL(ydl_opts) as ydl: # info = ydl.extract_info(url, download=True) # filename = ydl.prepare_filename(info) # return filename # except Exception as e: # st.error(f"An error occurred: {str(e)}") # return None # # Download button # if st.button("Download"): # if video_url: # try: # with st.spinner("Preparing download..."): # downloaded_file_path = download_video(video_url) # if downloaded_file_path and os.path.exists(downloaded_file_path): # with open(downloaded_file_path, 'rb') as file: # st.download_button( # label="Click here to download", # data=file, # file_name=os.path.basename(downloaded_file_path), # mime="application/octet-stream" # ) # st.success("βœ… Download ready!") # else: # st.error("❌ Download failed. Please try again.") # except Exception as e: # st.error(f"❌ An error occurred: {str(e)}") # else: # st.warning("⚠️ Please enter a valid YouTube URL.") # # Help section # with st.expander("ℹ️ Help & Information"): # st.markdown(""" # **About Cookie Authentication:** # - This app uses cookie authentication to bypass YouTube's bot detection # - Cookies help the app behave like a logged-in browser # - No personal data is stored or transmitted # **If downloads fail:** # 1. Check if the video is public # 2. Verify the URL is correct # 3. Try a different video # 4. Try again later if YouTube is blocking requests # """) import streamlit as st import yt_dlp import os from pathlib import Path from http.cookiejar import MozillaCookieJar import random import time # Constants COOKIES_FILE = 'youtube.com_cookies.txt' OUTPUT_DIR = Path("downloads") # Set page config st.set_page_config(page_title="YouTube Video Downloader", page_icon="πŸ“Ί") st.title("YouTube Video Downloader πŸ“Ί") # Create output directory if it doesn't exist OUTPUT_DIR.mkdir(exist_ok=True) def get_random_user_agent(): """랜덀 User-Agent 생성""" user_agents = [ 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:122.0) Gecko/20100101 Firefox/122.0', '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', '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' ] return random.choice(user_agents) def validate_cookies(): """μΏ ν‚€ 파일 μœ νš¨μ„± 검사""" if not os.path.exists(COOKIES_FILE): return False, "Cookie file not found" try: cookie_jar = MozillaCookieJar(COOKIES_FILE) cookie_jar.load() return True, "Cookies loaded successfully" except Exception as e: return False, f"Cookie error: {str(e)}" def download_with_retry(url, ydl_opts, max_retries=3, delay=5): """μž¬μ‹œλ„ 둜직이 ν¬ν•¨λœ λ‹€μš΄λ‘œλ“œ ν•¨μˆ˜""" last_error = None for attempt in range(max_retries): try: # 맀 μ‹œλ„λ§ˆλ‹€ μƒˆλ‘œμš΄ User-Agent μ‚¬μš© ydl_opts['headers']['User-Agent'] = get_random_user_agent() with yt_dlp.YoutubeDL(ydl_opts) as ydl: info = ydl.extract_info(url, download=True) filename = ydl.prepare_filename(info) return filename, info except yt_dlp.utils.DownloadError as e: last_error = e if "Sign in to confirm you're not a bot" in str(e): if attempt < max_retries - 1: st.warning(f"Bot detection encountered. Retrying in {delay} seconds... (Attempt {attempt + 1}/{max_retries})") time.sleep(delay) delay *= 2 # μ§€μˆ˜ λ°±μ˜€ν”„ continue elif "HTTP Error 429" in str(e): if attempt < max_retries - 1: st.warning(f"Rate limit detected. Waiting {delay} seconds... (Attempt {attempt + 1}/{max_retries})") time.sleep(delay * 2) # 레이트 λ¦¬λ°‹μ˜ 경우 더 κΈ΄ λŒ€κΈ° μ‹œκ°„ continue except Exception as e: last_error = e if attempt < max_retries - 1: st.warning(f"Error occurred. Retrying... (Attempt {attempt + 1}/{max_retries})") time.sleep(delay) continue # λͺ¨λ“  μž¬μ‹œλ„ μ‹€νŒ¨ ν›„ raise last_error or Exception("Download failed after all retries") def download_video(url): try: ydl_opts = { 'format': 'bestvideo[ext=mp4]+bestaudio[ext=m4a]/best[ext=mp4]/best', 'outtmpl': str(OUTPUT_DIR / '%(title)s.%(ext)s'), 'merge_output_format': 'mp4', # μΏ ν‚€ μ„€μ • 'cookiefile': COOKIES_FILE, # ν–₯μƒλœ μ˜΅μ…˜ 'quiet': True, 'no_warnings': True, 'extract_flat': False, 'socket_timeout': 30, 'retries': 10, 'fragment_retries': 10, # ν–₯μƒλœ 헀더 'headers': { 'User-Agent': get_random_user_agent(), 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8', 'Accept-Language': 'en-US,en;q=0.5', 'Accept-Encoding': 'gzip, deflate, br', 'Connection': 'keep-alive', 'Upgrade-Insecure-Requests': '1', 'Sec-Fetch-Dest': 'document', 'Sec-Fetch-Mode': 'navigate', 'Sec-Fetch-Site': 'none', 'Sec-Fetch-User': '?1', 'DNT': '1', 'Sec-GPC': '1', 'Pragma': 'no-cache', 'Cache-Control': 'no-cache', }, # μΆ”κ°€ μ˜΅μ…˜ 'age_limit': None, 'geo_bypass': True, 'geo_bypass_country': 'US', 'sleep_interval': 2, 'max_sleep_interval': 5, } # 진행 상황 ν‘œμ‹œ progress_bar = st.progress(0) status_text = st.empty() def progress_hook(d): if d['status'] == 'downloading': try: progress = d['downloaded_bytes'] / d['total_bytes'] progress_bar.progress(progress) status_text.text(f"Downloading: {progress:.1%} | Speed: {d.get('speed_str', 'N/A')} | ETA: {d.get('eta_str', 'N/A')}") except: status_text.text(f"Downloading... Speed: {d.get('speed_str', 'N/A')}") elif d['status'] == 'finished': progress_bar.progress(1.0) status_text.text("Processing downloaded files...") ydl_opts['progress_hooks'] = [progress_hook] return download_with_retry(url, ydl_opts) except Exception as e: raise Exception(f"Download error: {str(e)}") # μΏ ν‚€ μƒνƒœ 확인 cookie_valid, cookie_message = validate_cookies() if not cookie_valid: st.warning(f"⚠️ Cookie Issue: {cookie_message}") else: st.success("βœ… Cookies loaded successfully") # λΉ„λ””μ˜€ URL μž…λ ₯ video_url = st.text_input("Enter YouTube Video URL:", placeholder="https://www.youtube.com/watch?v=...") # λ‹€μš΄λ‘œλ“œ λ²„νŠΌ if st.button("Download Video"): if not video_url: st.warning("⚠️ Please enter a valid YouTube URL.") elif not cookie_valid: st.error("❌ Cannot proceed without valid cookies.") else: try: with st.spinner("Preparing download..."): downloaded_file_path, video_info = download_video(video_url) if downloaded_file_path and os.path.exists(downloaded_file_path): file_size = os.path.getsize(downloaded_file_path) st.info(f""" **Download Complete!** - File: {os.path.basename(downloaded_file_path)} - Size: {file_size / (1024*1024):.1f} MB """) with open(downloaded_file_path, 'rb') as file: st.download_button( label="πŸ’Ύ Download Video", data=file, file_name=os.path.basename(downloaded_file_path), mime="video/mp4" ) else: st.error("❌ Download failed. Please try again.") except Exception as e: st.error(f"❌ {str(e)}") # Help μ„Ήμ…˜ with st.expander("ℹ️ Troubleshooting Guide"): st.markdown(f""" **Current Status:** - Cookie File: {'βœ… Found' if os.path.exists(COOKIES_FILE) else '❌ Not Found'} - Cookie Validity: {'βœ… Valid' if cookie_valid else '❌ Invalid'} **Common Issues:** 1. Bot Detection: - The app will automatically retry with different settings - Each retry uses a random User-Agent and headers 2. Cookie Problems: - Current cookie file: {COOKIES_FILE} - Make sure the cookie file is up to date - Export fresh cookies when issues persist 3. Download Fails: - Try again after a few minutes - Check if the video is available in your region - Verify that the video is public """)