From ef4fc6582de96fc89fdf1d62e2e53e0ef8f4153e Mon Sep 17 00:00:00 2001 From: AlexandreSenpai Date: Wed, 13 Nov 2024 02:14:20 -0300 Subject: [PATCH 1/2] fix: add get mime type safelly and make google drive deps trully optional --- enma/_version.py | 2 +- enma/domain/entities/pagination.py | 3 + enma/domain/utils/mime.py | 11 +++ enma/infra/adapters/repositories/mangadex.py | 19 +++++- enma/infra/adapters/repositories/nhentai.py | 72 ++++++++++++++------ enma/infra/adapters/storage/google_drive.py | 28 ++++---- 6 files changed, 96 insertions(+), 39 deletions(-) create mode 100644 enma/domain/utils/mime.py diff --git a/enma/_version.py b/enma/_version.py index cb9dc8a..9a0e3da 100644 --- a/enma/_version.py +++ b/enma/_version.py @@ -1 +1 @@ -__version__ = '2.4.2' +__version__ = '2.4.3' diff --git a/enma/domain/entities/pagination.py b/enma/domain/entities/pagination.py index 1ff6e48..d39741a 100644 --- a/enma/domain/entities/pagination.py +++ b/enma/domain/entities/pagination.py @@ -50,3 +50,6 @@ def __init__(self, self.results = results or list() self.total_pages = total_pages self.total_results = total_results + + def add_result(self, result: Thumb): + self.results.append(result) \ No newline at end of file diff --git a/enma/domain/utils/mime.py b/enma/domain/utils/mime.py new file mode 100644 index 0000000..07b4332 --- /dev/null +++ b/enma/domain/utils/mime.py @@ -0,0 +1,11 @@ +from typing import Union +from enma.application.core.utils.logger import logger +from enma.domain.entities.manga import MIME + + +def get_mime_safelly(mime: str) -> Union[MIME, None]: + try: + return MIME[mime] + except ValueError: + logger.error(f'Invalid MIME type: {mime}') + return None \ No newline at end of file diff --git a/enma/infra/adapters/repositories/mangadex.py b/enma/infra/adapters/repositories/mangadex.py index a752f8e..e793146 100644 --- a/enma/infra/adapters/repositories/mangadex.py +++ b/enma/infra/adapters/repositories/mangadex.py @@ -28,6 +28,7 @@ SymbolicLink, Title) from enma.domain.entities.search_result import Pagination, SearchResult, Thumb +from enma.domain.utils import mime from enma.infra.core.interfaces.mangadex_response import (AuthorRelation, CoverArtRelation, IAltTitles, IGetResult, @@ -145,12 +146,20 @@ def fetch_chapter_by_symbolic_link(self, ch: IHash = response.json() chapter = Chapter() + for index, page in enumerate(ch.get('chapter').get('data')): extension = page.split('.')[-1] + safe_mime = mime.get_mime_safelly(extension.upper()) + + if safe_mime is None: + logger.warning(f'Could not determine MIME type for extension {extension}. Defaulting to JPEG.') + + safe_mime = safe_mime if safe_mime is not None else MIME.J + chapter.add_page(Image(uri=self.__create_chapter_page_uri(ch.get('chapter').get('hash'), page), name=f'{index}.{extension}', - mime=MIME[extension.upper()])) + mime=safe_mime)) return chapter def __fetch_chapter_hashes(self, chapter_id: str) -> tuple[str, list[str]]: @@ -208,9 +217,15 @@ def __create_chapter(self, for index, page in enumerate(files): extension = page.split('.')[-1] + safe_mime = mime.get_mime_safelly(extension.upper()) + + if safe_mime is None: + logger.warning(f'Could not determine MIME type for extension {extension}. Defaulting to JPEG.') + + safe_mime = safe_mime if safe_mime is not None else MIME.J ch.add_page(Image(uri=self.__create_chapter_page_uri(hash, page), name=f'{index}.{extension}', - mime=MIME[extension.upper()])) + mime=safe_mime)) return ch diff --git a/enma/infra/adapters/repositories/nhentai.py b/enma/infra/adapters/repositories/nhentai.py index c1c043b..3b2df72 100644 --- a/enma/infra/adapters/repositories/nhentai.py +++ b/enma/infra/adapters/repositories/nhentai.py @@ -20,6 +20,7 @@ from enma.domain.entities.manga import (MIME, Chapter, Genre, Author, Image, Manga, SymbolicLink, Title, Tag as EnmaTag) from enma.domain.entities.search_result import Pagination, SearchResult, Thumb +from enma.domain.utils import mime from enma.infra.core.interfaces.nhentai_response import NHentaiImage, NHentaiResponse, Tag as NHentaiResponseTag from enma.infra.core.utils.cache import Cache from enma._version import __version__ @@ -155,13 +156,19 @@ def __create_chapter(self, else: chapter = Chapter() for index, page in enumerate(pages): - mime = MIME[page.get('t').upper()] + safe_mime = mime.get_mime_safelly(page.get('t').upper()) + + if safe_mime is None: + logger.warning(f'Could not find a valid mime type for page {index+1}. Forcing mime type as JPG.') + + safe_mime = safe_mime if safe_mime is not None else MIME.J + chapter.add_page(Image(uri=self.__make_page_uri(type='page', - mime=mime, + mime=safe_mime, media_id=media_id, page_number=index+1), - name=f'{index}.{mime.value}', - mime=mime, + name=f'{index}.{safe_mime.value}', + mime=safe_mime, width=page.get('w'), height=page.get('h'))) return chapter @@ -224,19 +231,23 @@ def get(self, tags = [*characters, *related, *category] - thumbnail_mime = MIME[doujin.get("images").get("thumbnail").get("t").upper()] + safe_mime = mime.get_mime_safelly(doujin.get('images').get('thumbnail').get('t').upper()) + safe_mime = safe_mime if safe_mime is not None else MIME.J + thumbnail = Image(uri=self.__make_page_uri(type='thumbnail', - mime=thumbnail_mime, + mime=safe_mime, media_id=media_id), - mime=thumbnail_mime, + mime=safe_mime, width=doujin.get("images").get("thumbnail").get("w"), height=doujin.get("images").get("thumbnail").get("h")) - cover_mime = MIME[doujin.get("images").get("cover").get("t").upper()] + safe_mime = mime.get_mime_safelly(doujin.get("images").get("cover").get("t").upper()) + safe_mime = safe_mime if safe_mime is not None else MIME.J + cover = Image(uri=self.__make_page_uri(type='cover', media_id=media_id, - mime=cover_mime), - mime=cover_mime, + mime=safe_mime), + mime=safe_mime, width=doujin.get("images").get("cover").get("w"), height=doujin.get("images").get("cover").get("h")) @@ -352,18 +363,35 @@ def paginate(self, page: int) -> Pagination: PER_PAGE = data.get('per_page', 0) TOTAL_RESULTS = int(PAGES) * int(PER_PAGE) - return Pagination(page=int(page), - total_results=TOTAL_RESULTS, - total_pages=PAGES, - results=[Thumb(id=result.get('id'), - title=result.get('title').get('english'), - url=urljoin(self.__BASE_URL, f'g/{result.get("id")}'), - cover=Image(uri=self.__make_page_uri(type='cover', - media_id=result.get('media_id'), - mime=MIME[result.get('images').get('cover').get('t').upper()]), - mime=MIME[result.get("images").get("thumbnail").get("t").upper()], - width=result.get('images').get('cover').get('w'), - height=result.get('images').get('cover').get('h'))) for result in data.get('result')]) + + + pagination = Pagination(page=int(page), + total_results=TOTAL_RESULTS, + total_pages=PAGES, + results=[]) + + for result in data.get('result'): + safe_mime = mime.get_mime_safelly(result.get('images').get('cover').get('t').upper()) + safe_mime = safe_mime if safe_mime is not None else MIME.J + thumb = Thumb( + id=result.get('id'), + title=result.get('title').get('english'), + url=urljoin(self.__BASE_URL, f'g/{result.get("id")}'), + cover=Image( + uri=self.__make_page_uri( + type='cover', + media_id=result.get('media_id'), + mime=safe_mime + ), + mime=safe_mime, + width=result.get('images').get('cover').get('w'), + height=result.get('images').get('cover').get('h') + ) + ) + + pagination.add_result(thumb) + + return pagination def random(self, retry=0) -> Manga: response = self.__make_request(url=urljoin(self.__BASE_URL, 'random')) diff --git a/enma/infra/adapters/storage/google_drive.py b/enma/infra/adapters/storage/google_drive.py index 01d8613..62c4079 100644 --- a/enma/infra/adapters/storage/google_drive.py +++ b/enma/infra/adapters/storage/google_drive.py @@ -1,24 +1,24 @@ -try: - from googleapiclient.discovery import build, HttpError - from googleapiclient.http import MediaIoBaseUpload - from google.oauth2.service_account import Credentials -except ImportError as e: - raise ImportError( - "The dependencies for Google Drive are not installed. " - "Please install them using 'pip install enma[google_drive]'." - ) from e - from enma.application.core.interfaces.saver_adapter import File, ISaverAdapter from enma.application.core.utils.logger import logger class GoogleDriveStorage(ISaverAdapter): + try: + from googleapiclient.discovery import build, HttpError + from googleapiclient.http import MediaIoBaseUpload + from google.oauth2.service_account import Credentials + except ImportError as e: + raise ImportError( + "The dependencies for Google Drive are not installed. " + "Please install them using 'pip install enma[google_drive]'." + ) from e + def __init__( self, credentials_path: str, root_shared_folder: str): - self.credentials = Credentials.from_service_account_file(credentials_path) - self.service = build('drive', 'v3', credentials=self.credentials) + self.credentials = self.Credentials.from_service_account_file(credentials_path) + self.service = self.build('drive', 'v3', credentials=self.credentials) self.root_shared_folder = root_shared_folder def save(self, path: str, file: File) -> bool: @@ -32,7 +32,7 @@ def save(self, path: str, file: File) -> bool: logger.debug(f'Uploading image to google drive with name: {file.name} and parent folder: {folder_id}') - media = MediaIoBaseUpload(file.data, mimetype='application/octet-stream') + media = self.MediaIoBaseUpload(file.data, mimetype='application/octet-stream') self.service.files().create( body=file_metadata, @@ -40,7 +40,7 @@ def save(self, path: str, file: File) -> bool: fields='id' ).execute() return True - except HttpError as e: + except self.HttpError as e: logger.error(f'A HTTP error ocurred while trying to upload image to google drive: {e}') return False except Exception as e: From c31fa6ca3dd0c4707961fccce6dc15d3516750e8 Mon Sep 17 00:00:00 2001 From: AlexandreSenpai Date: Wed, 13 Nov 2024 02:21:27 -0300 Subject: [PATCH 2/2] fix: moved google drive dependencies to __init__ --- enma/infra/adapters/storage/google_drive.py | 29 ++++++++++++--------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/enma/infra/adapters/storage/google_drive.py b/enma/infra/adapters/storage/google_drive.py index 62c4079..d2ce96e 100644 --- a/enma/infra/adapters/storage/google_drive.py +++ b/enma/infra/adapters/storage/google_drive.py @@ -3,22 +3,27 @@ class GoogleDriveStorage(ISaverAdapter): - try: - from googleapiclient.discovery import build, HttpError - from googleapiclient.http import MediaIoBaseUpload - from google.oauth2.service_account import Credentials - except ImportError as e: - raise ImportError( - "The dependencies for Google Drive are not installed. " - "Please install them using 'pip install enma[google_drive]'." - ) from e - def __init__( self, credentials_path: str, root_shared_folder: str): - self.credentials = self.Credentials.from_service_account_file(credentials_path) - self.service = self.build('drive', 'v3', credentials=self.credentials) + + try: + from googleapiclient.discovery import build, HttpError + from googleapiclient.http import MediaIoBaseUpload + from google.oauth2.service_account import Credentials + + self.MediaIoBaseUpload = MediaIoBaseUpload + self.HttpError = HttpError + self.build = build + except ImportError as e: + raise ImportError( + "The dependencies for Google Drive are not installed. " + "Please install them using 'pip install enma[google_drive]'." + ) from e + + self.credentials = Credentials.from_service_account_file(credentials_path) + self.service = build('drive', 'v3', credentials=self.credentials) self.root_shared_folder = root_shared_folder def save(self, path: str, file: File) -> bool: