|
@@ -1,16 +1,10 @@
|
|
|
from google_auth_oauthlib.flow import InstalledAppFlow
|
|
from google_auth_oauthlib.flow import InstalledAppFlow
|
|
|
from google.auth.transport.requests import Request
|
|
from google.auth.transport.requests import Request
|
|
|
-from google.oauth2 import service_account
|
|
|
|
|
import os
|
|
import os
|
|
|
import pickle
|
|
import pickle
|
|
|
-import json
|
|
|
|
|
-import sqlite3
|
|
|
|
|
-import subprocess
|
|
|
|
|
-import shutil
|
|
|
|
|
-import tempfile
|
|
|
|
|
-from pathlib import Path
|
|
|
|
|
-from datetime import datetime
|
|
|
|
|
|
|
+import requests
|
|
|
import logging
|
|
import logging
|
|
|
|
|
+from pathlib import Path
|
|
|
|
|
|
|
|
# OAuth 2.0 scopes
|
|
# OAuth 2.0 scopes
|
|
|
SCOPES = ["https://www.googleapis.com/auth/youtube.readonly"]
|
|
SCOPES = ["https://www.googleapis.com/auth/youtube.readonly"]
|
|
@@ -18,130 +12,10 @@ SCOPES = ["https://www.googleapis.com/auth/youtube.readonly"]
|
|
|
# Configure logging
|
|
# Configure logging
|
|
|
logger = logging.getLogger(__name__)
|
|
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
|
|
creds = None
|
|
|
|
|
+ # The token file to save credentials
|
|
|
token_file = "youtube_token.pickle"
|
|
token_file = "youtube_token.pickle"
|
|
|
|
|
|
|
|
# Check if token file exists
|
|
# Check if token file exists
|
|
@@ -152,18 +26,10 @@ def get_token_interactive():
|
|
|
# If no valid credentials, authenticate user
|
|
# If no valid credentials, authenticate user
|
|
|
if not creds or not creds.valid:
|
|
if not creds or not creds.valid:
|
|
|
if creds and creds.expired and creds.refresh_token:
|
|
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)
|
|
flow = InstalledAppFlow.from_client_secrets_file("client_secret.json", SCOPES)
|
|
|
creds = flow.run_local_server(port=8080)
|
|
creds = flow.run_local_server(port=8080)
|
|
|
-
|
|
|
|
|
# Save the credentials to a file
|
|
# Save the credentials to a file
|
|
|
with open(token_file, "wb") as token:
|
|
with open(token_file, "wb") as token:
|
|
|
pickle.dump(creds, token)
|
|
pickle.dump(creds, token)
|
|
@@ -173,43 +39,86 @@ def get_token_interactive():
|
|
|
|
|
|
|
|
return creds
|
|
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:
|
|
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__":
|
|
if __name__ == "__main__":
|
|
|
import sys
|
|
import sys
|
|
@@ -217,50 +126,28 @@ if __name__ == "__main__":
|
|
|
# Set up logging
|
|
# Set up logging
|
|
|
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
|
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"
|
|
output_file = "youtube_cookies.txt"
|
|
|
if "--output" in sys.argv:
|
|
if "--output" in sys.argv:
|
|
|
output_idx = sys.argv.index("--output") + 1
|
|
output_idx = sys.argv.index("--output") + 1
|
|
|
if output_idx < len(sys.argv):
|
|
if output_idx < len(sys.argv):
|
|
|
output_file = sys.argv[output_idx]
|
|
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:
|
|
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:
|
|
else:
|
|
|
- print("Cookie extraction failed")
|
|
|
|
|
|
|
+ print("❌ Failed to create cookies file")
|
|
|
sys.exit(1)
|
|
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:
|
|
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:
|
|
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:
|
|
else:
|
|
|
- print("Authentication failed!")
|
|
|
|
|
- sys.exit(1)
|
|
|
|
|
-
|
|
|
|
|
|
|
+ print("❌ Failed to get valid OAuth token")
|
|
|
|
|
+ sys.exit(1)
|