#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ BDFR API Usage Examples This file demonstrates how to use the BDFR API layer for direct integration with the web interface, eliminating the need for subprocess console parsing. """ import asyncio import logging import sys from pathlib import Path from typing import List # Add the parent directory to the path so we can import bdfr sys.path.insert(0, str(Path(__file__).parent.parent)) from bdfr.api import ( BDFRManager, DownloadType, DownloadStatus, ProgressEvent, ProgressCallback, LoggingCallback, get_bdfr_manager ) # Configure logging logging.basicConfig( level=logging.INFO, format='[%(asctime)s] %(levelname)s: %(message)s', datefmt='%Y-%m-%d %H:%M:%S' ) logger = logging.getLogger(__name__) class WebSocketCallback(ProgressCallback): """Example callback that simulates WebSocket updates""" def __init__(self, websocket_id: str = "demo"): self.websocket_id = websocket_id async def on_progress(self, event: ProgressEvent): """Send progress update to WebSocket""" print(f"šŸ“Š [{self.websocket_id}] Progress: {event.message}") if event.progress is not None: print(f" Progress: {int(round(event.progress))}%") if event.data: print(f" Data: {event.data}") async def on_error(self, event: ProgressEvent): """Send error update to WebSocket""" print(f"āŒ [{self.websocket_id}] ERROR: {event.message}") if event.data.get('exception'): print(f" Exception: {event.data['exception']}") async def on_completed(self, event: ProgressEvent): """Send completion update to WebSocket""" status_icon = "āœ…" if event.event_type == "completed" else "āš ļø" print(f"{status_icon} [{self.websocket_id}] {event.message}") if event.data: print(f" Final stats: {event.data}") class DatabaseCallback(ProgressCallback): """Example callback that saves progress to a database""" def __init__(self, db_connection_string: str = "sqlite:///progress.db"): self.db_connection = db_connection_string async def on_progress(self, event: ProgressEvent): """Save progress to database""" # In a real implementation, you would save to your database print(f"šŸ’¾ [DB] Saved progress for {event.download_id}: {event.progress}%") async def on_error(self, event: ProgressEvent): """Save error to database""" print(f"šŸ’¾ [DB] Saved error for {event.download_id}: {event.message}") async def on_completed(self, event: ProgressEvent): """Save completion to database""" print(f"šŸ’¾ [DB] Saved completion for {event.download_id}") async def example_basic_usage(): """Basic usage example""" print("šŸš€ Basic BDFR API Usage Example") print("=" * 50) # Create a BDFR manager manager = BDFRManager("./downloads") # Create a download for a subreddit download_id = manager.download_subreddit( "python", # subreddit name limit=10, # download 10 posts sort="hot", # sort by hot no_dupes=True # avoid duplicates ) print(f"šŸ“‹ Created download with ID: {download_id}") # Monitor progress while True: status = manager.get_download_status(download_id) if not status: print("āŒ Download not found!") break print(f"šŸ“Š Status: {status['status']} | Progress: {int(round(status['progress']))}%") if status['status'] in ['completed', 'failed', 'cancelled']: print(f"šŸ Download finished with status: {status['status']}") break await asyncio.sleep(2) # Check every 2 seconds return download_id async def example_advanced_usage(): """Advanced usage with custom callbacks""" print("\nšŸŽÆ Advanced BDFR API Usage Example") print("=" * 50) # Create custom callbacks callbacks = [ LoggingCallback("web_interface"), WebSocketCallback("user_123"), DatabaseCallback() ] # Create manager with custom download directory manager = BDFRManager("./custom_downloads") # Download from multiple subreddits subreddits = ["programming", "learnprogramming", "Python"] download_ids = [] for subreddit in subreddits: download_id = manager.create_download( DownloadType.SUBREDDIT, subreddit, progress_callbacks=callbacks ) # Start the download manager.start_download(download_id) download_ids.append(download_id) print(f"šŸ“‹ Started download {download_id} for r/{subreddit}") # Monitor all downloads while download_ids: active_downloads = [] for download_id in download_ids[:]: # Copy list to avoid modification during iteration status = manager.get_download_status(download_id) if not status: print(f"āŒ Download {download_id} not found") download_ids.remove(download_id) continue print(f"šŸ“Š {download_id}: {status['status']} ({int(round(status['progress']))}%)") if status['status'] in ['completed', 'failed', 'cancelled']: print(f"šŸ Download {download_id} finished") download_ids.remove(download_id) else: active_downloads.append(download_id) if not active_downloads: break await asyncio.sleep(3) # Check every 3 seconds return len(download_ids) == 0 # Return success status async def example_user_download(): """Example of downloading user content""" print("\nšŸ‘¤ User Download Example") print("=" * 50) manager = get_bdfr_manager() # Use default manager # Download user's submitted posts download_id = manager.download_user( "testuser", # username limit=25, # 25 posts submitted=True, upvoted=False, saved=False ) print(f"šŸ“‹ Created user download: {download_id}") # Check status periodically for _ in range(10): # Check for up to 20 seconds status = manager.get_download_status(download_id) if not status: print("āŒ Download not found") break print(f"šŸ“Š Status: {status['status']} | Progress: {int(round(status['progress']))}%") if status['status'] in ['completed', 'failed']: break await asyncio.sleep(2) return download_id async def example_archive_operation(): """Example of archiving subreddit data""" print("\nšŸ“š Archive Operation Example") print("=" * 50) manager = BDFRManager("./archives") # Archive subreddit data (metadata only) download_id = manager.archive_subreddit( "dataisbeautiful", format_type="json", limit=50 ) print(f"šŸ“‹ Created archive operation: {download_id}") # Monitor progress while True: status = manager.get_download_status(download_id) if not status: print("āŒ Archive not found") break print(f"šŸ“Š Archive status: {status['status']} | Progress: {int(round(status['progress']))}%") if status['status'] in ['completed', 'failed']: print(f"šŸ Archive finished with status: {status['status']}") break await asyncio.sleep(2) return download_id async def example_web_integration(): """Example showing how to integrate with a web application""" print("\n🌐 Web Integration Example") print("=" * 50) # Simulate a web application using the API class MockWebApp: def __init__(self): self.manager = BDFRManager("./web_downloads") self.active_sessions = {} async def handle_download_request(self, user_id: str, subreddit: str, limit: int): """Handle a download request from the web interface""" # Create custom callback for this user callback = WebSocketCallback(f"ws_{user_id}") # Create and start download download_id = self.manager.download_subreddit( subreddit, limit=limit, progress_callbacks=[callback] ) # Track for this user session if user_id not in self.active_sessions: self.active_sessions[user_id] = [] self.active_sessions[user_id].append(download_id) return { "success": True, "download_id": download_id, "message": f"Started download of r/{subreddit} (limit: {limit})" } async def get_user_downloads(self, user_id: str): """Get all downloads for a user""" if user_id not in self.active_sessions: return [] downloads = [] for download_id in self.active_sessions[user_id]: status = self.manager.get_download_status(download_id) if status: downloads.append(status) return downloads async def cancel_user_download(self, user_id: str, download_id: str): """Cancel a specific download for a user""" if user_id in self.active_sessions and download_id in self.active_sessions[user_id]: success = self.manager.cancel_download(download_id) if success: self.active_sessions[user_id].remove(download_id) return {"success": True, "message": "Download cancelled"} else: return {"success": False, "message": "Failed to cancel download"} return {"success": False, "message": "Download not found for user"} # Simulate web app usage app = MockWebApp() # Simulate user requests user_id = "user123" # User starts a download result1 = await app.handle_download_request(user_id, "technology", 20) print(f"User request result: {result1}") # User starts another download result2 = await app.handle_download_request(user_id, "science", 15) print(f"User request result: {result2}") # Check user's downloads user_downloads = await app.get_user_downloads(user_id) print(f"User has {len(user_downloads)} active downloads:") for download in user_downloads: print(f" - {download['id']}: {download['status']} ({int(round(download['progress']))}%)") # Cancel one download if user_downloads: cancel_result = await app.cancel_user_download(user_id, user_downloads[0]['id']) print(f"Cancel result: {cancel_result}") return len(user_downloads) async def example_error_handling(): """Example of error handling""" print("\nāš ļø Error Handling Example") print("=" * 50) manager = BDFRManager("./test_downloads") # Try to download from a non-existent subreddit download_id = manager.download_subreddit( "this_subreddit_does_not_exist", limit=5 ) print(f"šŸ“‹ Created download for non-existent subreddit: {download_id}") # Monitor for error for _ in range(5): # Check for up to 10 seconds status = manager.get_download_status(download_id) if not status: print("āŒ Download disappeared") break print(f"šŸ“Š Status: {status['status']}") if status['status'] == 'failed': print(f"šŸ Download failed as expected: {status.get('error', 'Unknown error')}") break await asyncio.sleep(2) return download_id async def main(): """Run all examples""" print("šŸŽÆ BDFR API Examples") print("=" * 60) print("This demonstrates the new BDFR API layer for direct web integration") print() try: # Run basic example await example_basic_usage() # Run advanced example await example_advanced_usage() # Run user download example await example_user_download() # Run archive example await example_archive_operation() # Run web integration example await example_web_integration() # Run error handling example await example_error_handling() print("\nšŸŽ‰ All examples completed!") except KeyboardInterrupt: print("\nā¹ļø Examples interrupted by user") except Exception as e: print(f"\nāŒ Error running examples: {e}") import traceback traceback.print_exc() if __name__ == "__main__": # Run the examples asyncio.run(main())