125 lines
5.1 KiB
Python
125 lines
5.1 KiB
Python
from tools import *
|
||
from Utils.Config import *
|
||
from Utils.Lib import *
|
||
from AppBridge import *
|
||
# pip install beautifulsoup4 jsbeautifier cssbeautifier
|
||
from bs4 import BeautifulSoup
|
||
import jsbeautifier
|
||
import cssbeautifier
|
||
|
||
CUSTOM_MIME_MAP = {
|
||
"application/json": ".json",
|
||
"text/html": ".html",
|
||
"text/css": ".css",
|
||
"text/javascript": ".js",
|
||
"text/plain": ".txt",
|
||
}
|
||
|
||
# ------------------- HTML 压缩 -------------------
|
||
def CompressHtml(html_str: str) -> str:
|
||
"""
|
||
压缩 HTML 字符串(移除多余空白、注释,保留核心结构)
|
||
:param html_str: 原始 HTML 字符串
|
||
:return: 压缩后的 HTML 字符串
|
||
"""
|
||
# 解析 HTML
|
||
soup = BeautifulSoup(html_str, "html.parser")
|
||
# 移除所有注释节点
|
||
for comment in soup.find_all(string=lambda text: isinstance(text, str) and text.strip().startswith('<!--')):
|
||
comment.extract()
|
||
# 生成紧凑格式的 HTML(移除多余空白)
|
||
compressed = soup.prettify(formatter="minimal")
|
||
# 进一步移除换行和多余空格(保留标签内必要空格)
|
||
compressed = re.sub(r'\n\s*', '', compressed)
|
||
# 移除标签间多余空格(如 > < → ><)
|
||
compressed = re.sub(r'>\s+<', '><', compressed)
|
||
return compressed
|
||
|
||
# ------------------- JS 压缩 -------------------
|
||
def CompressJS(js_str: str) -> str:
|
||
"""
|
||
压缩 JS 字符串(移除空白、注释,保留语法正确性)
|
||
:param js_str: 原始 JS 字符串
|
||
:return: 压缩后的 JS 字符串
|
||
"""
|
||
# jsbeautifier 反向配置:禁用所有格式化,实现压缩
|
||
opts = jsbeautifier.default_options()
|
||
opts.indent_size = 0 # 无缩进
|
||
opts.space_in_empty_paren = False # 空括号无空格
|
||
opts.preserve_newlines = False # 不保留换行
|
||
opts.break_chained_methods = False # 链式方法不换行
|
||
opts.keep_array_indentation = False # 数组不缩进
|
||
opts.keep_function_indentation = False # 函数不缩进
|
||
opts.space_after_anon_function = False # 匿名函数后无空格
|
||
opts.space_before_conditional = False # 条件语句前无空格
|
||
|
||
# 先美化再压缩(确保语法正确),再移除注释
|
||
compressed = jsbeautifier.beautify(js_str, opts)
|
||
# 移除单行注释(// ...)
|
||
compressed = re.sub(r'//.*?$', '', compressed, flags=re.MULTILINE)
|
||
# 移除多行注释(/* ... */)
|
||
compressed = re.sub(r'/\*[\s\S]*?\*/', '', compressed)
|
||
# 移除多余空白(保留语法必要空格)
|
||
compressed = re.sub(r'\s+', ' ', compressed).strip()
|
||
return compressed
|
||
|
||
# ------------------- CSS 压缩 -------------------
|
||
def CompressCSS(css_str: str) -> str:
|
||
"""
|
||
压缩 CSS 字符串(移除空白、注释,保留语法正确性)
|
||
:param css_str: 原始 CSS 字符串
|
||
:return: 压缩后的 CSS 字符串
|
||
"""
|
||
# cssbeautifier 反向配置:禁用所有格式化
|
||
opts = cssbeautifier.default_options()
|
||
opts.indent_size = 0 # 无缩进
|
||
opts.newline_between_rules = False # 规则间不换行
|
||
opts.space_around_selector_separator = False # 选择器分隔符无空格
|
||
|
||
# 先美化再压缩,确保语法正确
|
||
compressed = cssbeautifier.beautify(css_str, opts)
|
||
# 移除单行注释(// ...)
|
||
compressed = re.sub(r'//.*?$', '', compressed, flags=re.MULTILINE)
|
||
# 移除多行注释(/* ... */)
|
||
compressed = re.sub(r'/\*[\s\S]*?\*/', '', compressed)
|
||
# 移除多余空白和换行
|
||
compressed = re.sub(r'\s+', ' ', compressed).strip()
|
||
# 移除 {}/: 前后多余空格(CSS 语法优化)
|
||
compressed = re.sub(r'\s*{\s*', '{', compressed)
|
||
compressed = re.sub(r'\s*}\s*', '}', compressed)
|
||
compressed = re.sub(r'\s*:\s*', ':', compressed)
|
||
compressed = re.sub(r'\s*;\s*', ';', compressed)
|
||
return compressed
|
||
|
||
def IsInRule(Path, Method):
|
||
try:
|
||
App.url_map.match(Path, Method)
|
||
return True
|
||
except: return False
|
||
|
||
@asyncs
|
||
def MakeCompressLater(code, ext, sha256):
|
||
if ext == ".html":
|
||
openw(os.path.join("./FastTemp", sha256), CompressHtml(code))
|
||
if ext == ".css":
|
||
openw(os.path.join("./FastTemp", sha256), CompressCSS(code))
|
||
if ext == ".js":
|
||
openw(os.path.join("./FastTemp", sha256), CompressJS(code))
|
||
|
||
@App.after_request
|
||
def FastTempLoader(Response: flask.Response):
|
||
try:
|
||
if Response.status_code != 200: return Response
|
||
if not Response: return Response
|
||
if os.path.isfile(os.path.join("./FastTemp", hashlib.sha256(Response.get_data()).hexdigest())):
|
||
return openr(os.path.join("./FastTemp", hashlib.sha256(Response.get_data()).hexdigest()))
|
||
if IsInRule(flask.request.path, flask.request.method): return Response
|
||
ContentType = Response.headers.get("Content-Type", "").split(";")[0].strip()
|
||
ext = CUSTOM_MIME_MAP.get(ContentType)
|
||
if not ext: return Response
|
||
MakeCompressLater(Response.get_data(as_text=True), ext, sha256=hashlib.sha256(Response.get_data()).hexdigest())
|
||
return Response
|
||
except:
|
||
pass
|
||
return Response
|