Spaces:
Running
Running
from pathlib import Path | |
import yt_dlp | |
import os | |
from concurrent.futures import ThreadPoolExecutor | |
import logging | |
import shutil | |
class LinkDownloader: | |
def __init__(self, output_dir, max_workers=2): | |
self.output_dir = Path(output_dir) | |
self.output_dir.mkdir(parents=True, exist_ok=True) | |
self.executor = ThreadPoolExecutor(max_workers=max_workers) | |
# Setup logging | |
logging.basicConfig( | |
level=logging.INFO, | |
format='%(asctime)s - %(levelname)s - %(message)s' | |
) | |
self.logger = logging.getLogger(__name__) | |
def download_from_url(self, url): | |
"""Downloads a video from the given URL using yt_dlp.""" | |
# Create a unique subdirectory to prevent file conflicts | |
unique_download_dir = self.output_dir / "download" | |
unique_download_dir.mkdir(parents=True, exist_ok=True) | |
temp_filepath = str(unique_download_dir / 'downloaded_video.%(ext)s') | |
ydl_opts = { | |
'format': 'best[height<=1080]', | |
'outtmpl': temp_filepath, | |
'quiet': False, | |
'no_warnings': True, | |
'extract_audio': False, | |
'concurrent_fragment_downloads': 3, | |
'file_access_retries': 3, | |
'retries': 3, | |
'socket_timeout': 30, | |
'nocheckcertificate': True, | |
} | |
try: | |
with yt_dlp.YoutubeDL(ydl_opts) as ydl: | |
# Get video information | |
info = ydl.extract_info(url, download=False) | |
ext = info.get('ext', 'mp4') | |
# Submit download task to executor | |
future = self.executor.submit(ydl.download, [url]) | |
future.result() # Wait for download to complete | |
# Find the downloaded file | |
downloaded_files = list(unique_download_dir.glob(f'downloaded_video.{ext}')) | |
if not downloaded_files: | |
self.logger.error("No files found after download.") | |
return None | |
# Take the first file (should be the only one) | |
actual_filepath = str(downloaded_files[0]) | |
if os.path.exists(actual_filepath): | |
# Copy the file to the main output directory to prevent deletion | |
final_filepath = str(self.output_dir / f"downloaded_video.{ext}") | |
shutil.copy2(actual_filepath, final_filepath) | |
self.logger.info(f"Download completed: {final_filepath}") | |
return final_filepath | |
else: | |
self.logger.error("File not found after download.") | |
return None | |
except yt_dlp.DownloadError as e: | |
self.logger.error(f"yt_dlp error: {e}") | |
except Exception as e: | |
self.logger.error(f"Unexpected error: {e}") | |
return None | |
def __del__(self): | |
"""Ensure proper shutdown of the executor.""" | |
self.executor.shutdown(wait=True) | |
self.logger.info("ThreadPoolExecutor shut down.") | |