prasanth.thangavel
Add option for public deployment without authentication (i.e. no cookies)
0d1e776
import logging | |
import gradio as gr | |
from yt_dlp import YoutubeDL | |
import os | |
from dotenv import load_dotenv | |
from pathlib import Path | |
import tempfile | |
import shutil | |
from pathlib import Path | |
# Configure logging | |
logging.basicConfig( | |
level=logging.INFO, | |
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', | |
handlers=[ | |
#logging.FileHandler('youtube_downloader.log'), | |
logging.StreamHandler() | |
] | |
) | |
# Create logger instance | |
logger = logging.getLogger('youtube_downloader') | |
def cookies_to_env(cookie_file_path: str) -> str: | |
"""Convert cookie file content to environment variable format""" | |
try: | |
with open(cookie_file_path, 'r') as f: | |
lines = f.readlines() | |
# Keep header comments | |
header = [line.strip() for line in lines if line.startswith('#')] | |
# Get cookie content (non-comment lines) | |
cookies = [line.strip() for line in lines if line.strip() and not line.startswith('#')] | |
# Join with escaped newlines | |
content = '\\n'.join(header + [''] + cookies) # Empty line after headers | |
# Create env file content | |
return f'FIREFOX_COOKIES="{content}"' | |
except Exception as e: | |
raise ValueError(f"Error converting cookie file: {str(e)}") | |
def env_to_cookies(env_content: str, output_file: str) -> None: | |
"""Convert environment variable content back to cookie file""" | |
try: | |
# Extract content from env format | |
if '="' not in env_content: | |
raise ValueError("Invalid env content format") | |
content = env_content.split('="', 1)[1].strip('"') | |
# Replace escaped newlines with actual newlines | |
cookie_content = content.replace('\\n', '\n') | |
# Write to cookie file | |
with open(output_file, 'w') as f: | |
f.write(cookie_content) | |
except Exception as e: | |
raise ValueError(f"Error converting to cookie file: {str(e)}") | |
def save_to_env_file(env_content: str, env_file: str = '.env') -> None: | |
"""Save environment variable content to .env file""" | |
try: | |
with open(env_file, 'w') as f: | |
f.write(env_content) | |
#print(f"Successfully saved to {env_file}") | |
except Exception as e: | |
raise ValueError(f"Error saving to env file: {str(e)}") | |
def env_to_cookies_from_env(output_file: str) -> None: | |
"""Convert environment variable from .env file to cookie file""" | |
try: | |
load_dotenv() # Load from .env file | |
env_content = os.getenv('FIREFOX_COOKIES', "") | |
#print(f"Printing env content: \n{env_content}") | |
if not env_content: | |
raise ValueError("FIREFOX_COOKIES not found in .env file") | |
env_to_cookies(f'FIREFOX_COOKIES="{env_content}"', output_file) | |
except Exception as e: | |
raise ValueError(f"Error converting to cookie file: {str(e)}") | |
def get_cookies(): | |
"""Get cookies from environment variable""" | |
load_dotenv() | |
cookie_content = os.getenv('FIREFOX_COOKIES', "") | |
#print(cookie_content) | |
if not cookie_content: | |
raise ValueError("FIREFOX_COOKIES environment variable not set") | |
return cookie_content | |
def create_temp_cookie_file(): | |
"""Create temporary cookie file from environment variable""" | |
temp_cookie = tempfile.NamedTemporaryFile(mode='w+', delete=False, suffix='.txt') | |
try: | |
cookie_content = get_cookies() | |
# Replace escaped newlines with actual newlines | |
cookie_content = cookie_content.replace('\\n', '\n') | |
temp_cookie.write() | |
temp_cookie.flush() | |
return Path(temp_cookie.name) | |
finally: | |
temp_cookie.close() | |
def download_for_browser(url, mode='audio', quality='high'): | |
if not url: | |
return None, "Please enter a valid URL" | |
logger.info(f"Downloading {url} in {mode} mode with {quality} quality") | |
# Create temporary directory that persists until file is served | |
temp_dir = Path(tempfile.mkdtemp()) | |
# Convert cookie file to env and save locally | |
#cookie_file = "cookies.firefox-private.txt" | |
#env_content = cookies_to_env(cookie_file) | |
#save_to_env_file(env_content) | |
# Convert back to cookie file using .env | |
cookiefile = "firefox-cookies.txt" | |
env_to_cookies_from_env("firefox-cookies.txt") | |
try: | |
# Configure download options | |
opts = { | |
'format': 'bestaudio/best' if mode == 'audio' else 'bestvideo+bestaudio/best', | |
'outtmpl': str(temp_dir / '%(title)s.%(ext)s'), | |
'restrictfilenames': True, | |
'windowsfilenames': True, | |
'quiet': True, | |
'no_warnings': True | |
} | |
# Add format-specific options | |
if mode == 'audio': | |
opts.update({ | |
'postprocessors': [{ | |
'key': 'FFmpegExtractAudio', | |
'preferredcodec': 'mp3', | |
'preferredquality': '320' if quality == 'high' else '192', | |
}], | |
'prefer_ffmpeg': True, | |
'keepvideo': False | |
}) | |
else: | |
opts.update({ | |
'format': 'bestvideo+bestaudio/best' if quality == 'high' else 'best[height<=720]', | |
'merge_output_format': 'mp4' | |
}) | |
# Add cookies | |
load_dotenv() | |
USE_FIREFOX_COOKIES = os.getenv("USE_FIREFOX_COOKIES", "False") | |
logger.info(f"Using Firefox cookies: {USE_FIREFOX_COOKIES}") | |
if USE_FIREFOX_COOKIES == "True": | |
opts["cookiefile"] = "firefox-cookies.txt" #create_temp_cookie_file() | |
else: | |
opts["no_cookies"] = True # Forces yt-dlp to access YouTube without any authentication | |
# Download file | |
logger.info(f"Downloading {url} with options: {opts}") | |
with YoutubeDL(opts) as ydl: | |
info = ydl.extract_info(url, download=True) | |
# Find downloaded file | |
files = list(temp_dir.glob('*')) | |
if not files: | |
return None, "Download failed - no files found" | |
download_file = files[0] | |
if not download_file.exists(): | |
return None, f"File not found after download: {download_file.name}" | |
# Return file while it exists in temp directory | |
return str(download_file), f"Successfully converted: {download_file.name}" | |
except Exception as e: | |
if temp_dir.exists(): | |
shutil.rmtree(temp_dir) | |
# Return raw error message without formatting | |
error_msg = str(e) | |
if "ERROR: [youtube]" in error_msg: | |
# Extract everything after "ERROR: [youtube]" | |
error_msg = error_msg.split("ERROR: [youtube]")[1].strip() | |
logger.error(f"Download error: {error_msg}") | |
return None, error_msg | |
def create_browser_ui(): | |
with gr.Blocks(title="YouTube Downloader", theme=gr.themes.Soft()) as demo: | |
gr.Markdown("""## <span style='display: flex; align-items: center; gap: 10px;'><img src='https://cdn-icons-png.flaticon.com/512/1384/1384060.png' width='25'/> <img src='https://upload.wikimedia.org/wikipedia/commons/thumb/6/6a/Youtube_Music_icon.svg/2048px-Youtube_Music_icon.svg.png' width='25'/>YouTube Downloader</span>""") | |
with gr.Row(): | |
with gr.Column(scale=1): | |
url_input = gr.Textbox( | |
label="YouTube URL", | |
placeholder="https://youtube.com/watch?v=...", | |
scale=1 | |
) | |
mode_input = gr.Radio( | |
choices=["audio", "video"], | |
value="audio", | |
label="Format" | |
) | |
quality_input = gr.Radio( | |
choices=["high", "medium"], | |
value="high", | |
label="Quality" | |
) | |
download_button = gr.Button("Convert", variant="primary") | |
with gr.Column(scale=1): | |
status_text = gr.Textbox(label="Converting Status", interactive=False) | |
output_file = gr.File(label="Download to your device ...") | |
download_button.click( | |
fn=download_for_browser, | |
inputs=[url_input, mode_input, quality_input], | |
outputs=[output_file, status_text] | |
) | |
return demo | |
demo = create_browser_ui() | |
demo.launch( | |
share=False, | |
debug=True, | |
show_error=True | |
) |