From 42ebed5503b26753c7b44ddb7a81916dbf7f0bf5 Mon Sep 17 00:00:00 2001 From: Kurbanov Bulat Date: Sun, 9 Jan 2022 20:12:06 +0300 Subject: [PATCH] Add downloading --- fastapi_file_server/app/on_start.py | 4 +- .../app/services/file_downloader.py | 61 +++++++++++++++++++ .../app/services/file_uploader.py | 37 ++++------- fastapi_file_server/app/services/storages.py | 30 +++++++++ fastapi_file_server/app/views.py | 34 ++++++++++- 5 files changed, 135 insertions(+), 31 deletions(-) create mode 100644 fastapi_file_server/app/services/file_downloader.py create mode 100644 fastapi_file_server/app/services/storages.py diff --git a/fastapi_file_server/app/on_start.py b/fastapi_file_server/app/on_start.py index e76747a..c997b79 100644 --- a/fastapi_file_server/app/on_start.py +++ b/fastapi_file_server/app/on_start.py @@ -1,5 +1,5 @@ -from app.services.file_uploader import FileUploader +from app.services.storages import StoragesContainer async def on_start(): - await FileUploader.prepare() + await StoragesContainer.prepare() diff --git a/fastapi_file_server/app/services/file_downloader.py b/fastapi_file_server/app/services/file_downloader.py new file mode 100644 index 0000000..c410482 --- /dev/null +++ b/fastapi_file_server/app/services/file_downloader.py @@ -0,0 +1,61 @@ +from io import BytesIO +from typing import Optional + +from telegram_files_storage import AiogramFilesStorage, TelethonFilesStorage + +from app.services.storages import StoragesContainer + + +class FileDownloader: + _aiogram_storage_index = 0 + _telethon_storage_index = 0 + + @classmethod + @property + def AIOGRAM_STORAGES(cls) -> list[AiogramFilesStorage]: + return StoragesContainer.AIOGRAM_STORAGES + + @classmethod + @property + def TELETHON_STORAGES(cls) -> list[TelethonFilesStorage]: + return StoragesContainer.TELETHON_STORAGES + + @classmethod + def get_aiogram_storage(cls) -> AiogramFilesStorage: + if not cls.AIOGRAM_STORAGES: + raise ValueError("Aiogram storage not exist!") + + cls._aiogram_storage_index = (cls._aiogram_storage_index + 1) % len( + cls.AIOGRAM_STORAGES + ) + + return cls.AIOGRAM_STORAGES[cls._aiogram_storage_index] + + @classmethod + def get_telethon_storage(cls) -> TelethonFilesStorage: + if not cls.TELETHON_STORAGES: + raise ValueError("Telethon storage not exists!") + + cls._telethon_storage_index = (cls._telethon_storage_index + 1) % len( + cls.TELETHON_STORAGES + ) + + return cls.TELETHON_STORAGES[cls._telethon_storage_index] + + @classmethod + async def download_by_file_id(cls, file_id: str) -> Optional[BytesIO]: + if not cls.AIOGRAM_STORAGES: + return None + + storage = cls.get_aiogram_storage() + + return await storage.download(file_id) + + @classmethod + async def download_by_message_id(cls, message_id: int) -> Optional[BytesIO]: + if not cls.TELETHON_STORAGES: + return None + + storage = cls.get_telethon_storage() + + return await storage.download(message_id) diff --git a/fastapi_file_server/app/services/file_uploader.py b/fastapi_file_server/app/services/file_uploader.py index 8f7d542..211a96a 100644 --- a/fastapi_file_server/app/services/file_uploader.py +++ b/fastapi_file_server/app/services/file_uploader.py @@ -6,16 +6,23 @@ from fastapi import UploadFile from telegram_files_storage import AiogramFilesStorage, TelethonFilesStorage from app.models import UploadedFile, UploadBackends -from core.config import env_config +from app.services.storages import StoragesContainer class FileUploader: - AIOGRAM_STORAGES: list[AiogramFilesStorage] = [] - TELETHON_STORAGES: list[TelethonFilesStorage] = [] - _aiogram_storage_index = 0 _telethon_storage_index = 0 + @classmethod + @property + def AIOGRAM_STORAGES(cls) -> list[AiogramFilesStorage]: + return StoragesContainer.AIOGRAM_STORAGES + + @classmethod + @property + def TELETHON_STORAGES(cls) -> list[TelethonFilesStorage]: + return StoragesContainer.TELETHON_STORAGES + def __init__(self, file: UploadFile, caption: Optional[str] = None) -> None: self.file = file self.caption = caption @@ -81,28 +88,6 @@ class FileUploader: data=self.upload_data, ) - @classmethod - async def prepare(cls): - if env_config.BOT_TOKENS: - cls.AIOGRAM_STORAGES: list[AiogramFilesStorage] = [ - AiogramFilesStorage(env_config.TELEGRAM_CHAT_ID, token) - for token in env_config.BOT_TOKENS - ] - - if env_config.TELETHON_APP_CONFIG and env_config.TELETHON_SESSIONS: - cls.TELETHON_STORAGES: list[TelethonFilesStorage] = [ - TelethonFilesStorage( - env_config.TELEGRAM_CHAT_ID, - env_config.TELETHON_APP_CONFIG.APP_ID, - env_config.TELETHON_APP_CONFIG.API_HASH, - session, - ) - for session in env_config.TELETHON_SESSIONS - ] - - for storage in [*cls.AIOGRAM_STORAGES, *cls.TELETHON_STORAGES]: - await storage.prepare() - @classmethod def get_aiogram_storage(cls) -> AiogramFilesStorage: if not cls.AIOGRAM_STORAGES: diff --git a/fastapi_file_server/app/services/storages.py b/fastapi_file_server/app/services/storages.py new file mode 100644 index 0000000..291e6f1 --- /dev/null +++ b/fastapi_file_server/app/services/storages.py @@ -0,0 +1,30 @@ +from telegram_files_storage import AiogramFilesStorage, TelethonFilesStorage + +from core.config import env_config + + +class StoragesContainer: + AIOGRAM_STORAGES: list[AiogramFilesStorage] = [] + TELETHON_STORAGES: list[TelethonFilesStorage] = [] + + @classmethod + async def prepare(cls): + if env_config.BOT_TOKENS: + cls.AIOGRAM_STORAGES: list[AiogramFilesStorage] = [ + AiogramFilesStorage(env_config.TELEGRAM_CHAT_ID, token) + for token in env_config.BOT_TOKENS + ] + + if env_config.TELETHON_APP_CONFIG and env_config.TELETHON_SESSIONS: + cls.TELETHON_STORAGES: list[TelethonFilesStorage] = [ + TelethonFilesStorage( + env_config.TELEGRAM_CHAT_ID, + env_config.TELETHON_APP_CONFIG.APP_ID, + env_config.TELETHON_APP_CONFIG.API_HASH, + session, + ) + for session in env_config.TELETHON_SESSIONS + ] + + for storage in [*cls.AIOGRAM_STORAGES, *cls.TELETHON_STORAGES]: + await storage.prepare() diff --git a/fastapi_file_server/app/views.py b/fastapi_file_server/app/views.py index 440668b..77f8f24 100644 --- a/fastapi_file_server/app/views.py +++ b/fastapi_file_server/app/views.py @@ -1,12 +1,20 @@ from typing import Optional -from fastapi import File, UploadFile, Depends, Form, APIRouter, HTTPException - -from starlette import status +from fastapi import ( + File, + UploadFile, + Depends, + Form, + APIRouter, + HTTPException, + Response, + status, +) from app.depends import check_token from app.models import UploadedFile as UploadedFileDB from app.serializers import UploadedFile, CreateUploadedFile +from app.services.file_downloader import FileDownloader from app.services.file_uploader import FileUploader @@ -46,6 +54,26 @@ async def upload_file(file: UploadFile = File({}), caption: Optional[str] = Form return await FileUploader.upload(file, caption=caption) +@router.get("/download_by_file_id/{file_id}") +async def download_by_file_id(file_id: str): + data = await FileDownloader.download_by_file_id(file_id) + + if data is None: + raise HTTPException(status.HTTP_400_BAD_REQUEST) + + return Response(data.read()) + + +@router.get("/download_by_message/{chat_id}/{message_id}") +async def download_by_message(chat_id: str, message_id: int): + data = await FileDownloader.download_by_message_id(message_id) + + if data is None: + raise HTTPException(status.HTTP_400_BAD_REQUEST) + + return Response(data.read()) + + @router.delete("/{file_id}", response_model=UploadedFile, responses={400: {}}) async def delete_file(file_id: int): uploaded_file = await UploadedFileDB.objects.get_or_none(id=file_id)