Murad Jabir 1 rok pred
rodič
commit
af60dc7102
2 zmenil súbory, kde vykonal 131 pridanie a 260 odobranie
  1. 97 210
      auth.py
  2. 34 50
      main.py

+ 97 - 210
auth.py

@@ -1,16 +1,10 @@
 from google_auth_oauthlib.flow import InstalledAppFlow
 from google.auth.transport.requests import Request
-from google.oauth2 import service_account
 import os
 import pickle
-import json
-import sqlite3
-import subprocess
-import shutil
-import tempfile
-from pathlib import Path
-from datetime import datetime
+import requests
 import logging
+from pathlib import Path
 
 # OAuth 2.0 scopes
 SCOPES = ["https://www.googleapis.com/auth/youtube.readonly"]
@@ -18,130 +12,10 @@ SCOPES = ["https://www.googleapis.com/auth/youtube.readonly"]
 # Configure logging
 logger = logging.getLogger(__name__)
 
-def extract_chrome_cookies(output_file="youtube_cookies.txt"):
-    """Extract YouTube cookies from Chrome browser."""
-    try:
-        # Common Chrome cookie database locations
-        chrome_paths = [
-            Path.home() / ".config/google-chrome/Default/Cookies",
-            Path.home() / ".config/google-chrome/Profile 1/Cookies",
-            Path.home() / ".config/chromium/Default/Cookies",
-            Path.home() / "snap/chromium/common/chromium/Default/Cookies",
-            Path.home() / ".var/app/com.google.Chrome/config/google-chrome/Default/Cookies",
-        ]
-        
-        # Find existing Chrome cookies database
-        chrome_cookies_db = None
-        for path in chrome_paths:
-            if path.exists():
-                chrome_cookies_db = path
-                logger.info(f"Found Chrome cookies database: {chrome_cookies_db}")
-                break
-        
-        if not chrome_cookies_db:
-            logger.warning("Chrome cookies database not found")
-            return False
-        
-        # Use yt-dlp to extract cookies directly from browser
-        try:
-            cmd = ['yt-dlp', '--cookies-from-browser', 'chrome', '--print-to-file', 'cookies', output_file, '--no-download', 'https://www.youtube.com']
-            result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
-            
-            if result.returncode == 0:
-                logger.info(f"Successfully extracted Chrome cookies to {output_file}")
-                return True
-            else:
-                logger.error(f"yt-dlp cookie extraction failed: {result.stderr}")
-                return False
-                
-        except subprocess.TimeoutExpired:
-            logger.error("Cookie extraction timed out")
-            return False
-        except Exception as e:
-            logger.error(f"Error running yt-dlp cookie extraction: {e}")
-            return False
-            
-    except Exception as e:
-        logger.error(f"Error extracting Chrome cookies: {e}")
-        return False
-
-def check_browser_cookies():
-    """Check which browsers have accessible cookies for YouTube."""
-    browsers = ['chrome', 'chromium', 'firefox', 'brave', 'edge']
-    available_browsers = []
-    
-    for browser in browsers:
-        try:
-            # Test if browser cookies are accessible
-            cmd = ['yt-dlp', '--cookies-from-browser', browser, '--simulate', '--quiet', 'https://www.youtube.com/watch?v=dQw4w9WgXcQ']
-            result = subprocess.run(cmd, capture_output=True, text=True, timeout=10)
-            if result.returncode == 0:
-                available_browsers.append(browser)
-                logger.info(f"Browser {browser} has accessible cookies")
-            else:
-                logger.debug(f"Browser {browser} cookies not accessible: {result.stderr}")
-        except (subprocess.TimeoutExpired, subprocess.SubprocessError) as e:
-            logger.debug(f"Error checking {browser} cookies: {e}")
-            continue
-    
-    return available_browsers
-
-def update_cookies_from_browser(browser='chrome', output_file="youtube_cookies.txt"):
-    """Update cookies file from specified browser."""
-    try:
-        logger.info(f"Updating cookies from {browser} browser...")
-        
-        # Use yt-dlp to extract cookies from browser
-        cmd = ['yt-dlp', '--cookies-from-browser', browser, '--print-to-file', 'cookies', output_file, '--no-download', 'https://www.youtube.com']
-        result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
-        
-        if result.returncode == 0:
-            logger.info(f"Successfully updated cookies from {browser} to {output_file}")
-            
-            # Verify the cookies file was created and has content
-            cookies_path = Path(output_file)
-            if cookies_path.exists() and cookies_path.stat().st_size > 0:
-                logger.info(f"Cookies file created: {cookies_path} ({cookies_path.stat().st_size} bytes)")
-                return True
-            else:
-                logger.warning("Cookies file was not created or is empty")
-                return False
-        else:
-            logger.error(f"Failed to extract cookies from {browser}: {result.stderr}")
-            return False
-            
-    except subprocess.TimeoutExpired:
-        logger.error("Cookie extraction timed out")
-        return False
-    except Exception as e:
-        logger.error(f"Error updating cookies from browser: {e}")
-        return False
-
-def get_service_account_token():
-    """Authenticate using service account (no user interaction needed)."""
-    service_account_file = "service_account.json"
-    
-    if not os.path.exists(service_account_file):
-        print(f"Service account file {service_account_file} not found.")
-        print("To use service account authentication:")
-        print("1. Go to Google Cloud Console")
-        print("2. Navigate to IAM & Admin > Service Accounts")
-        print("3. Create a new service account")
-        print("4. Download the JSON key file as 'service_account.json'")
-        return None
-    
-    try:
-        creds = service_account.Credentials.from_service_account_file(
-            service_account_file, scopes=SCOPES)
-        print("Service account authentication successful!")
-        return creds
-    except Exception as e:
-        print(f"Service account authentication failed: {e}")
-        return None
-
-def get_token_interactive():
-    """Perform OAuth authentication with user interaction."""
+def get_token():
+    """Perform OAuth authentication and save the token."""
     creds = None
+    # The token file to save credentials
     token_file = "youtube_token.pickle"
 
     # Check if token file exists
@@ -152,18 +26,10 @@ def get_token_interactive():
     # If no valid credentials, authenticate user
     if not creds or not creds.valid:
         if creds and creds.expired and creds.refresh_token:
-            try:
-                creds.refresh(Request())
-                print("Token refreshed successfully!")
-            except Exception as e:
-                print(f"Token refresh failed: {e}")
-                creds = None
-        
-        if not creds:
-            print("Starting interactive authentication...")
+            creds.refresh(Request())
+        else:
             flow = InstalledAppFlow.from_client_secrets_file("client_secret.json", SCOPES)
             creds = flow.run_local_server(port=8080)
-        
         # Save the credentials to a file
         with open(token_file, "wb") as token:
             pickle.dump(creds, token)
@@ -173,43 +39,86 @@ def get_token_interactive():
     
     return creds
 
-def get_token_non_interactive():
-    """Try non-interactive authentication methods first."""
-    # Try service account first
-    creds = get_service_account_token()
-    if creds:
-        return creds
-    
-    # Try existing saved token
-    token_file = "youtube_token.pickle"
-    if os.path.exists(token_file):
+def create_cookies_from_token(output_file="youtube_cookies.txt"):
+    """Create a cookies file using the OAuth token for yt-dlp authentication."""
+    try:
+        # Get valid credentials
+        creds = get_token()
+        if not creds:
+            logger.error("No valid credentials available")
+            return False
+        
+        # Create a session with the OAuth token
+        session = requests.Session()
+        
+        # Add authorization header
+        headers = {
+            'Authorization': f'Bearer {creds.token}',
+            'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
+        }
+        session.headers.update(headers)
+        
+        # Make a request to YouTube to establish session
         try:
-            with open(token_file, "rb") as token:
-                creds = pickle.load(token)
+            response = session.get('https://www.youtube.com/', timeout=30)
+            if response.status_code == 200:
+                logger.info("Successfully authenticated with YouTube using OAuth token")
+                
+                # Extract cookies from the session
+                cookies_content = "# Netscape HTTP Cookie File\n# Generated from OAuth token\n\n"
+                
+                for cookie in session.cookies:
+                    # Convert cookie to Netscape format
+                    domain = cookie.domain if cookie.domain else '.youtube.com'
+                    flag = 'TRUE' if domain.startswith('.') else 'FALSE'
+                    path = cookie.path if cookie.path else '/'
+                    secure = 'TRUE' if cookie.secure else 'FALSE'
+                    expires = str(int(cookie.expires)) if cookie.expires else '0'
+                    
+                    cookies_content += f"{domain}\t{flag}\t{path}\t{secure}\t{expires}\t{cookie.name}\t{cookie.value}\n"
+                
+                # Add essential YouTube cookies if they're missing
+                essential_cookies = [
+                    ('.youtube.com', 'TRUE', '/', 'FALSE', '0', 'PREF', 'f1=50000000&hl=en'),
+                    ('.youtube.com', 'TRUE', '/', 'FALSE', '0', 'CONSENT', 'YES+'),
+                ]
+                
+                for domain, flag, path, secure, expires, name, value in essential_cookies:
+                    if name not in [c.name for c in session.cookies]:
+                        cookies_content += f"{domain}\t{flag}\t{path}\t{secure}\t{expires}\t{name}\t{value}\n"
+                
+                # Write cookies file
+                with open(output_file, 'w') as f:
+                    f.write(cookies_content)
+                
+                logger.info(f"Cookies file created: {output_file}")
+                return True
+            else:
+                logger.error(f"Failed to authenticate with YouTube: {response.status_code}")
+                return False
+                
+        except requests.RequestException as e:
+            logger.error(f"Error making request to YouTube: {e}")
+            return False
             
-            if creds and creds.valid:
-                print("Using existing valid token.")
-                return creds
-            elif creds and creds.expired and creds.refresh_token:
-                try:
-                    creds.refresh(Request())
-                    print("Token refreshed successfully!")
-                    # Save refreshed token
-                    with open(token_file, "wb") as token:
-                        pickle.dump(creds, token)
-                    return creds
-                except Exception as e:
-                    print(f"Token refresh failed: {e}")
-        except Exception as e:
-            print(f"Error loading saved token: {e}")
-    
-    print("No valid non-interactive authentication method available.")
-    print("Run with --interactive flag or set up service account authentication.")
-    return None
+    except Exception as e:
+        logger.error(f"Error creating cookies from token: {e}")
+        return False
 
-def get_token():
-    """Main authentication function - tries non-interactive first."""
-    return get_token_non_interactive()
+def get_oauth_headers():
+    """Get OAuth authorization headers for API requests."""
+    try:
+        creds = get_token()
+        if not creds or not creds.valid:
+            return None
+            
+        return {
+            'Authorization': f'Bearer {creds.token}',
+            'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36'
+        }
+    except Exception as e:
+        logger.error(f"Error getting OAuth headers: {e}")
+        return None
 
 if __name__ == "__main__":
     import sys
@@ -217,50 +126,28 @@ if __name__ == "__main__":
     # Set up logging
     logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
     
-    if "--extract-cookies" in sys.argv:
-        # Extract cookies from Chrome
-        browser = 'chrome'
-        if "--browser" in sys.argv:
-            browser_idx = sys.argv.index("--browser") + 1
-            if browser_idx < len(sys.argv):
-                browser = sys.argv[browser_idx]
-        
+    if "--create-cookies" in sys.argv:
+        # Create cookies file from OAuth token
         output_file = "youtube_cookies.txt"
         if "--output" in sys.argv:
             output_idx = sys.argv.index("--output") + 1
             if output_idx < len(sys.argv):
                 output_file = sys.argv[output_idx]
         
-        print(f"Extracting cookies from {browser} browser...")
-        success = update_cookies_from_browser(browser, output_file)
+        print("Creating cookies file from OAuth token...")
+        success = create_cookies_from_token(output_file)
         if success:
-            print(f"Cookies successfully extracted to {output_file}")
+            print(f"✅ Cookies file created successfully: {output_file}")
+            print("This file can now be used with yt-dlp for authentication")
         else:
-            print("Cookie extraction failed")
+            print("❌ Failed to create cookies file")
             sys.exit(1)
-    
-    elif "--check-browsers" in sys.argv:
-        # Check available browsers
-        print("Checking available browsers with YouTube cookies...")
-        browsers = check_browser_cookies()
-        if browsers:
-            print(f"Available browsers: {', '.join(browsers)}")
-        else:
-            print("No browsers with accessible YouTube cookies found")
-    
-    elif "--interactive" in sys.argv:
-        creds = get_token_interactive()
     else:
-        creds = get_token_non_interactive()
-        if not creds:
-            print("\nTo authenticate interactively, run: python3 auth.py --interactive")
-            print("To extract cookies from browser, run: python3 auth.py --extract-cookies")
-            print("To check available browsers, run: python3 auth.py --check-browsers")
-    
-    if "--extract-cookies" not in sys.argv and "--check-browsers" not in sys.argv:
+        # Default behavior - just get/refresh token
+        creds = get_token()
         if creds:
-            print("Authentication successful!")
+            print("✅ OAuth token is valid and ready")
+            print("💡 To create cookies for yt-dlp, run: python3 auth.py --create-cookies")
         else:
-            print("Authentication failed!")
-            sys.exit(1)
-
+            print("❌ Failed to get valid OAuth token")
+            sys.exit(1)

+ 34 - 50
main.py

@@ -49,29 +49,16 @@ OUTPUT_DIR = "/media/media/YouTube"  # Change this to your Emby folder
 DAYS_BACK = 5  # Videos published in the last 5 days
 DAYS_TO_KEEP = 10  # Delete videos older than 10 days
 TOKEN_FILE = "youtube_token.pickle"  # Token file from the OAuth script
-COOKIES_FILE = "youtube_cookies.txt"  # YouTube cookies file for authentication
 EXIT_TIME = dt_time(6, 0)  # Exit the script at 06:00
 POPULAR_VIDEOS_MAX_RESULTS = 10  # Number of popular videos to fetch (you can change this)
 REGION_CODES = ["US", "GB", "LU", "LK"]  # Regions to fetch popular videos from
 
-# Helper function to build yt-dlp commands with cookies
-def build_yt_dlp_command(base_args, url=None):
-    """Build a yt-dlp command with cookies if available."""
-    command = ["yt-dlp"] + base_args
-    
-    # Add cookies if file exists
-    if os.path.exists(COOKIES_FILE):
-        command.extend(["--cookies", COOKIES_FILE])
-        # Log cookies usage at debug level to avoid spam
-        logging.getLogger(__name__).debug(f"Using cookies file: {COOKIES_FILE}")
-    else:
-        logging.getLogger(__name__).debug(f"Cookies file not found: {COOKIES_FILE}")
-    
-    # Add URL if provided
-    if url:
-        command.append(url)
-    
-    return command
+# Proxy Configuration (set to None to disable)
+HTTP_PROXY = os.environ.get("HTTP_PROXY", None)  # e.g., "http://proxy.example.com:8080"
+HTTPS_PROXY = os.environ.get("HTTPS_PROXY", None)  # e.g., "http://proxy.example.com:8080"
+# Alternative: Set directly in code
+# HTTP_PROXY = "http://proxy.example.com:8080"
+# HTTPS_PROXY = "http://proxy.example.com:8080"
 
 # Logging Configuration
 LOG_DIR = "logs"
@@ -384,7 +371,11 @@ def list_available_formats(video_id):
     logger = logging.getLogger(__name__)
     
     try:
-        command = build_yt_dlp_command(["--list-formats"], f"https://www.youtube.com/watch?v={video_id}")
+        command = [
+            "yt-dlp",
+            "--list-formats",
+            f"https://www.youtube.com/watch?v={video_id}"
+        ]
         
         result = subprocess.run(command, capture_output=True, text=True, timeout=30)
         if result.returncode == 0:
@@ -397,6 +388,20 @@ def list_available_formats(video_id):
     except Exception as e:
         logger.warning(f"Error listing formats for {video_id}: {e}")
 
+def get_proxy_args():
+    """Get proxy arguments for yt-dlp commands."""
+    proxy_args = []
+    
+    if HTTP_PROXY or HTTPS_PROXY:
+        # Use the first available proxy for both HTTP and HTTPS
+        proxy_url = HTTP_PROXY or HTTPS_PROXY
+        proxy_args.extend(["--proxy", proxy_url])
+        
+        logger = logging.getLogger(__name__)
+        logger.info(f"🌐 Using proxy: {proxy_url}")
+    
+    return proxy_args
+
 def download_video(video_id, output_path, video_title="Unknown"):
     """Download a YouTube video with quality priority - downloads 4K VP9/AV1 if needed for conversion."""
     global download_stats
@@ -413,10 +418,11 @@ def download_video(video_id, output_path, video_title="Unknown"):
         # First, check what format would be downloaded to determine compatibility
         logger.debug(f"Checking video format compatibility for: {video_title}")
         
-        simulate_command = build_yt_dlp_command([
-            "--simulate", "--print", "%(format_id)s|%(vcodec)s|%(height)s|%(ext)s",
-            "-f", "(bestvideo[height>=2160][vcodec~='^(avc|h264|hevc|h265)']+bestaudio)/(bestvideo[height>=2160]+bestaudio)/(bestvideo[height>=1440]+bestaudio)/(bestvideo[height>=1080]+bestaudio)/best"
-        ], f"https://www.youtube.com/watch?v={video_id}")
+        simulate_command = [
+            "yt-dlp", "--simulate", "--print", "%(format_id)s|%(vcodec)s|%(height)s|%(ext)s",
+            "-f", "(bestvideo[height>=2160][vcodec~='^(avc|h264|hevc|h265)']+bestaudio)/(bestvideo[height>=2160]+bestaudio)/(bestvideo[height>=1440]+bestaudio)/(bestvideo[height>=1080]+bestaudio)/best",
+            f"https://www.youtube.com/watch?v={video_id}"
+        ]
         
         will_be_compatible = True
         predicted_codec = "unknown"
@@ -467,15 +473,17 @@ def download_video(video_id, output_path, video_title="Unknown"):
         )
         
         # 4K-prioritized approach: Try Apple TV compatible 4K first, then any 4K, then lower resolutions
-        command = build_yt_dlp_command([
+        command = [
+            "yt-dlp",
             "-f", "(bestvideo[height>=2160][vcodec~='^(avc|h264|hevc|h265)']+bestaudio)/(bestvideo[height>=2160]+bestaudio)/(bestvideo[height>=1440]+bestaudio)/(bestvideo[height>=1080]+bestaudio)/best",
             "--merge-output-format", "mp4",
             "--no-mtime",
             "--newline",  # Better for parsing progress
             "--verbose" if logger.isEnabledFor(logging.DEBUG) else "--no-warnings",
             "--write-info-json",  # Write metadata for analysis
+            f"https://www.youtube.com/watch?v={video_id}",
             "-o", output_template
-        ], f"https://www.youtube.com/watch?v={video_id}")
+        ]
         
         logger.debug(f"yt-dlp command: {' '.join(command)}")
         
@@ -1562,36 +1570,12 @@ def check_exit_time():
         print("Current time exceeds exit time. Terminating script.")
         sys.exit(0)
 
-def check_cookies_status():
-    """Check and log the status of the YouTube cookies file."""
-    logger = logging.getLogger(__name__)
-    
-    if os.path.exists(COOKIES_FILE):
-        try:
-            # Check if cookies file is readable and has content
-            with open(COOKIES_FILE, 'r') as f:
-                content = f.read().strip()
-                if content:
-                    logger.info(f"✅ YouTube cookies file found and loaded: {COOKIES_FILE}")
-                else:
-                    logger.warning(f"⚠️ YouTube cookies file is empty: {COOKIES_FILE}")
-        except Exception as e:
-            logger.error(f"❌ Error reading cookies file {COOKIES_FILE}: {e}")
-    else:
-        logger.warning(f"⚠️ YouTube cookies file not found: {COOKIES_FILE}")
-        logger.info("   To avoid bot detection, create cookies file using:")
-        logger.info("   1. Export cookies from browser using a browser extension")
-        logger.info("   2. Save as 'youtube_cookies.txt' in the application directory")
-
 def main():
     global download_stats
     logger = logging.getLogger(__name__)
     
     logger.info("Starting main download process")
     
-    # Check cookies status
-    check_cookies_status()
-    
     # Initialize Home Assistant integration
     if HOMEASSISTANT_ENABLED:
         logger.info("Initializing Home Assistant integration...")