prasanth.thangavel
Change app.py to older approach, and update README
ee37f40
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())
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'
})
load_dotenv()
USE_FIREFOX_COOKIES = os.getenv("USE_FIREFOX_COOKIES", "False")
if USE_FIREFOX_COOKIES == "True":
# 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")
# Add cookies
logger.info(f"Using Firefox cookies: {USE_FIREFOX_COOKIES}")
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
logger.info(f"Downloaded file: {download_file.name}")
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 ...")
gr.Markdown("""### ⓘ Note:
The downloader currently works only when running locally.<br>
To run it in HF deployed space, cookies are required to access YouTube.<br>
For more details, visit [README](https://huggingface.co/spaces/prasanthntu/youtube-downloader/blob/main/README.md)""", label="Note", show_label=True, container=True)
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
)