#!/usr/bin/env python3 """ Pornhub Video Downloader Side-Script Uses yt-dlp under the hood for highly robust and fast downloads. """ import sys import os import subprocess # Ensure yt-dlp and curl-cffi are installed try: import yt_dlp import curl_cffi except ImportError: print("[-] yt-dlp or curl-cffi is not fully installed in this Python environment.") print("[*] Attempting to install yt-dlp with curl-cffi automatically...") try: subprocess.check_call([sys.executable, "-m", "pip", "install", "--upgrade", "yt-dlp[default,curl-cffi]"]) import yt_dlp import curl_cffi print("[+] Successfully installed yt-dlp and curl-cffi!") except Exception as e: print(f"[!] Failed to install dependencies automatically: {e}") print("[!] Please install manually with: pip install \"yt-dlp[default,curl-cffi]\"") sys.exit(1) import argparse def download_video(url, output_dir=".", quality="best"): print(f"[*] Initializing download for: {url}") print(f"[*] Output directory: {os.path.abspath(output_dir)}") if not os.path.exists(output_dir): os.makedirs(output_dir, exist_ok=True) # Format selector configuration # 'best' is the safest format because it downloads pre-merged streams and doesn't strictly require ffmpeg. # 'bestvideo+bestaudio/best' will download separate video/audio streams and merge them if ffmpeg is present. format_selector = 'best' if quality != 'best': if quality.endswith('p'): height = quality[:-1] format_selector = f'bestvideo[height<={height}]+bestaudio/best[height<={height}]' else: format_selector = quality ydl_opts = { 'format': format_selector, 'outtmpl': os.path.join(output_dir, '%(title)s [%(id)s].%(ext)s'), 'noplaylist': True, # Pornhub has age gates; standard user-agent and headers are handled by yt-dlp automatically, # but let's add some basic robust options. 'ignoreerrors': False, 'logtostderr': False, 'quiet': False, 'no_warnings': False, 'nocheckcertificate': True, } # Setup browser impersonation to bypass Cloudflare/TLS fingerprinting blocks (e.g. HTTP 403 Forbidden) try: from yt_dlp.networking.impersonate import ImpersonateTarget ydl_opts['impersonate'] = ImpersonateTarget.from_str('chrome') except (ImportError, AttributeError): ydl_opts['impersonate'] = 'chrome' try: with yt_dlp.YoutubeDL(ydl_opts) as ydl: info = ydl.extract_info(url, download=True) filename = ydl.prepare_filename(info) print("\n[+] Download completed successfully!") print(f"[+] File saved to: {filename}") return True except Exception as e: print(f"\n[!] Error downloading video: {e}") # Try a safe fallback to 'best' format if we tried a complex format query if format_selector != 'best': print("[*] Retrying with fallback quality ('best')...") ydl_opts['format'] = 'best' try: with yt_dlp.YoutubeDL(ydl_opts) as ydl: info = ydl.extract_info(url, download=True) filename = ydl.prepare_filename(info) print("\n[+] Download completed successfully via fallback!") print(f"[+] File saved to: {filename}") return True except Exception as fe: print(f"[!] Fallback download also failed: {fe}") return False def main(): parser = argparse.ArgumentParser(description="Pornhub / General Video Downloader") parser.add_argument("url", nargs="?", help="URL of the video to download") parser.add_argument("-o", "--output", default="/mnt/zim/tour-comfy/wireframe/", help="Output directory path (default: current directory)") parser.add_argument("-q", "--quality", default="720p", help="Video quality (e.g. best, 1080p, 720p, 480p) (default: 720p)") args = parser.parse_args() url = args.url if not url: # Interactive mode try: url = input("Enter video URL (e.g., Pornhub link): ").strip() except (KeyboardInterrupt, EOFError): print("\n[-] Cancelled.") sys.exit(0) if not url: print("[!] No URL provided. Exiting.") sys.exit(1) success = download_video(url, args.output, args.quality) sys.exit(0 if success else 1) if __name__ == "__main__": main()