import math, asyncio, subprocess from telethon import TelegramClient from aiohttp import web 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 chunk[first_part_cut:] elif part == last_part: yield chunk[:last_part_cut] else: yield 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() # if body: # ffmpeg = "ffmpeg" # cmd = [ # ffmpeg, # "ffmpeg", # "-i", # "pipe:0", # "-c", # "copy", # "-re", # "pipe:1", # ] # ffmpeg_cmd = subprocess.Popen( # cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, shell=False # ) # body = await body.__anext__() # ffmpeg_cmd.stdin.write(body.tobytes()) # ffmpeg_cmd.stdin.close() # body = b"" # while True: # print(body) # output = ffmpeg_cmd.stdout.read() # if len(output) > 0: # body += output # ffmpeg_cmd.stdout.close() # else: # error_msg = ffmpeg_cmd.poll() # if error_msg is not None: # break return web.Response( body=body, headers=headers, status=206 if self.offset else 200 )