ForcePilot/backend/package/yuxi/services/file_preview.py
2026-05-30 14:59:03 +08:00

103 lines
2.7 KiB
Python

from __future__ import annotations
import mimetypes
from pathlib import PurePosixPath
_MARKDOWN_EXTENSIONS = frozenset({".md", ".markdown", ".mdx"})
_PDF_EXTENSIONS = frozenset({".pdf"})
_TEXT_EXTENSIONS = frozenset(
{
".txt",
".text",
".log",
".json",
".jsonl",
".yaml",
".yml",
".toml",
".ini",
".cfg",
".conf",
".csv",
".tsv",
".py",
".js",
".ts",
".jsx",
".tsx",
".vue",
".html",
".htm",
".css",
".less",
".scss",
".xml",
".sql",
".sh",
".bash",
".zsh",
".fish",
".env",
".dockerfile",
".gitignore",
".weather",
}
)
_IMAGE_EXTENSIONS = frozenset({".png", ".jpg", ".jpeg", ".gif", ".bmp", ".webp", ".svg"})
_BINARY_SIGNATURES = (
b"\x7fELF",
b"MZ",
b"%PDF-",
b"PK\x03\x04",
b"PK\x05\x06",
b"PK\x07\x08",
b"\x89PNG\r\n\x1a\n",
b"\xff\xd8\xff",
b"GIF87a",
b"GIF89a",
b"RIFF",
)
def detect_preview_type(path: str, raw_content: bytes) -> tuple[str, bool, str | None]:
suffix = PurePosixPath(path).suffix.lower()
mime_type, _encoding = mimetypes.guess_type(path)
head = raw_content[:1024]
if suffix in _IMAGE_EXTENSIONS or (mime_type and mime_type.startswith("image/")):
return "image", True, None
if suffix in _PDF_EXTENSIONS or mime_type == "application/pdf" or head.startswith(b"%PDF-"):
return "pdf", True, None
if suffix in _MARKDOWN_EXTENSIONS:
return "markdown", True, None
if suffix in _TEXT_EXTENSIONS:
return "text", True, None
if b"\x00" in head:
return "unsupported", False, "当前文件是二进制文件,暂不支持预览"
if any(head.startswith(signature) for signature in _BINARY_SIGNATURES):
if head.startswith(b"RIFF") and b"WEBP" in head[:16]:
return "image", True, None
return "unsupported", False, "当前文件格式暂不支持预览"
if mime_type:
if mime_type.startswith("text/"):
return "text", True, None
if mime_type in {"application/json", "application/xml", "application/javascript"}:
return "text", True, None
if mime_type.startswith("application/"):
return "unsupported", False, "当前文件格式暂不支持预览"
if not raw_content:
return "text", True, None
try:
raw_content.decode("utf-8")
return "text", True, None
except UnicodeDecodeError:
return "unsupported", False, "当前文件不是可读文本,暂不支持预览"