auth.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. from google_auth_oauthlib.flow import InstalledAppFlow
  2. from google.auth.transport.requests import Request
  3. from google.oauth2 import service_account
  4. import os
  5. import pickle
  6. import json
  7. import subprocess
  8. import tempfile
  9. import time
  10. from pathlib import Path
  11. # OAuth 2.0 scopes
  12. SCOPES = ["https://www.googleapis.com/auth/youtube.readonly"]
  13. def extract_cookies_from_browser():
  14. """Extract cookies from browsers to bypass YouTube bot detection."""
  15. print("🍪 Attempting to extract cookies from browsers...")
  16. # Try different browsers in order of preference
  17. browsers = ['chrome', 'firefox', 'edge', 'safari', 'opera', 'brave']
  18. cookies_file = "youtube_cookies.txt"
  19. for browser in browsers:
  20. try:
  21. print(f" Trying {browser}...")
  22. # Use yt-dlp's built-in cookie extraction
  23. cmd = [
  24. 'yt-dlp',
  25. '--cookies-from-browser', browser,
  26. '--cookies', cookies_file,
  27. '--simulate',
  28. '--no-warnings',
  29. 'https://www.youtube.com/watch?v=dQw4w9WgXcQ' # Test video
  30. ]
  31. result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
  32. if result.returncode == 0 and os.path.exists(cookies_file):
  33. print(f" ✅ Successfully extracted cookies from {browser}")
  34. return cookies_file
  35. else:
  36. print(f" ❌ Failed to extract from {browser}")
  37. # Clean up any partial cookie file
  38. if os.path.exists(cookies_file):
  39. os.remove(cookies_file)
  40. except subprocess.TimeoutExpired:
  41. print(f" ⏰ Timeout extracting from {browser}")
  42. except Exception as e:
  43. print(f" ❌ Error with {browser}: {e}")
  44. print(" ⚠️ Could not extract cookies from any browser")
  45. return None
  46. def generate_youtube_cookies():
  47. """Generate or update YouTube cookies for yt-dlp."""
  48. cookies_file = "youtube_cookies.txt"
  49. # Check if cookies file already exists and is recent
  50. if os.path.exists(cookies_file):
  51. file_age = time.time() - os.path.getmtime(cookies_file)
  52. if file_age < 3600: # Less than 1 hour old
  53. print(f"✅ Using existing cookies file (age: {file_age/60:.1f} minutes)")
  54. return cookies_file
  55. else:
  56. print(f"🔄 Cookies file is {file_age/3600:.1f} hours old, refreshing...")
  57. # Try to extract fresh cookies
  58. result = extract_cookies_from_browser()
  59. if result:
  60. return result
  61. # If extraction failed, try manual cookie creation
  62. print("🔧 Attempting manual cookie setup...")
  63. return create_manual_cookies()
  64. def create_manual_cookies():
  65. """Create a basic cookie file for YouTube."""
  66. cookies_file = "youtube_cookies.txt"
  67. # Create a basic Netscape-format cookie file
  68. # This won't have real session cookies but may help with some requests
  69. cookie_content = """# Netscape HTTP Cookie File
  70. # This is a generated file! Do not edit.
  71. .youtube.com TRUE / FALSE 0 PREF f1=50000000
  72. .youtube.com TRUE / FALSE 0 VISITOR_INFO1_LIVE
  73. .youtube.com TRUE / TRUE 0 YSC
  74. .google.com TRUE / FALSE 0 NID
  75. """
  76. try:
  77. with open(cookies_file, 'w') as f:
  78. f.write(cookie_content)
  79. print(f"📝 Created basic cookie file: {cookies_file}")
  80. return cookies_file
  81. except Exception as e:
  82. print(f"❌ Failed to create cookie file: {e}")
  83. return None
  84. def test_youtube_access():
  85. """Test if YouTube access works with current authentication."""
  86. print("🧪 Testing YouTube access...")
  87. cookies_file = "youtube_cookies.txt"
  88. test_commands = [
  89. # Test with cookies
  90. {
  91. 'name': 'With Cookies',
  92. 'cmd': ['yt-dlp', '--cookies', cookies_file, '--simulate', '--print', 'title', 'https://www.youtube.com/watch?v=dQw4w9WgXcQ']
  93. },
  94. # Test with browser extraction
  95. {
  96. 'name': 'Browser Extraction',
  97. 'cmd': ['yt-dlp', '--cookies-from-browser', 'chrome', '--simulate', '--print', 'title', 'https://www.youtube.com/watch?v=dQw4w9WgXcQ']
  98. },
  99. # Test with user agent
  100. {
  101. 'name': 'Custom User Agent',
  102. 'cmd': ['yt-dlp', '--user-agent', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36', '--simulate', '--print', 'title', 'https://www.youtube.com/watch?v=dQw4w9WgXcQ']
  103. }
  104. ]
  105. for test in test_commands:
  106. try:
  107. print(f" Testing: {test['name']}")
  108. result = subprocess.run(test['cmd'], capture_output=True, text=True, timeout=20)
  109. if result.returncode == 0:
  110. print(f" ✅ Success: {result.stdout.strip()}")
  111. return test['name']
  112. else:
  113. print(f" ❌ Failed: {result.stderr[:100]}...")
  114. except subprocess.TimeoutExpired:
  115. print(f" ⏰ Timeout")
  116. except Exception as e:
  117. print(f" ❌ Error: {e}")
  118. print(" ⚠️ All YouTube access tests failed")
  119. return None
  120. def setup_youtube_access():
  121. """Setup YouTube access with cookie authentication."""
  122. print("🔧 Setting up YouTube access with cookie authentication...")
  123. # Generate cookies
  124. cookies_file = generate_youtube_cookies()
  125. if cookies_file:
  126. # Test the access
  127. working_method = test_youtube_access()
  128. if working_method:
  129. print(f"✅ YouTube access working with: {working_method}")
  130. return cookies_file
  131. print("⚠️ YouTube access setup incomplete - may encounter bot detection")
  132. return None
  133. def get_service_account_token():
  134. """Authenticate using service account (no user interaction needed)."""
  135. service_account_file = "service_account.json"
  136. if not os.path.exists(service_account_file):
  137. print(f"Service account file {service_account_file} not found.")
  138. print("To use service account authentication:")
  139. print("1. Go to Google Cloud Console")
  140. print("2. Navigate to IAM & Admin > Service Accounts")
  141. print("3. Create a new service account")
  142. print("4. Download the JSON key file as 'service_account.json'")
  143. return None
  144. try:
  145. creds = service_account.Credentials.from_service_account_file(
  146. service_account_file, scopes=SCOPES)
  147. print("Service account authentication successful!")
  148. return creds
  149. except Exception as e:
  150. print(f"Service account authentication failed: {e}")
  151. return None
  152. def get_token_interactive():
  153. """Perform OAuth authentication with user interaction."""
  154. creds = None
  155. token_file = "youtube_token.pickle"
  156. # Check if token file exists
  157. if os.path.exists(token_file):
  158. with open(token_file, "rb") as token:
  159. creds = pickle.load(token)
  160. # If no valid credentials, authenticate user
  161. if not creds or not creds.valid:
  162. if creds and creds.expired and creds.refresh_token:
  163. try:
  164. creds.refresh(Request())
  165. print("Token refreshed successfully!")
  166. except Exception as e:
  167. print(f"Token refresh failed: {e}")
  168. creds = None
  169. if not creds:
  170. print("Starting interactive authentication...")
  171. flow = InstalledAppFlow.from_client_secrets_file("client_secret.json", SCOPES)
  172. creds = flow.run_local_server(port=8080)
  173. # Save the credentials to a file
  174. with open(token_file, "wb") as token:
  175. pickle.dump(creds, token)
  176. print(f"Token saved to {token_file}")
  177. else:
  178. print("Token already exists and is valid.")
  179. return creds
  180. def get_token_non_interactive():
  181. """Try non-interactive authentication methods first."""
  182. # Try service account first
  183. creds = get_service_account_token()
  184. if creds:
  185. return creds
  186. # Try existing saved token
  187. token_file = "youtube_token.pickle"
  188. if os.path.exists(token_file):
  189. try:
  190. with open(token_file, "rb") as token:
  191. creds = pickle.load(token)
  192. if creds and creds.valid:
  193. print("Using existing valid token.")
  194. return creds
  195. elif creds and creds.expired and creds.refresh_token:
  196. try:
  197. creds.refresh(Request())
  198. print("Token refreshed successfully!")
  199. # Save refreshed token
  200. with open(token_file, "wb") as token:
  201. pickle.dump(creds, token)
  202. return creds
  203. except Exception as e:
  204. print(f"Token refresh failed: {e}")
  205. except Exception as e:
  206. print(f"Error loading saved token: {e}")
  207. print("No valid non-interactive authentication method available.")
  208. print("Run with --interactive flag or set up service account authentication.")
  209. return None
  210. def get_token():
  211. """Main authentication function - tries non-interactive first."""
  212. return get_token_non_interactive()
  213. if __name__ == "__main__":
  214. import sys
  215. if "--interactive" in sys.argv:
  216. creds = get_token_interactive()
  217. elif "--cookies" in sys.argv:
  218. print("🍪 Setting up YouTube cookies...")
  219. cookies_file = setup_youtube_access()
  220. if cookies_file:
  221. print(f"✅ Cookie setup complete: {cookies_file}")
  222. else:
  223. print("❌ Cookie setup failed")
  224. sys.exit(0)
  225. elif "--test" in sys.argv:
  226. print("🧪 Testing YouTube access...")
  227. working_method = test_youtube_access()
  228. if working_method:
  229. print(f"✅ YouTube access working with: {working_method}")
  230. else:
  231. print("❌ YouTube access not working")
  232. sys.exit(0)
  233. else:
  234. creds = get_token_non_interactive()
  235. if not creds:
  236. print("\nTo authenticate interactively, run: python3 auth.py --interactive")
  237. print("To setup YouTube cookies, run: python3 auth.py --cookies")
  238. print("To test YouTube access, run: python3 auth.py --test")
  239. if creds:
  240. print("Authentication successful!")
  241. # Also setup cookies when authenticating
  242. print("\n🍪 Setting up YouTube cookies for bot detection bypass...")
  243. setup_youtube_access()
  244. else:
  245. print("Authentication failed!")
  246. sys.exit(1)