""" Cleanup Task Periodically deletes images older than the retention period. """ import asyncio import logging from datetime import datetime, timedelta from pathlib import Path import config logger = logging.getLogger(__name__) class CleanupTask: """Background task to clean up old images.""" def __init__(self): self.images_dir = config.IMAGES_DIR self.retention_days = config.IMAGE_RETENTION_DAYS self.interval_hours = config.CLEANUP_INTERVAL_HOURS self.running = False self._task = None async def start(self): """Start the cleanup task.""" if self.running: logger.warning("Cleanup task already running") return self.running = True self._task = asyncio.create_task(self._run()) logger.info(f"Cleanup task started (retention: {self.retention_days} days, interval: {self.interval_hours}h)") async def stop(self): """Stop the cleanup task.""" if not self.running: return self.running = False if self._task: self._task.cancel() try: await self._task except asyncio.CancelledError: pass logger.info("Cleanup task stopped") async def _run(self): """Main cleanup loop.""" while self.running: try: await self._cleanup_old_images() # Sleep for the configured interval await asyncio.sleep(self.interval_hours * 3600) except asyncio.CancelledError: break except Exception as e: logger.error(f"Error in cleanup task: {e}") await asyncio.sleep(300) # Wait 5 minutes before retry async def _cleanup_old_images(self): """Delete images older than retention period.""" try: cutoff_time = datetime.now() - timedelta(days=self.retention_days) cutoff_timestamp = cutoff_time.timestamp() deleted_count = 0 deleted_size = 0 # Find all image files image_files = list(self.images_dir.glob(f"*.{config.IMAGE_FORMAT.lower()}")) for file_path in image_files: try: # Check file modification time file_mtime = file_path.stat().st_mtime if file_mtime < cutoff_timestamp: file_size = file_path.stat().st_size file_path.unlink() deleted_count += 1 deleted_size += file_size logger.debug(f"Deleted old image: {file_path.name}") except Exception as e: logger.error(f"Error deleting {file_path.name}: {e}") if deleted_count > 0: logger.info( f"Cleanup completed: deleted {deleted_count} images " f"({deleted_size / 1024 / 1024:.1f} MB) older than {self.retention_days} days" ) else: logger.debug(f"Cleanup completed: no images older than {self.retention_days} days") except Exception as e: logger.error(f"Error during cleanup: {e}") async def cleanup_now(self): """Trigger immediate cleanup (for testing or manual trigger).""" logger.info("Manual cleanup triggered") await self._cleanup_old_images() # Global cleanup task instance cleanup_task = CleanupTask()