import logging from abc import ABCMeta, abstractmethod import httpx from app.connectors.aws.s3 import S3Client from app.exceptions import ( LnktoException, NonImageResponseException, SpotifyThumbnailException, TiktokThumbnailException, YoutubeImageException, ) from app.image import Image from app.models import ( AdReportingCreativeThumbnailUrl, Creative, ) logger = logging.getLogger(__name__) CreativeProcessorsType = dict[str, "BaseCreativeProcessor"] class BaseCreativeProcessor(metaclass=ABCMeta): def __init__( self, web_client: httpx.AsyncClient, s3_client: S3Client, assets_s3_bucket_name: str, assets_s3_raw_path: str, assets_s3_thumbnail_path: str, ) -> None: self.web_client = web_client self.s3_client = s3_client self.assets_s3_bucket_name = assets_s3_bucket_name self.assets_s3_raw_path = assets_s3_raw_path self.assets_s3_thumbnail_path = assets_s3_thumbnail_path @abstractmethod async def _get_image_for_creative(self, creative: Creative) -> Image: ... async def process(self, creative: Creative) -> AdReportingCreativeThumbnailUrl: logger.info( "Processing creative %s, platform %s", creative.creative_id, creative.platform, ) result = AdReportingCreativeThumbnailUrl( platform=creative.platform, creative_id=creative.creative_id, source_url=creative.source_url, fails_count=creative.fails_count, ) try: logger.info("Downloading image for creative %s", creative.creative_id) image = await self._get_image_for_creative(creative) await self.s3_client.put_object( self.assets_s3_bucket_name, f"{self.assets_s3_raw_path}/{image.name}", image.image_bytes, image.mime_type, ) await self.s3_client.put_object( self.assets_s3_bucket_name, f"{self.assets_s3_thumbnail_path}/{image.thumbnail.name}", image.thumbnail.image_bytes, image.thumbnail.mime_type, ) result.img_s3_obj_key = f"{self.assets_s3_raw_path}/{image.name}" result.timg64x64_s3_obj_key = ( f"{self.assets_s3_thumbnail_path}/{image.thumbnail.name}" ) logger.info("Image for creative %s downloaded", creative.creative_id) except httpx.HTTPError: logger.warning( "HTTP exception happened during creative processing", extra={ "creative_id": creative.creative_id, "platform": creative.platform, }, exc_info=True, ) except NonImageResponseException: logger.warning( "Site returned non image response", extra={ "creative_id": creative.creative_id, "platform": creative.platform, }, exc_info=True, ) except LnktoException: logger.warning( "Lnk.to response does not contain youtube video link", extra={ "creative_id": creative.creative_id, "platform": creative.platform, }, exc_info=True, ) except YoutubeImageException: logger.warning( "Youtube video thumbnail is not available", extra={ "creative_id": creative.creative_id, "platform": creative.platform, }, exc_info=True, ) except TiktokThumbnailException: logger.warning( "Tiktok thumbnail is not available", extra={ "creative_id": creative.creative_id, "platform": creative.platform, }, exc_info=True, ) except SpotifyThumbnailException: logger.warning( "Spotify thumbnail is not available", extra={ "creative_id": creative.creative_id, "platform": creative.platform, }, exc_info=True, ) except Exception: logger.exception( "Unhandled exception happened during creative processing", extra={ "creative_id": creative.creative_id, "platform": creative.platform, }, ) if not result.img_s3_obj_key: result.fails_count += 1 return result