""" Hugging Face Spaces 启动入口 """ import os import sys import subprocess import time import signal import atexit def setup_environment(): """设置必要的环境变量和目录""" # 确保数据目录存在 try: os.makedirs("data", exist_ok=True) print("✅ 数据目录已创建") except Exception as e: print(f"⚠️ 创建数据目录失败: {str(e)}") # 打印环境诊断信息 print("🔍 系统信息:") print(f" - Python: {sys.version}") print(f" - 工作目录: {os.getcwd()}") print(f" - 用户: {os.getuid() if hasattr(os, 'getuid') else 'N/A'}") # 检查二进制文件 for binary in ["subconverter/subconverter", "clash_core/clash.meta-linux-amd64"]: if os.path.exists(binary): try: # 文件信息 print(f"✅ 发现二进制文件: {binary}") print(f" - 大小: {os.path.getsize(binary)} 字节") print(f" - 权限: {oct(os.stat(binary).st_mode)[-3:]}") print(f" - 可执行: {os.access(binary, os.X_OK)}") # 尝试获取文件类型 try: file_type = subprocess.check_output(["file", binary], universal_newlines=True) print(f" - 类型: {file_type.strip()}") except Exception as e: print(f" - 获取文件类型失败: {str(e)}") # 设置执行权限 os.chmod(binary, 0o755) print(f"✅ {binary}权限已设置") except Exception as e: print(f"⚠️ 检查{binary}时出错: {str(e)}") else: print(f"❌ 找不到二进制文件: {binary}") # 尝试执行 Clash 来获取版本信息 clash_path = "clash_core/clash.meta-linux-amd64" if os.path.exists(clash_path): try: version_output = subprocess.check_output([clash_path, "-v"], stderr=subprocess.STDOUT, universal_newlines=True, timeout=2) print(f"✅ Clash版本信息: {version_output.strip()}") except Exception as e: print(f"⚠️ 获取Clash版本信息失败: {str(e)}") # 设置环境变量 os.environ["FLASK_PORT"] = "7860" # HF Spaces 要求的端口 # 如果没有设置 SUB_URL 环境变量,尝试从 .env 加载 if not os.environ.get("SUB_URL") and os.path.exists(".env"): try: with open(".env", "r") as f: for line in f: line = line.strip() if line and not line.startswith("#"): key, value = line.split("=", 1) os.environ[key] = value print("✅ 从.env加载了环境变量") except Exception as e: print(f"⚠️ 加载.env失败: {str(e)}") def start_app(): """启动应用程序""" print("🚀 正在启动 Simple Clash Relay...") # 使用 gunicorn 启动 Flask 应用 app_process = subprocess.Popen( ["gunicorn", "--bind", "0.0.0.0:7860", "--workers", "2", "--threads", "4", "--timeout", "120", "app.main:app"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True ) # 注册应用程序退出时的清理函数 def cleanup(): print("🛑 正在停止应用...") app_process.terminate() try: app_process.wait(timeout=5) except subprocess.TimeoutExpired: app_process.kill() atexit.register(cleanup) # 信号处理 def signal_handler(sig, frame): print(f"📢 接收到信号 {sig},正在退出...") cleanup() sys.exit(0) signal.signal(signal.SIGINT, signal_handler) signal.signal(signal.SIGTERM, signal_handler) # 输出应用程序日志 while True: output = app_process.stdout.readline() if output: print(output, end="") # 如果进程已经退出,退出循环 if app_process.poll() is not None: break # 获取剩余输出 remaining_output, _ = app_process.communicate() if remaining_output: print(remaining_output, end="") return app_process.returncode if __name__ == "__main__": setup_environment() exit_code = start_app() sys.exit(exit_code)