diff --git a/backend/api.py b/backend/api.py index 9a6af52..af2434a 100644 --- a/backend/api.py +++ b/backend/api.py @@ -51,12 +51,18 @@ class TgToFileListRequestBody(BaseModel): @apiutils.atimeit async def search_tg_file_list(body: TgToFileListRequestBody): try: + param = configParse.get_TgToFileSystemParameter() res = hints.TotalList() res_type = "msg" client = await clients_mgr.get_client_force(body.token) - res_dict = {} + res_dict = [] res = await client.get_messages_by_search_db(body.chat_id, body.search, limit=body.length, inc=body.inc, offset=body.index) - res_dict = [json.loads(item) for item in res] + for item in res: + msg_info = json.loads(item) + file_name = apiutils.get_message_media_name_from_dict(msg_info) + msg_info['file_name'] = file_name + msg_info['download_url'] = f"{param.base.exposed_url}/tg/api/v1/file/get/{body.chat_id}/{msg_info.get('id')}/{file_name}?sign={body.token}" + res_dict.append(msg_info) response_dict = { "client": json.loads(client.to_json()), @@ -77,19 +83,20 @@ async def get_tg_file_list(body: TgToFileListRequestBody): res = hints.TotalList() res_type = "chat" client = await clients_mgr.get_client_force(body.token) - res_dict = {} - if body.chat_id == 0: - res = await client.get_dialogs(limit=body.length, offset=body.index, refresh=body.refresh) - res_dict = [{"id": item.id, "is_channel": item.is_channel, - "is_group": item.is_group, "is_user": item.is_user, "name": item.name, } for item in res] - elif body.search != "": + res_dict = [] + if body.search != "": res = await client.get_messages_by_search(body.chat_id, search_word=body.search, limit=body.length, offset=body.index, inner_search=body.inner) - res_type = "msg" - res_dict = [json.loads(item.to_json()) for item in res] else: res = await client.get_messages(body.chat_id, limit=body.length, offset=body.index) - res_type = "msg" - res_dict = [json.loads(item.to_json()) for item in res] + res_type = "msg" + for item in res: + file_name = apiutils.get_message_media_name(item) + if file_name == "": + file_name = "unknown.tmp" + msg_info = json.loads(item.to_json()) + msg_info['file_name'] = file_name + msg_info['download_url'] = f"{param.base.exposed_url}/tg/api/v1/file/get/{body.chat_id}/{item.id}/{file_name}?sign={body.token}" + res_dict.append(msg_info) response_dict = { "client": json.loads(client.to_json()), @@ -134,7 +141,7 @@ async def get_tg_file_media_stream(token: str, cid: int, mid: int, request: Requ maybe_file_type = mime_type.split("/")[-1] file_name = f"{chat_id}.{msg_id}.{maybe_file_type}" headers[ - "Content-Disposition"] = f'Content-Disposition: inline; filename="{file_name.encode("utf-8")}"' + "Content-Disposition"] = f'inline; filename="{file_name}"' if range_header is not None: start, end = apiutils.get_range_header(range_header, file_size) @@ -142,9 +149,13 @@ async def get_tg_file_media_stream(token: str, cid: int, mid: int, request: Requ # headers["content-length"] = str(size) headers["content-range"] = f"bytes {start}-{end}/{file_size}" status_code = status.HTTP_206_PARTIAL_CONTENT + else: + headers["content-length"] = str(file_size) + headers["content-range"] = f"bytes 0-{file_size-1}/{file_size}" return StreamingResponse( client.streaming_get_iter(msg, start, end, request), headers=headers, + media_type=mime_type, status_code=status_code, ) except Exception as err: @@ -155,7 +166,13 @@ async def get_tg_file_media_stream(token: str, cid: int, mid: int, request: Requ @app.get("/tg/api/v1/file/get/{chat_id}/{msg_id}/{file_name}") @apiutils.atimeit async def get_tg_file_media(chat_id: int|str, msg_id: int, file_name: str, sign: str, req: Request): - return await get_tg_file_media_stream(sign, chat_id, msg_id, req) + try: + if isinstance(chat_id, str): + chat_id = int(chat_id) + return await get_tg_file_media_stream(sign, chat_id, msg_id, req) + except Exception as err: + logger.error(f"{err=}") + return Response(json.dumps({"detail": f"{err=}"}), status_code=status.HTTP_404_NOT_FOUND) @app.post("/tg/api/v1/client/login") diff --git a/backend/apiutils.py b/backend/apiutils.py index 3ad2207..a478b23 100644 --- a/backend/apiutils.py +++ b/backend/apiutils.py @@ -36,6 +36,22 @@ def get_message_media_name(msg: types.Message) -> str: return attr.file_name return "" +def get_message_media_name_from_dict(msg: dict[str, any]) -> str: + doc = None + try: + doc = msg['media']['document'] + except: + pass + file_name = None + if doc is not None: + for attr in doc['attributes']: + file_name = attr.get('file_name') + if file_name != "" and file_name is not None: + break + if file_name == "" or file_name is None: + file_name = "unknown.tmp" + return file_name + def timeit_sec(func): @wraps(func) def timeit_wrapper(*args, **kwargs): diff --git a/configParse.py b/configParse.py index 8c28805..1d81ce8 100644 --- a/configParse.py +++ b/configParse.py @@ -8,6 +8,7 @@ from pydantic import BaseModel class TgToFileSystemParameter(BaseModel): class BaseParameter(BaseModel): salt: str = "" + exposed_url: str = "http://127.0.0.1:7777" port: int = 7777 timeit_enable: bool = False base: BaseParameter @@ -34,7 +35,6 @@ class TgToFileSystemParameter(BaseModel): enable: bool = False token: str = "" port: int = 2000 - base_url: str = "http://127.0.0.1" chat_id: list[int] = [] web: TgWebParameter diff --git a/frontend/home.py b/frontend/home.py index 736affe..bdfc8b1 100644 --- a/frontend/home.py +++ b/frontend/home.py @@ -27,8 +27,7 @@ if 'is_order' not in st.session_state: st.session_state.is_order = False param = configParse.get_TgToFileSystemParameter() -background_server_url = f"{param.web.base_url}:{param.base.port}/tg/api/v1/file/search" -download_server_url = f"{param.web.base_url}:{param.base.port}/tg/api/v1/file/msg?token={param.web.token}&cid={param.web.chat_id[0]}&mid=" +background_server_url = f"{param.base.exposed_url}/tg/api/v1/file/search" @st.experimental_fragment @@ -130,7 +129,7 @@ def do_search_req(): doc = None file_size = 0 msg_id = str(v['id']) - download_url = download_server_url + msg_id + download_url = v['download_url'] url_list.append(download_url) try: doc = v['media']['document'] diff --git a/logging_config.yaml b/logging_config.yaml index ddda5a9..9f3a521 100644 --- a/logging_config.yaml +++ b/logging_config.yaml @@ -2,7 +2,7 @@ version: 1 disable_existing_loggers: false formatters: standard: - format: '%(asctime)s [%(name)s][%(levelname)s]:%(message)s' + format: '[%(levelname)s] %(asctime)s [%(name)s:%(lineno)d]:%(message)s' handlers: console: level: INFO diff --git a/start.py b/start.py index 0e4a1fc..45bc7f0 100644 --- a/start.py +++ b/start.py @@ -5,6 +5,7 @@ import yaml import logging import uvicorn +from uvicorn.config import LOGGING_CONFIG import configParse from backend import backendapp @@ -17,6 +18,21 @@ for handler in logging.getLogger().handlers: if isinstance(handler, logging.handlers.TimedRotatingFileHandler): handler.suffix = "%Y-%m-%d" +LOGGING_CONFIG["formatters"]["default"]["fmt"] = "[%(levelname)s] %(asctime)s [uvicorn.default]:%(message)s" +LOGGING_CONFIG["formatters"]["access"]["fmt"] = '[%(levelname)s]%(asctime)s [uvicorn.access]:%(client_addr)s - "%(request_line)s" %(status_code)s' +LOGGING_CONFIG["handlers"]["timed_rotating_file"] = { + "class": "logging.handlers.TimedRotatingFileHandler", + "filename": "logs/app.log", + "when": "midnight", + "interval": 1, + "backupCount": 7, + "level": "INFO", + "formatter": "default", + "encoding": "utf-8", +} +LOGGING_CONFIG["loggers"]["uvicorn"]["handlers"].append("timed_rotating_file") +LOGGING_CONFIG["loggers"]["uvicorn.access"]["handlers"].append("timed_rotating_file") + logger = logging.getLogger(__file__.split("/")[-1]) if __name__ == "__main__":