- 品牌设置API: GET/PUT /api/settings/brand (名称/标语/Logo) - 管理页系统设置: 品牌编辑器表单(名称/标语/Logo URL) - 恢复Anthropic风格: 米色(#FBF9F6) 底色, Source Serif 4 + Noto Serif SC 衬线体 - 暖棕(#C2693A) 主色调, 更充裕的间距与留白, 庄重大气 - 全站中文: antd zhCN locale, 所有页面文本汉化 - 数据存储在 Config singleton, 运行时更新
69 lines
2.0 KiB
Python
69 lines
2.0 KiB
Python
import os
|
|
from contextlib import asynccontextmanager
|
|
from fastapi import FastAPI, HTTPException
|
|
from fastapi.middleware.cors import CORSMiddleware
|
|
from fastapi.responses import FileResponse
|
|
from starlette.responses import RedirectResponse
|
|
from starlette.background import BackgroundTask
|
|
import httpx
|
|
from database import init_db
|
|
from services.transcoder import transcode_mgr
|
|
from routes import streams_router, nodes_router, auth_router
|
|
from routes.settings_route import router as settings_router
|
|
from config import settings
|
|
|
|
|
|
@asynccontextmanager
|
|
async def lifespan(app: FastAPI):
|
|
await init_db()
|
|
yield
|
|
transcode_mgr.stop_all()
|
|
|
|
|
|
app = FastAPI(title="StreamCore", lifespan=lifespan)
|
|
|
|
app.add_middleware(
|
|
CORSMiddleware,
|
|
allow_origins=["*"],
|
|
allow_credentials=True,
|
|
allow_methods=["*"],
|
|
allow_headers=["*"],
|
|
)
|
|
|
|
app.include_router(streams_router)
|
|
app.include_router(nodes_router)
|
|
app.include_router(auth_router)
|
|
app.include_router(settings_router)
|
|
|
|
|
|
@app.get("/api/health")
|
|
async def health():
|
|
from services.multicast_detector import multicast_detector
|
|
return {
|
|
"status": "ok",
|
|
"multicast_available": multicast_detector.is_available(),
|
|
}
|
|
|
|
|
|
@app.get("/hls/{path:path}")
|
|
async def serve_hls(path: str):
|
|
hls_root = "/tmp/hls"
|
|
fs_path = os.path.join(hls_root, path)
|
|
if os.path.isfile(fs_path):
|
|
return FileResponse(fs_path)
|
|
|
|
target = f"http://127.0.0.1:{settings.media_origin_hls_port}/{path}"
|
|
try:
|
|
async with httpx.AsyncClient(timeout=httpx.Timeout(10)) as client:
|
|
req = client.build_request("GET", target)
|
|
resp = await client.send(req, stream=True)
|
|
from starlette.responses import StreamingResponse
|
|
return StreamingResponse(
|
|
resp.aiter_bytes(),
|
|
status_code=resp.status_code,
|
|
headers=dict(resp.headers),
|
|
media_type=resp.headers.get("content-type"),
|
|
)
|
|
except Exception:
|
|
raise HTTPException(status_code=404)
|