================================================================================ CRUNCHYROLL DOWNLOADER - CHALLENGE 1 SOLUTION Chapter 5: Complete Workflow Challenge: JWT Login Flow ================================================================================ PROBLEM: Implement complete login: config → login → cache token → return token. SOLUTION: ================================================================================ File: download.py (complete workflow) import sys from config import Config from crunchyroll_utils import ( login, load_cached_token, save_token, CrunchyrollClient ) def get_token(config: Config) -> str: """Get JWT token, either cached or fresh.""" print("=" * 60) print("STEP 1: AUTHENTICATION") print("=" * 60) # Try to load cached token print("\nšŸ” Checking for cached token...") token = load_cached_token(config.token_cache) if token: print("āœ… Using cached token") return token # No cached token, must login print("\nšŸ” No cached token, logging in...") token = login(config) if not token: print("āŒ Login failed!") return None # Save token for next time save_token(token, config.token_cache) return token def download_episodes(token: str, config: Config): """Download episode data.""" print("\n" + "=" * 60) print("STEP 2: FETCH EPISODES") print("=" * 60) client = CrunchyrollClient(config, token) episodes = client.get_episodes() if not episodes: print("āŒ Failed to fetch episodes") return False # Process episodes print(f"\nāœ… Got {len(episodes)} episodes") print("\nFirst 5 episodes:") print("-" * 60) for i, ep in enumerate(episodes[:5], 1): title = ep.get('title', 'Unknown') series = ep.get('series_title', 'Unknown Series') print(f"{i}. {series} - {title}") return True def main(): """Main workflow.""" print("\n" + "=" * 70) print("šŸš€ CRUNCHYROLL EPISODE DOWNLOADER") print("=" * 70 + "\n") # Step 0: Load and validate config print("STEP 0: CONFIGURATION") print("-" * 60) config = Config() if not config.validate(): print("āŒ Configuration invalid!") return False print(f"Email: {config.email}") # Step 1: Get JWT token token = get_token(config) if not token: return False # Step 2: Download episodes if not download_episodes(token, config): return False # Success! print("\n" + "=" * 70) print("āœ… DOWNLOAD COMPLETE!") print("=" * 70) return True if __name__ == "__main__": success = main() sys.exit(0 if success else 1) EXPECTED OUTPUT ================================================================================ ====================================================================== šŸš€ CRUNCHYROLL EPISODE DOWNLOADER ====================================================================== STEP 0: CONFIGURATION ------------------------------------------------------------ āœ… Configuration is valid! Email: emubantam@gmail.com ============================================================ STEP 1: AUTHENTICATION ============================================================ šŸ” Checking for cached token... āœ… Using cached token ============================================================ STEP 2: FETCH EPISODES ============================================================ šŸ“” Fetching episodes... āœ… Fetched 150 episodes āœ… Got 150 episodes First 5 episodes: ------------------------------------------------------------ 1. Attack on Titan - Season 1 Episode 1 2. Attack on Titan - Season 1 Episode 2 3. Demon Slayer - Season 1 Episode 1 4. Jujutsu Kaisen - Season 1 Episode 1 5. My Hero Academia - Season 1 Episode 1 ====================================================================== āœ… DOWNLOAD COMPLETE! ====================================================================== THE COMPLETE JWT WORKFLOW ================================================================================ 1. CONFIG VALIDATION āœ… Email/password exist and valid 2. CHECK CACHE - Is token_cache.json present? - YES → Load and use it - NO → Need to login 3. LOGIN (if needed) POST /auth/v1/authenticate Send: {"username": "...", "password": "..."} Get: {"access_token": "eyJ..."} 4. CACHE TOKEN Save token to token_cache.json for next time 5. USE TOKEN GET /api/v2/episodes Header: "Authorization: Bearer eyJ..." Get: Episode data 6. PROCESS Parse episodes, export to CSV, etc. ERROR RECOVERY ================================================================================ If you get 401 (token expired): 1. Delete token_cache.json 2. Re-run: python download.py 3. Script will re-login automatically 4. New token saved to cache If login fails: - Check username/password in .env - Check internet connection - Try logging in on crunchyroll.com manually ================================================================================