初始化提交
This commit is contained in:
39
app/utils/file_security.py
Normal file
39
app/utils/file_security.py
Normal file
@@ -0,0 +1,39 @@
|
||||
import os
|
||||
from asyncio import log
|
||||
|
||||
|
||||
def resolve_path_within_directory(
|
||||
base_dir: str,
|
||||
unsafe_path: str,
|
||||
*,
|
||||
require_file: bool = True,
|
||||
) -> str:
|
||||
# 用户传入的路径可能是文件名、相对路径、绝对路径,也可能夹带 `../`。
|
||||
# 这里统一解析成真实路径,并用 commonpath 判断它是否仍在允许目录内。
|
||||
# 这样比简单判断字符串前缀可靠,可以覆盖符号链接、重复分隔符、相对路径
|
||||
# 等场景,适用于上传目录、素材目录、任务产物目录这类白名单目录。
|
||||
if not unsafe_path:
|
||||
raise ValueError("empty path is not allowed")
|
||||
|
||||
base_dir_real = os.path.realpath(base_dir)
|
||||
candidate_path = unsafe_path
|
||||
|
||||
if not os.path.isabs(candidate_path):
|
||||
candidate_path = os.path.join(base_dir_real, candidate_path)
|
||||
|
||||
|
||||
resolved_path = os.path.realpath(candidate_path)
|
||||
|
||||
try:
|
||||
common_path = os.path.commonpath([base_dir_real, resolved_path])
|
||||
except ValueError as exc:
|
||||
# Windows 下不同盘符会触发 ValueError,这类路径一定不属于允许目录。
|
||||
raise ValueError("path is outside the allowed directory") from exc
|
||||
|
||||
if common_path != base_dir_real:
|
||||
raise ValueError("path is outside the allowed directory")
|
||||
|
||||
if require_file and not os.path.isfile(resolved_path):
|
||||
raise ValueError("file does not exist")
|
||||
|
||||
return resolved_path
|
||||
236
app/utils/utils.py
Normal file
236
app/utils/utils.py
Normal file
@@ -0,0 +1,236 @@
|
||||
import json
|
||||
import locale
|
||||
import os
|
||||
from pathlib import Path
|
||||
import threading
|
||||
from typing import Any
|
||||
from uuid import uuid4
|
||||
|
||||
from loguru import logger
|
||||
|
||||
from app.models import const
|
||||
|
||||
|
||||
def get_response(status: int, data: Any = None, message: str = ""):
|
||||
obj = {
|
||||
"status": status,
|
||||
}
|
||||
if data:
|
||||
obj["data"] = data
|
||||
if message:
|
||||
obj["message"] = message
|
||||
return obj
|
||||
|
||||
|
||||
def to_json(obj):
|
||||
try:
|
||||
# Define a helper function to handle different types of objects
|
||||
def serialize(o):
|
||||
# If the object is a serializable type, return it directly
|
||||
if isinstance(o, (int, float, bool, str)) or o is None:
|
||||
return o
|
||||
# If the object is binary data, convert it to a base64-encoded string
|
||||
elif isinstance(o, bytes):
|
||||
return "*** binary data ***"
|
||||
# If the object is a dictionary, recursively process each key-value pair
|
||||
elif isinstance(o, dict):
|
||||
return {k: serialize(v) for k, v in o.items()}
|
||||
# If the object is a list or tuple, recursively process each element
|
||||
elif isinstance(o, (list, tuple)):
|
||||
return [serialize(item) for item in o]
|
||||
# If the object is a custom type, attempt to return its __dict__ attribute
|
||||
elif hasattr(o, "__dict__"):
|
||||
return serialize(o.__dict__)
|
||||
# Return None for other cases (or choose to raise an exception)
|
||||
else:
|
||||
return None
|
||||
|
||||
# Use the serialize function to process the input object
|
||||
serialized_obj = serialize(obj)
|
||||
|
||||
# Serialize the processed object into a JSON string
|
||||
return json.dumps(serialized_obj, ensure_ascii=False, indent=4)
|
||||
except Exception as e:
|
||||
logger.error(f"failed to serialize object to json: {str(e)}")
|
||||
return None
|
||||
|
||||
|
||||
def get_uuid(remove_hyphen: bool = False):
|
||||
u = str(uuid4())
|
||||
if remove_hyphen:
|
||||
u = u.replace("-", "")
|
||||
return u
|
||||
|
||||
|
||||
def root_dir():
|
||||
return os.path.dirname(os.path.dirname(os.path.dirname(os.path.realpath(__file__))))
|
||||
|
||||
|
||||
def storage_dir(sub_dir: str = "", create: bool = False):
|
||||
d = os.path.join(root_dir(), "storage")
|
||||
if sub_dir:
|
||||
d = os.path.join(d, sub_dir)
|
||||
if create and not os.path.exists(d):
|
||||
os.makedirs(d)
|
||||
|
||||
return d
|
||||
|
||||
|
||||
def resource_dir(sub_dir: str = ""):
|
||||
d = os.path.join(root_dir(), "resource")
|
||||
if sub_dir:
|
||||
d = os.path.join(d, sub_dir)
|
||||
return d
|
||||
|
||||
|
||||
def task_dir(sub_dir: str = ""):
|
||||
d = os.path.join(storage_dir(), "tasks")
|
||||
if sub_dir:
|
||||
d = os.path.join(d, sub_dir)
|
||||
if not os.path.exists(d):
|
||||
os.makedirs(d)
|
||||
return d
|
||||
|
||||
|
||||
def font_dir(sub_dir: str = ""):
|
||||
d = resource_dir("fonts")
|
||||
if sub_dir:
|
||||
d = os.path.join(d, sub_dir)
|
||||
if not os.path.exists(d):
|
||||
os.makedirs(d)
|
||||
return d
|
||||
|
||||
|
||||
def song_dir(sub_dir: str = ""):
|
||||
d = resource_dir("songs")
|
||||
if sub_dir:
|
||||
d = os.path.join(d, sub_dir)
|
||||
if not os.path.exists(d):
|
||||
os.makedirs(d)
|
||||
return d
|
||||
|
||||
|
||||
def public_dir(sub_dir: str = ""):
|
||||
d = resource_dir("public")
|
||||
if sub_dir:
|
||||
d = os.path.join(d, sub_dir)
|
||||
if not os.path.exists(d):
|
||||
os.makedirs(d)
|
||||
return d
|
||||
|
||||
|
||||
def run_in_background(func, *args, **kwargs):
|
||||
def run():
|
||||
try:
|
||||
func(*args, **kwargs)
|
||||
except Exception as e:
|
||||
logger.error(f"run_in_background error: {e}", exc_info=True)
|
||||
|
||||
thread = threading.Thread(target=run, daemon=False)
|
||||
thread.start()
|
||||
return thread
|
||||
|
||||
|
||||
def time_convert_seconds_to_hmsm(seconds) -> str:
|
||||
hours = int(seconds // 3600)
|
||||
seconds = seconds % 3600
|
||||
minutes = int(seconds // 60)
|
||||
milliseconds = int(seconds * 1000) % 1000
|
||||
seconds = int(seconds % 60)
|
||||
return "{:02d}:{:02d}:{:02d},{:03d}".format(hours, minutes, seconds, milliseconds)
|
||||
|
||||
|
||||
def text_to_srt(idx: int, msg: str, start_time: float, end_time: float) -> str:
|
||||
start_time = time_convert_seconds_to_hmsm(start_time)
|
||||
end_time = time_convert_seconds_to_hmsm(end_time)
|
||||
srt = """%d
|
||||
%s --> %s
|
||||
%s
|
||||
""" % (
|
||||
idx,
|
||||
start_time,
|
||||
end_time,
|
||||
msg,
|
||||
)
|
||||
return srt
|
||||
|
||||
|
||||
def str_contains_punctuation(word):
|
||||
for p in const.PUNCTUATIONS:
|
||||
if p in word:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def split_string_by_punctuations(s):
|
||||
result = []
|
||||
txt = ""
|
||||
|
||||
previous_char = ""
|
||||
next_char = ""
|
||||
for i in range(len(s)):
|
||||
char = s[i]
|
||||
if char == "\n":
|
||||
result.append(txt.strip())
|
||||
txt = ""
|
||||
continue
|
||||
|
||||
if i > 0:
|
||||
previous_char = s[i - 1]
|
||||
if i < len(s) - 1:
|
||||
next_char = s[i + 1]
|
||||
|
||||
if char == "." and previous_char.isdigit() and next_char.isdigit():
|
||||
# # In the case of "withdraw 10,000, charged at 2.5% fee", the dot in "2.5" should not be treated as a line break marker
|
||||
txt += char
|
||||
continue
|
||||
|
||||
if char == "," and previous_char.isdigit() and next_char.isdigit():
|
||||
# 英文数字里的千分位逗号不是断句符,例如 "1,000 years"。
|
||||
# Edge TTS 的 word boundary 通常会把这种数字整体作为连续内容返回;
|
||||
# 如果这里拆成 "1" 和 "000 years",后续字幕聚合会无法匹配脚本原文,
|
||||
# 进而错误回退到 Whisper。
|
||||
txt += char
|
||||
continue
|
||||
|
||||
if char not in const.PUNCTUATIONS:
|
||||
txt += char
|
||||
else:
|
||||
result.append(txt.strip())
|
||||
txt = ""
|
||||
result.append(txt.strip())
|
||||
# filter empty string
|
||||
result = list(filter(None, result))
|
||||
return result
|
||||
|
||||
|
||||
def md5(text):
|
||||
import hashlib
|
||||
|
||||
return hashlib.md5(text.encode("utf-8")).hexdigest()
|
||||
|
||||
|
||||
def get_system_locale():
|
||||
try:
|
||||
loc = locale.getdefaultlocale()
|
||||
# zh_CN, zh_TW return zh
|
||||
# en_US, en_GB return en
|
||||
language_code = loc[0].split("_")[0]
|
||||
return language_code
|
||||
except Exception:
|
||||
return "en"
|
||||
|
||||
|
||||
def load_locales(i18n_dir):
|
||||
_locales = {}
|
||||
for root, dirs, files in os.walk(i18n_dir):
|
||||
for file in files:
|
||||
if file.endswith(".json"):
|
||||
lang = file.split(".")[0]
|
||||
with open(os.path.join(root, file), "r", encoding="utf-8") as f:
|
||||
_locales[lang] = json.loads(f.read())
|
||||
return _locales
|
||||
|
||||
|
||||
def parse_extension(filename):
|
||||
return Path(filename).suffix.lower().lstrip('.')
|
||||
Reference in New Issue
Block a user