112 lines
4.3 KiB
Python
112 lines
4.3 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, EnumSignLevel
|
|
|
|
|
|
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()
|
|
sign = clients_mgr.generate_sign(client.session_name, EnumSignLevel.VIST)
|
|
url = f"{param.base.exposed_url}/tg/api/v1/file/get/{utils.get_peer_id(msg.peer_id)}/{msg.id}/{file_name}?sign={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(sign: 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()
|
|
sign_info = clients_mgr.parse_sign(sign)
|
|
client_id = TgFileSystemClientManager.get_sign_client_id(sign_info)
|
|
client = await clients_mgr.get_client_force(client_id)
|
|
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,
|
|
)
|