| import math, asyncio, subprocess | |
| from telethon import TelegramClient | |
| from fastapi.responses import StreamingResponse | |
| import logging | |
| logging.getLogger(__name__) | |
| logging.basicConfig(level=logging.INFO) | |
| class Download: | |
| client: TelegramClient | |
| route: str | |
| offset: int | |
| handler: None | |
| file: None | |
| limit: int | |
| file_size: float | |
| def __init__(self, handler): | |
| self.handler = handler | |
| self.file = handler.message.media | |
| self.file_size = handler.message.file.size | |
| self.limit = handler.sanity.limit | |
| self.offset = handler.sanity.offset | |
| self.client = handler.client | |
| self.mime_type = handler.message.file.mime_type | |
| async def download(self): | |
| part_size = int(512 * 1024) * 2 | |
| first_part_cut = self.offset % part_size | |
| first_part = math.floor(self.offset / part_size) | |
| last_part_cut = part_size - (self.limit % part_size) | |
| last_part = math.ceil(self.limit / part_size) | |
| part_count = math.ceil(self.file_size / part_size) | |
| part = first_part | |
| try: | |
| async for chunk in self.client.iter_download( | |
| self.file, offset=first_part * part_size, request_size=part_size | |
| ): | |
| if part == first_part: | |
| yield bytes(chunk[first_part_cut:]) | |
| elif part == last_part: | |
| yield bytes(chunk[:last_part_cut]) | |
| else: | |
| yield bytes(chunk) | |
| logging.debug(f"Part {part}/{last_part} (total {part_count}) served!") | |
| part += 1 | |
| logging.debug("serving finished") | |
| except (GeneratorExit, StopAsyncIteration, asyncio.CancelledError): | |
| logging.debug("file serve interrupted") | |
| raise | |
| except Exception as e: | |
| print(e) | |
| logging.debug("file serve errored", exc_info=True) | |
| async def handle_request(self): | |
| headers = { | |
| "content-type": self.mime_type, | |
| "content-range": f"bytes {self.offset}-{self.limit-1}/{self.file_size}", | |
| "content-length": str(self.limit - self.offset), | |
| "accept-ranges": "bytes", | |
| "content-transfer-encoding": "Binary", | |
| "content-disposition": f'{self.handler.route}; filename="{self.handler.message.file.name}"', | |
| } | |
| logging.info( | |
| f"Serving file in {self.handler.message.file.name}) ; Range: {self.offset} - {self.limit}" | |
| ) | |
| if self.handler.head: | |
| body = None | |
| else: | |
| body = self.download() | |
| return StreamingResponse( | |
| media_type=self.mime_type, | |
| content=body, | |
| headers=headers, | |
| status_code=206 if self.offset else 200, | |
| ) | |