TgToFileSystem/backend/api_implement.py
2024-06-15 16:43:13 +08:00

111 lines
4.2 KiB
Python

import traceback
import json
import logging
from urllib.parse import quote
from telethon import types, hints, utils
import fastapi
from fastapi import Request
from fastapi.responses import StreamingResponse, Response
import configParse
from backend import apiutils
from backend.TgFileSystemClientManager import TgFileSystemClientManager
logger = logging.getLogger(__file__.split("/")[-1])
async def link_convert(link: str) -> str:
clients_mgr = TgFileSystemClientManager.get_instance()
link_slice = link.split("/")
if len(link_slice) < 5:
raise RuntimeError("link format invalid")
chat_id_or_name, msg_id = link_slice[-2:]
is_msg_id = msg_id.isascii() and msg_id.isdecimal()
if not is_msg_id:
raise RuntimeError("message id invalid")
msg_id = int(msg_id)
is_chat_name = chat_id_or_name.isascii() and not chat_id_or_name.isdecimal()
is_chat_id = chat_id_or_name.isascii() and chat_id_or_name.isdecimal()
if not is_chat_name and not is_chat_id:
raise RuntimeError("chat id invalid")
client = clients_mgr.get_first_client()
if client is None:
raise RuntimeError("client not ready, login first pls.")
if is_chat_id:
chat_id_or_name = int(chat_id_or_name)
msg = await client.get_message(chat_id_or_name, msg_id)
file_name = apiutils.get_message_media_name(msg)
param = configParse.get_TgToFileSystemParameter()
url = (
f"{param.base.exposed_url}/tg/api/v1/file/get/{utils.get_peer_id(msg.peer_id)}/{msg.id}/{file_name}?sign={client.sign}"
)
return url
async def get_chat_details(mgr: TgFileSystemClientManager) -> dict[int, any]:
chat_details = {}
for _, client in mgr.clients.items():
chat_list = client.client_param.whitelist_chat
for chat_id in chat_list:
chat_entity = await client.get_entity(chat_id)
chat_details[chat_id] = json.loads(chat_entity.to_json())
return chat_details
async def get_clients_manager_status(detail: bool) -> dict[str, any]:
clients_mgr = TgFileSystemClientManager.get_instance()
ret = await clients_mgr.get_status()
if not detail:
return ret
ret["clist"] = await get_chat_details(clients_mgr)
return ret
async def get_media_file_stream(token: str, cid: int, mid: int, request: Request) -> StreamingResponse:
msg_id = mid
chat_id = cid
headers = {
# "content-type": "video/mp4",
"accept-ranges": "bytes",
"content-encoding": "identity",
# "content-length": stream_file_size,
"access-control-expose-headers": ("content-type, accept-ranges, content-length, " "content-range, content-encoding"),
}
range_header = request.headers.get("range")
clients_mgr = TgFileSystemClientManager.get_instance()
client = await clients_mgr.get_client_force(token)
msg = await client.get_message(chat_id, msg_id)
if not isinstance(msg.media, types.MessageMediaDocument) and not isinstance(msg.media, types.MessageMediaPhoto):
raise RuntimeError(f"request don't support: {msg.media=}")
file_size = msg.media.document.size
start = 0
end = file_size - 1
status_code = fastapi.status.HTTP_200_OK
mime_type = msg.media.document.mime_type
headers["content-type"] = mime_type
# headers["content-length"] = str(file_size)
file_name = apiutils.get_message_media_name(msg)
if file_name == "":
maybe_file_type = mime_type.split("/")[-1]
file_name = f"{chat_id}.{msg_id}.{maybe_file_type}"
headers["Content-Disposition"] = f"inline; filename*=utf-8'{quote(file_name)}'"
if range_header is not None:
start, end = apiutils.get_range_header(range_header, file_size)
size = end - start + 1
# headers["content-length"] = str(size)
headers["content-range"] = f"bytes {start}-{end}/{file_size}"
status_code = fastapi.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,
)