Files
enigFM/backend/app/main.py
mamonov.ep 487da10365 Improve mini-player and add periodic sync
- Redesign mini-player: progress bar on top, centered controls
- Add vertical volume slider with popup on hover
- Add volume percentage display
- Add custom speaker SVG icons
- Add periodic sync every 10 seconds for playback synchronization
- Broadcast user_joined when connecting via WebSocket
- Disable nginx proxy buffering for streaming
- Allow extra env variables in pydantic settings

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-12 16:53:56 +03:00

93 lines
2.8 KiB
Python

import asyncio
from contextlib import asynccontextmanager
from datetime import datetime
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from sqlalchemy import select
from .routers import auth, rooms, tracks, websocket, messages
from .database import async_session
from .models.room import Room
from .services.sync import manager
async def periodic_sync():
"""Send sync updates to all rooms every 10 seconds"""
while True:
await asyncio.sleep(10)
# Get all active rooms
for room_id in list(manager.active_connections.keys()):
try:
async with async_session() as db:
result = await db.execute(select(Room).where(Room.id == room_id))
room = result.scalar_one_or_none()
if not room or not room.is_playing:
continue
# Calculate current position
current_position = room.playback_position or 0
if room.playback_started_at:
elapsed = (datetime.utcnow() - room.playback_started_at).total_seconds() * 1000
current_position = int((room.playback_position or 0) + elapsed)
track_url = None
if room.current_track_id:
track_url = f"/api/tracks/{room.current_track_id}/stream"
await manager.broadcast_to_room(
room_id,
{
"type": "sync_state",
"is_playing": room.is_playing,
"position": current_position,
"current_track_id": str(room.current_track_id) if room.current_track_id else None,
"track_url": track_url,
"server_time": datetime.utcnow().isoformat(),
},
)
except Exception:
pass
@asynccontextmanager
async def lifespan(app: FastAPI):
# Start background sync task
sync_task = asyncio.create_task(periodic_sync())
yield
# Cleanup
sync_task.cancel()
try:
await sync_task
except asyncio.CancelledError:
pass
app = FastAPI(title="EnigFM", description="Listen to music together with friends", lifespan=lifespan)
# CORS
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# Routers
app.include_router(auth.router)
app.include_router(rooms.router)
app.include_router(tracks.router)
app.include_router(messages.router)
app.include_router(websocket.router)
@app.get("/")
async def root():
return {"message": "EnigFM API", "version": "1.0.0"}
@app.get("/health")
async def health():
return {"status": "ok"}