feat: 视觉记录系统 v1.0.0 - 摄像头定时拍照+智能分析+Web界面
This commit is contained in:
158
web/app.py
Normal file
158
web/app.py
Normal file
@@ -0,0 +1,158 @@
|
||||
"""
|
||||
Web 后端 - FastAPI
|
||||
"""
|
||||
from fastapi import FastAPI, HTTPException
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
from fastapi.responses import FileResponse, HTMLResponse
|
||||
from pathlib import Path
|
||||
import uvicorn
|
||||
import json
|
||||
|
||||
from config import WEB_PORT, WEB_HOST, IMAGES_DIR
|
||||
from database import db
|
||||
from scheduler import scheduler
|
||||
from camera import list_available_cameras, CameraCapture
|
||||
|
||||
|
||||
# 创建应用
|
||||
app = FastAPI(title="视觉记录系统")
|
||||
|
||||
# 静态文件目录
|
||||
STATIC_DIR = Path(__file__).parent / "static"
|
||||
app.mount("/static", StaticFiles(directory=str(STATIC_DIR)), name="static")
|
||||
app.mount("/images", StaticFiles(directory=str(IMAGES_DIR)), name="images")
|
||||
|
||||
|
||||
# ============== 页面路由 ==============
|
||||
|
||||
@app.get("/", response_class=HTMLResponse)
|
||||
async def index():
|
||||
"""主页"""
|
||||
index_file = STATIC_DIR / "index.html"
|
||||
if index_file.exists():
|
||||
return FileResponse(str(index_file))
|
||||
return HTMLResponse("<h1>请创建 index.html</h1>")
|
||||
|
||||
|
||||
# ============== API 路由 ==============
|
||||
|
||||
@app.get("/api/status")
|
||||
async def get_status():
|
||||
"""获取系统状态"""
|
||||
stats = db.get_stats()
|
||||
scheduler_status = scheduler.get_status()
|
||||
|
||||
return {
|
||||
"scheduler": scheduler_status,
|
||||
"stats": stats
|
||||
}
|
||||
|
||||
|
||||
@app.get("/api/cameras")
|
||||
async def get_cameras():
|
||||
"""获取可用摄像头"""
|
||||
cameras = list_available_cameras()
|
||||
details = []
|
||||
for cam_index in cameras:
|
||||
cam = CameraCapture(cam_index)
|
||||
details.append(cam.get_camera_info())
|
||||
cam.close()
|
||||
return {"cameras": details}
|
||||
|
||||
|
||||
@app.post("/api/scheduler/start")
|
||||
async def start_scheduler(interval: int = None, auto_analyze: bool = None):
|
||||
"""启动定时拍照"""
|
||||
result = scheduler.start(interval=interval, auto_analyze=auto_analyze)
|
||||
if not result['success']:
|
||||
raise HTTPException(status_code=400, detail=result['error'])
|
||||
return result
|
||||
|
||||
|
||||
@app.post("/api/scheduler/stop")
|
||||
async def stop_scheduler():
|
||||
"""停止定时拍照"""
|
||||
return scheduler.stop()
|
||||
|
||||
|
||||
@app.post("/api/scheduler/interval")
|
||||
async def set_interval(interval: int):
|
||||
"""设置拍照间隔"""
|
||||
return scheduler.set_interval(interval)
|
||||
|
||||
|
||||
@app.post("/api/capture")
|
||||
async def capture_now():
|
||||
"""立即拍照"""
|
||||
result = scheduler.capture_now()
|
||||
if not result['success']:
|
||||
raise HTTPException(status_code=500, detail=result['error'])
|
||||
return result
|
||||
|
||||
|
||||
@app.post("/api/analyze/{image_id}")
|
||||
async def analyze_image(image_id: int):
|
||||
"""分析指定图片"""
|
||||
result = scheduler.analyze_now(image_id)
|
||||
if not result['success']:
|
||||
raise HTTPException(status_code=500, detail=result['error'])
|
||||
return result
|
||||
|
||||
|
||||
@app.post("/api/analyze/unanalyzed")
|
||||
async def analyze_unanalyzed():
|
||||
"""分析所有未分析的图片"""
|
||||
results = scheduler.analyze_unanalyzed()
|
||||
return {"results": results}
|
||||
|
||||
|
||||
@app.get("/api/images")
|
||||
async def get_images(limit: int = 50, offset: int = 0, analyzed_only: bool = False):
|
||||
"""获取图片列表"""
|
||||
images = db.get_images(limit=limit, offset=offset, analyzed_only=analyzed_only)
|
||||
return {"images": images, "total": len(images)}
|
||||
|
||||
|
||||
@app.get("/api/images/{image_id}")
|
||||
async def get_image(image_id: int):
|
||||
"""获取单个图片详情"""
|
||||
image = db.get_image_by_id(image_id)
|
||||
if not image:
|
||||
raise HTTPException(status_code=404, detail="图片不存在")
|
||||
|
||||
events = db.get_events_by_image(image_id)
|
||||
image['events'] = events
|
||||
return image
|
||||
|
||||
|
||||
@app.delete("/api/images/{image_id}")
|
||||
async def delete_image(image_id: int):
|
||||
"""删除图片"""
|
||||
success = db.delete_image(image_id)
|
||||
if not success:
|
||||
raise HTTPException(status_code=404, detail="图片不存在")
|
||||
return {"success": True}
|
||||
|
||||
|
||||
@app.get("/api/events")
|
||||
async def get_events(limit: int = 50, offset: int = 0, event_type: str = None):
|
||||
"""获取事件列表"""
|
||||
events = db.get_events(limit=limit, offset=offset, event_type=event_type)
|
||||
return {"events": events, "total": len(events)}
|
||||
|
||||
|
||||
@app.get("/api/stats")
|
||||
async def get_stats():
|
||||
"""获取统计信息"""
|
||||
return db.get_stats()
|
||||
|
||||
|
||||
# ============== 启动 ==============
|
||||
|
||||
def run_web():
|
||||
"""启动 Web 服务"""
|
||||
uvicorn.run(app, host=WEB_HOST, port=WEB_PORT)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
run_web()
|
||||
Reference in New Issue
Block a user