""" 故事提取工具 - 入口脚本 支持开发模式和 PyInstaller 打包模式 """ import os import sys import shutil import socket import webbrowser import threading import time def get_base_dir() -> str: """获取基础目录""" if getattr(sys, 'frozen', False): return os.path.dirname(sys.executable) else: return os.path.dirname(os.path.abspath(__file__)) def cleanup_data(base_dir: str): """启动时清理数据库缓存和临时文件""" print("正在清理上次运行的数据...") # 实际数据目录(开发模式下在 backend/ 下,打包后在 exe 同级目录下) if getattr(sys, 'frozen', False): data_dir = base_dir else: data_dir = os.path.join(base_dir, "backend") # 清理数据库 db_dir = os.path.join(data_dir, "data") if os.path.exists(db_dir): for f in os.listdir(db_dir): if f.endswith((".db", ".db-wal", ".db-shm", ".db-journal")): try: os.remove(os.path.join(db_dir, f)) except Exception: pass # 清理上传的文件 uploads_dir = os.path.join(data_dir, "uploads") if os.path.exists(uploads_dir): for subdir in ["transcripts", "persons", "photos"]: sub = os.path.join(uploads_dir, subdir) if os.path.exists(sub): shutil.rmtree(sub, ignore_errors=True) # 清理导出文件 exports_dir = os.path.join(data_dir, "exports") if os.path.exists(exports_dir): shutil.rmtree(exports_dir, ignore_errors=True) # 重新创建目录结构 for d in [db_dir, uploads_dir, exports_dir]: os.makedirs(d, exist_ok=True) for sub in ["transcripts", "persons", "photos"]: os.makedirs(os.path.join(uploads_dir, sub), exist_ok=True) print("清理完成!") def find_free_port(start_port: int = 8000) -> int: """找到一个可用端口""" for port in range(start_port, start_port + 100): try: with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: s.bind(("127.0.0.1", port)) return port except OSError: continue return start_port def open_browser(port: int): """延迟打开浏览器""" time.sleep(2) url = f"http://localhost:{port}" print(f"\n正在打开浏览器: {url}") webbrowser.open(url) def main(): base_dir = get_base_dir() # 1. 清理数据 cleanup_data(base_dir) # 2. 找可用端口 port = find_free_port(8000) if port != 8000: print(f"端口 8000 被占用,使用端口 {port}") # 3. 启动服务 print(f"\n{'='*50}") print(f" 故事提取工具") print(f" 访问地址: http://localhost:{port}") print(f" 按 Ctrl+C 停止服务") print(f"{'='*50}\n") # 延迟打开浏览器(不阻塞主线程) threading.Thread(target=open_browser, args=(port,), daemon=True).start() # 启动 uvicorn os.environ["APP_PORT"] = str(port) if getattr(sys, 'frozen', False): # 打包模式:直接 import 并运行 import uvicorn # 把 backend 目录加到 path,让 app 包能被找到 backend_dir = os.path.join(base_dir, "backend") if backend_dir not in sys.path: sys.path.insert(0, backend_dir) uvicorn.run("app.main:app", host="0.0.0.0", port=port, log_level="info") else: # 开发模式 import uvicorn backend_dir = os.path.join(base_dir, "backend") if backend_dir not in sys.path: sys.path.insert(0, backend_dir) uvicorn.run("app.main:app", host="0.0.0.0", port=port, log_level="info") if __name__ == "__main__": main()