diff --git a/bot/__init__.py b/bot/__init__.py index 0a12eec..c05a791 100644 --- a/bot/__init__.py +++ b/bot/__init__.py @@ -5,7 +5,7 @@ from .config import Telegram, LOGGER_CONFIG_JSON dictConfig(LOGGER_CONFIG_JSON) -version = 1.2 +version = 1.3 logger = getLogger('bot') TelegramBot = Client( diff --git a/bot/__main__.py b/bot/__main__.py index ba9f106..da32186 100644 --- a/bot/__main__.py +++ b/bot/__main__.py @@ -1,7 +1,7 @@ from bot import TelegramBot, logger -from bot.server import run_server +from bot.server import server if __name__ == '__main__': logger.info('Initializing...') - TelegramBot.loop.create_task(run_server()) + TelegramBot.loop.create_task(server.serve()) TelegramBot.run() diff --git a/bot/config.py b/bot/config.py index 63a86ce..d2ce2dc 100644 --- a/bot/config.py +++ b/bot/config.py @@ -37,11 +37,11 @@ LOGGER_CONFIG_JSON = { } }, 'loggers': { - 'hypercorn': { + 'uvicorn': { 'level': 'INFO', 'handlers': ['file_handler', 'stream_handler'] }, - 'hypercorn.error': { + 'uvicorn.error': { 'level': 'WARNING', 'handlers': ['file_handler', 'stream_handler'] }, diff --git a/bot/modules/telegram.py b/bot/modules/telegram.py index fe2506d..7b500ec 100644 --- a/bot/modules/telegram.py +++ b/bot/modules/telegram.py @@ -5,7 +5,6 @@ from bot import TelegramBot from bot.config import Telegram from bot.server.error import abort - async def get_message(message_id: int): message = None @@ -39,6 +38,7 @@ async def get_file_properties(msg: Message): if not media: abort(400, 'Unknown file type.') file_name = getattr(media, 'file_name', None) + file_size = getattr(media, 'file_size', 0) if not file_name: file_format = { @@ -53,4 +53,4 @@ async def get_file_properties(msg: Message): mime_type = guess_type(file_name)[0] or 'application/octet-stream' - return file_name, mime_type \ No newline at end of file + return file_name, file_size, mime_type diff --git a/bot/server/__init__.py b/bot/server/__init__.py index 9d52220..13a8f36 100644 --- a/bot/server/__init__.py +++ b/bot/server/__init__.py @@ -1,13 +1,13 @@ from quart import Quart -from hypercorn import Config -from hypercorn.asyncio import serve +from uvicorn import Server as UvicornServer, Config from logging import getLogger from bot.config import Server, LOGGER_CONFIG_JSON from . import main, error -logger = getLogger('hypercorn') +logger = getLogger('uvicorn') instance = Quart(__name__) +instance.config['RESPONSE_TIMEOUT'] = None @instance.before_serving async def before_serve(): @@ -21,9 +21,11 @@ instance.register_error_handler(404, error.not_found) instance.register_error_handler(405, error.invalid_method) instance.register_error_handler(error.HTTPError, error.http_error) -async def run_server(): - config = Config() - config.bind = [f'{Server.BIND_ADDRESS}:{Server.PORT}'] - config.logconfig_dict = LOGGER_CONFIG_JSON - - await serve(instance, config) +server = UvicornServer ( + Config ( + app=instance, + host=Server.BIND_ADDRESS, + port=Server.PORT, + log_config=LOGGER_CONFIG_JSON + ) +) diff --git a/bot/server/main.py b/bot/server/main.py index e715347..c0d7b8d 100644 --- a/bot/server/main.py +++ b/bot/server/main.py @@ -1,8 +1,9 @@ from quart import Blueprint, Response, request, render_template, redirect -from .error import abort -from bot import version, TelegramBot +from math import ceil, floor +from bot import TelegramBot from bot.config import Telegram, Server from bot.modules.telegram import get_message, get_file_properties +from .error import abort bp = Blueprint('main', __name__) @@ -14,19 +15,63 @@ async def home(): async def transmit_file(file_id): file = await get_message(int(file_id)) or abort(404) code = request.args.get('code') or abort(401) + range_header = request.headers.get('Range', 0) if code != file.caption: abort(403) - - file_name, mime_type = await get_file_properties(file) - headers = { - 'Content-Type': mime_type, - 'Content-Disposition': f'attachment; filename="{file_name}"' + + file_name, file_size, mime_type = await get_file_properties(file) + + if range_header: + from_bytes, until_bytes = range_header.replace("bytes=", "").split("-") + from_bytes = int(from_bytes) + until_bytes = int(until_bytes) if until_bytes else file_size - 1 + else: + from_bytes = 0 + until_bytes = file_size -1 + + if (until_bytes > file_size) or (from_bytes < 0) or (until_bytes < from_bytes): + abort(416, 'Invalid range.') + + chunk_size = 1024 * 1024 + until_bytes = min(until_bytes, file_size - 1) + + offset = from_bytes - (from_bytes % chunk_size) + first_part_cut = from_bytes - offset + last_part_cut = until_bytes % chunk_size + 1 + + req_length = until_bytes - from_bytes + 1 + part_count = ceil(until_bytes / chunk_size) - floor(offset / chunk_size) + + disposition = 'inline' if 'video' in mime_type or 'audio' in mime_type or 'html' in mime_type else 'attachment' + headers={ + 'Content-Type': f'{mime_type}', + 'Content-Range': f'bytes {from_bytes}-{until_bytes}/{file_size}', + 'Content-Length': str(req_length), + 'Content-Disposition': f'{disposition}; filename="{file_name}"', + 'Accept-Ranges': 'bytes', } - file_stream = TelegramBot.stream_media(file) + async def file_streamer(): + current_part = 1 + async for chunk in TelegramBot.stream_media(file, offset = from_bytes // chunk_size): + + if not chunk: + break + elif part_count == 1: + yield chunk[first_part_cut:last_part_cut] + elif current_part == 1: + yield chunk[first_part_cut:] + elif current_part == part_count: + yield chunk[:last_part_cut] + else: + yield chunk + current_part += 1 - return Response(file_stream, headers=headers) + if current_part > part_count: + break + + return Response(file_streamer(), headers=headers, status=206 if range_header else 200) @bp.route('/stream/') async def stream_file(file_id): @@ -38,4 +83,4 @@ async def stream_file(file_id): async def file_deeplink(file_id): code = request.args.get('code') or abort(401) - return redirect(f'https://t.me/{Telegram.BOT_USERNAME}?start=file_{file_id}_{code}') \ No newline at end of file + return redirect(f'https://t.me/{Telegram.BOT_USERNAME}?start=file_{file_id}_{code}') diff --git a/bot/server/templates/player.html b/bot/server/templates/player.html index f09d15d..93bd1e3 100644 --- a/bot/server/templates/player.html +++ b/bot/server/templates/player.html @@ -1,3 +1,4 @@ + @@ -103,7 +104,7 @@