diff --git a/openshorts/main.py b/openshorts/main.py index 7c1428d..6dc47d2 100644 --- a/openshorts/main.py +++ b/openshorts/main.py @@ -457,7 +457,6 @@ def get_video_resolution(video_path): cap.release() return width, height - def sanitize_filename(filename): """Remove invalid characters from filename.""" filename = re.sub(r'[<>:"/\\|?*#]', '', filename) @@ -467,131 +466,65 @@ def sanitize_filename(filename): def download_youtube_video(url, output_dir="."): """ - Downloads a YouTube video using yt-dlp. - Returns the path to the downloaded video and the video title. + Downloads a YouTube video using the custom fixed yt-dl wrapper in /home/ren/yt/yt-dl. """ - print(f"🔍 Debug: yt-dlp version: {yt_dlp.version.__version__}") - print("📥 Downloading video from YouTube...") + import subprocess + import json + import time + import sys + + print("📥 Downloading video from YouTube using custom solver...") step_start_time = time.time() - - cookies_path = '/app/cookies.txt' - cookies_env = os.environ.get("YOUTUBE_COOKIES") - if cookies_env: - print("🍪 Found YOUTUBE_COOKIES env var, creating cookies file inside container...") - try: - with open(cookies_path, 'w') as f: - f.write(cookies_env) - if os.path.exists(cookies_path): - print(f" Debug: Cookies file created. Size: {os.path.getsize(cookies_path)} bytes") - with open(cookies_path, 'r') as f: - content = f.read(100) - print(f" Debug: First 100 chars of cookie file: {content}") - except Exception as e: - print(f"⚠️ Failed to write cookies file: {e}") - cookies_path = None - else: - cookies_path = None - print("⚠️ YOUTUBE_COOKIES env var not found.") - # Common yt-dlp options to work around YouTube bot detection. - # extractor_args tries multiple player clients in order; tv_embed / android - # avoid the OAuth/PO-token checks that block server IPs. - _COMMON_YDL_OPTS = { - 'quiet': False, - 'verbose': True, - 'no_warnings': False, - 'cookiefile': cookies_path if cookies_path else None, - 'socket_timeout': 30, - 'retries': 10, - 'fragment_retries': 10, - 'nocheckcertificate': True, - 'cachedir': False, - 'extractor_args': { - 'youtube': { - 'player_client': ['tv_embed', 'android', 'mweb', 'web'], - 'player_skip': ['webpage', 'configs'], - } - }, - 'http_headers': { - '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' - ), - }, - } - - with yt_dlp.YoutubeDL(_COMMON_YDL_OPTS) as ydl: - try: - info = ydl.extract_info(url, download=False) - video_title = info.get('title', 'youtube_video') - sanitized_title = sanitize_filename(video_title) - except Exception as e: - # Force print to stderr/stdout immediately so it's captured before crash - import sys - import traceback - - # Print minimal error first to ensure something gets out - print("🚨 YOUTUBE DOWNLOAD ERROR 🚨", file=sys.stderr) - - error_msg = f""" - -❌ ================================================================= ❌ -❌ FATAL ERROR: YOUTUBE DOWNLOAD FAILED -❌ ================================================================= ❌ - -REASON: YouTube has blocked the download request (Error 429/Unavailable). - This is likely a temporary IP ban on this server. - -👇 SOLUTION FOR USER 👇 ---------------------------------------------------------------------- -1. Download the video manually to your computer. -2. Use the 'Upload Video' tab in this app to process it. ---------------------------------------------------------------------- - -Technical Details: {str(e)} - """ - # Print to both streams to ensure capture - print(error_msg, file=sys.stdout) - print(error_msg, file=sys.stderr) - - # Force flush - sys.stdout.flush() - sys.stderr.flush() - - # Wait a split second to allow buffer to drain before raising - time.sleep(0.5) - - raise e + # Ensure output_dir is absolute path + abs_output_dir = os.path.abspath(output_dir) - output_template = os.path.join(output_dir, f'{sanitized_title}.%(ext)s') - expected_file = os.path.join(output_dir, f'{sanitized_title}.mp4') - if os.path.exists(expected_file): - os.remove(expected_file) - print(f"🗑️ Removed existing file to re-download with H.264 codec") + # We run the command with --json to get structured output + cmd = [ + "/home/ren/yt/venv/bin/python", + "/home/ren/yt/yt-dl", + "--cookies", "/home/ren/yt/cookies.txt", + "--json", + "-o", abs_output_dir, + url + ] - ydl_opts = { - **_COMMON_YDL_OPTS, - 'format': 'bestvideo[vcodec^=avc1][ext=mp4]+bestaudio[ext=m4a]/bestvideo[vcodec^=avc1]+bestaudio/best[ext=mp4]/best', - 'outtmpl': output_template, - 'merge_output_format': 'mp4', - 'overwrites': True, - } - - with yt_dlp.YoutubeDL(ydl_opts) as ydl: - ydl.download([url]) - - downloaded_file = os.path.join(output_dir, f'{sanitized_title}.mp4') - - if not os.path.exists(downloaded_file): - for f in os.listdir(output_dir): - if f.startswith(sanitized_title) and f.endswith('.mp4'): - downloaded_file = os.path.join(output_dir, f) - break + print(f" Executing: {' '.join(cmd)}") + result = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) + if result.returncode != 0: + print("🚨 YOUTUBE DOWNLOAD ERROR 🚨", file=sys.stderr) + print(result.stderr, file=sys.stderr) + raise Exception(f"YouTube download failed: {result.stderr}") + + try: + output_data = json.loads(result.stdout.strip()) + downloaded_file = output_data['filepath'] + video_title = output_data.get('title', 'youtube_video') + sanitized_title = sanitize_filename(video_title) + except Exception as e: + print(f"⚠️ JSON parsing failed, trying fallback detection. Error: {e}") + print(f" Raw stdout: {result.stdout}") + + # Fallback parsing: if not JSON, the script prints the absolute filepath to stdout + downloaded_file = result.stdout.strip() + if not os.path.exists(downloaded_file): + # Search output_dir for files created recently + downloaded_file = None + for file in os.listdir(abs_output_dir): + if file.endswith('.mp4'): + full_path = os.path.join(abs_output_dir, file) + if time.time() - os.path.getmtime(full_path) < 60: + downloaded_file = full_path + break + + if not downloaded_file or not os.path.exists(downloaded_file): + raise Exception("Failed to locate downloaded video file.") + + sanitized_title = sanitize_filename(os.path.splitext(os.path.basename(downloaded_file))[0]) + step_end_time = time.time() print(f"✅ Video downloaded in {step_end_time - step_start_time:.2f}s: {downloaded_file}") - return downloaded_file, sanitized_title def process_video_to_vertical(input_video, final_output_video, mute_ranges=None):