Add multiple features: auth, uploads, queue management, and filters
- Replace email with username for authentication - Update User model, schemas, and auth endpoints - Update frontend login and register views - Add migration to remove email column - Add multiple track upload support - New backend endpoint for bulk upload - Frontend multi-file selection with progress - Auto-extract metadata from ID3 tags - Visual upload progress for each file - Prevent duplicate tracks in room queue - Backend validation for duplicates - Visual indication of tracks already in queue - Error handling with user feedback - Add bulk track selection for rooms - Multi-select mode with checkboxes - Bulk add endpoint with duplicate filtering - Selection counter and controls - Add track filters in room modal - Search by title and artist - Filter by "My tracks" - Filter by "Not in queue" - Live filtering with result counter - Improve Makefile - Add build-backend and build-frontend commands - Add rebuild-backend and rebuild-frontend commands - Add rebuild-clean variants - Update migrations to run in Docker 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -7,7 +7,7 @@ from ..database import get_db
|
||||
from ..models.user import User
|
||||
from ..models.room import Room, RoomParticipant
|
||||
from ..models.track import RoomQueue
|
||||
from ..schemas.room import RoomCreate, RoomResponse, RoomDetailResponse, QueueAdd
|
||||
from ..schemas.room import RoomCreate, RoomResponse, RoomDetailResponse, QueueAdd, QueueAddMultiple
|
||||
from ..schemas.track import TrackResponse
|
||||
from ..schemas.user import UserResponse
|
||||
from ..services.auth import get_current_user
|
||||
@@ -197,6 +197,21 @@ async def add_to_queue(
|
||||
db: AsyncSession = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user),
|
||||
):
|
||||
# Check if track already in queue
|
||||
result = await db.execute(
|
||||
select(RoomQueue).where(
|
||||
RoomQueue.room_id == room_id,
|
||||
RoomQueue.track_id == data.track_id,
|
||||
)
|
||||
)
|
||||
existing_item = result.scalar_one_or_none()
|
||||
|
||||
if existing_item:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="Track already in queue",
|
||||
)
|
||||
|
||||
# Get max position
|
||||
result = await db.execute(
|
||||
select(func.max(RoomQueue.position)).where(RoomQueue.room_id == room_id)
|
||||
@@ -221,6 +236,69 @@ async def add_to_queue(
|
||||
return {"status": "added"}
|
||||
|
||||
|
||||
@router.post("/{room_id}/queue/bulk")
|
||||
async def add_multiple_to_queue(
|
||||
room_id: UUID,
|
||||
data: QueueAddMultiple,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user),
|
||||
):
|
||||
"""Add multiple tracks to queue at once, skipping duplicates."""
|
||||
if not data.track_ids:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="No tracks provided",
|
||||
)
|
||||
|
||||
# Get existing tracks in queue
|
||||
result = await db.execute(
|
||||
select(RoomQueue.track_id).where(RoomQueue.room_id == room_id)
|
||||
)
|
||||
existing_track_ids = set(result.scalars().all())
|
||||
|
||||
# Filter out duplicates
|
||||
new_track_ids = [tid for tid in data.track_ids if tid not in existing_track_ids]
|
||||
|
||||
if not new_track_ids:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="All tracks already in queue",
|
||||
)
|
||||
|
||||
# Get max position
|
||||
result = await db.execute(
|
||||
select(func.max(RoomQueue.position)).where(RoomQueue.room_id == room_id)
|
||||
)
|
||||
max_pos = result.scalar() or 0
|
||||
|
||||
# Add all new tracks
|
||||
added_count = 0
|
||||
for i, track_id in enumerate(new_track_ids):
|
||||
queue_item = RoomQueue(
|
||||
room_id=room_id,
|
||||
track_id=track_id,
|
||||
position=max_pos + i + 1,
|
||||
added_by=current_user.id,
|
||||
)
|
||||
db.add(queue_item)
|
||||
added_count += 1
|
||||
|
||||
await db.flush()
|
||||
|
||||
# Notify others
|
||||
await manager.broadcast_to_room(
|
||||
room_id,
|
||||
{"type": "queue_updated"},
|
||||
)
|
||||
|
||||
skipped_count = len(data.track_ids) - added_count
|
||||
return {
|
||||
"status": "added",
|
||||
"added": added_count,
|
||||
"skipped": skipped_count,
|
||||
}
|
||||
|
||||
|
||||
@router.delete("/{room_id}/queue/{track_id}")
|
||||
async def remove_from_queue(
|
||||
room_id: UUID,
|
||||
|
||||
Reference in New Issue
Block a user