The Web API layer has been successfully implemented and is ready for testing.
src/SpotifyTools.Web/
├── Controllers/
│ ├── GenresController.cs # Genre and genre-track endpoints
│ ├── TracksController.cs # Track CRUD and search
│ ├── PlaylistsController.cs # Playlist management
│ └── ClustersController.cs # Cluster operations
├── DTOs/
│ ├── GenreDto.cs
│ ├── TrackDto.cs
│ ├── ArtistSummaryDto.cs
│ ├── PlaylistDto.cs
│ ├── ClusterDto.cs
│ ├── PagedResult.cs
│ └── CreatePlaylistRequest.cs
├── Program.cs # DI configuration, Swagger setup
└── appsettings.json # Connection strings
GET /api/genres- List all genres with track/artist countsGET /api/genres/{genreName}/tracks?page=1&pageSize=50- Paginated tracks for a genre
GET /api/tracks?page=1&pageSize=50&sortBy=name- Paginated all tracksGET /api/tracks/search?q={query}&page=1&pageSize=50- Search tracksGET /api/tracks/{id}- Get single track by ID
GET /api/playlists- List all playlistsGET /api/playlists/{id}- Get playlist with tracksPOST /api/playlists- Create new playlistPOST /api/playlists/{id}/tracks- Add tracks to playlist (bulk)DELETE /api/playlists/{id}/tracks/{trackId}- Remove track from playlistDELETE /api/playlists/{id}- Delete playlist
GET /api/clusters- List saved clustersGET /api/clusters/{id}- Get cluster by IDGET /api/clusters/suggested?minTracksPerCluster=20- Generate suggested clustersPOST /api/clusters- Save a new clusterPUT /api/clusters/{id}- Update clusterDELETE /api/clusters/{id}- Delete clusterPOST /api/clusters/{id}/finalize- Mark cluster as finalizedPOST /api/clusters/{id}/create-playlist?makePublic=false- Create Spotify playlist from cluster
- Pagination - All list endpoints support server-side pagination
- DTOs - Clean separation between domain entities and API contracts
- Swagger/OpenAPI - Auto-generated API documentation at root URL
- Dependency Injection - Reuses all existing services (Analytics, Sync, UnitOfWork)
- CORS Enabled - Ready for frontend clients
- Error Handling - Consistent 500 error responses with logging
The API uses the same configuration as the CLI tool:
- Database: PostgreSQL on localhost:5433
- Connection string: Defined in
appsettings.json - Port: Default 5000 (HTTP) / 5001 (HTTPS)
-
Test the API:
cd src/SpotifyTools.Web dotnet runThen open browser to
https://localhost:5001for Swagger UI -
Sample API Calls:
# Get all genres curl https://localhost:5001/api/genres # Get tracks for a genre (paginated) curl "https://localhost:5001/api/genres/rock/tracks?page=1&pageSize=20" # Search tracks curl "https://localhost:5001/api/tracks/search?q=metallica" # Get suggested clusters curl "https://localhost:5001/api/clusters/suggested?minTracksPerCluster=20"
-
Build Blazor UI (Next phase)
- Add Razor components for three-panel layout
- Create ApiClientService for typed HTTP calls
- Implement virtualized lists with Blazor.Virtualizer
Clean Separation:
Blazor Components → HTTP → API Controllers → Services → Repositories → Database
Same Process Hosting:
- API and Blazor will run in the same process
- HTTP calls are in-memory (localhost loopback)
- Minimal overhead (~1-5ms latency)
- All database columns use snake_case (enforced by EFCore.NamingConventions)
- Artist.Genres is stored as string[] in domain, converted to List in DTOs
- Playlist entity doesn't have SpotifyId (uses Id field directly)
- IRepository.Delete is synchronous, not async
- PagedResult includes HasNextPage/HasPreviousPage helpers
Branch: cop_webui
Status: ✅ Ready for testing
Build: ✅ Compiles successfully
The web UI is now complete with a three-panel layout!
-
Three-Panel Layout (
/)- Left Panel: Genre list (clickable, shows track counts)
- Center Panel: Track browser with checkboxes for multi-select
- Right Panel: Playlist list
-
ApiClientService - Typed HTTP client for calling the API
-
Bootstrap 5 - Modern responsive styling
-
Interactive Features:
- Click genre → Loads tracks
- Select tracks with checkboxes
- Refresh button to reload data
cd src/SpotifyTools.Web
dotnet runThen visit:
- http://localhost:5241/ - Web UI (three-panel interface)
- http://localhost:5241/swagger - API documentation
Important: If the UI doesn't respond to clicks, do a hard refresh (Shift+Refresh or Cmd+Shift+R on Mac) to clear browser cache and establish the Blazor WebSocket connection.
✅ View all genres
✅ Browse tracks by genre (click genre to load tracks)
✅ Multi-select tracks with checkboxes
✅ View all playlists
✅ Loading states and spinners
✅ Interactive Blazor Server with SignalR
⏳ Add tracks to playlist (coming next)
⏳ Create new playlists (coming next)
⏳ Drag & drop (future enhancement)
To make it fully functional:
- Implement "Add to Playlist" modal with playlist selector
- Implement "Create New Playlist" modal
- Add playlist detail view (show tracks in selected playlist)
- Add virtualization for large track lists (100+ items)
- Add search/filter for tracks
- Implement drag-drop (optional enhancement)