- Add `is_nsfw` column to `songs` and `playlists` tables
- Implement NSFW checkbox in song/playlist upload and edit forms
- Enforce NSFW content visibility: block unauthenticated users from viewing/streaming NSFW songs/playlists
- Filter NSFW content from public-facing lists (recent, popular, liked, etc.) unless user is logged in
- Add VIP-only visibility option for songs/playlists (admin/VIP users only)
- Display "EXPLICIT" badge on NSFW items in UI
- Add CSS styling for NSFW badge
- Update service layer to handle NSFW flag in CRUD operations and queries
- Add `/songs/bulk-visibility` POST endpoint to update visibility of multiple selected songs in bulk
- Enforces VIP permission checks before allowing 'vip' visibility setting
- Updates only songs user has permission to manage
- Extend song list UI with a new "Change Visibility" dropdown button and menu
- Menu items are conditionally rendered based on user role/VIP status
- Integrated with existing bulk action toolbar (toggles, prevents overlap)
- Implement frontend logic to:
- Toggle visibility dropdown independently from other bulk menus
- Submit selected song IDs and chosen visibility via fetch POST request
- Update UI in-place after successful update (badges, selection reset)
- Show toast notification with count of updated songs
- Includes badge rendering helper for consistent visibility display
- Introduce `vip` visibility level with CSS badge styling
- Update admin routes to restrict VIP setting to admins and VIP users
- Extend visibility logic in public routes (`canView`, `denyAccess`)
- Add VIP-specific SQL queries in playlists, social, and songs services
- Update EJS templates to show VIP badges and radio options for authorized users
- Add `generation_cooldown_seconds` column to `site_settings` (default 180s)
- Add `is_vip` column to `users` table (default 0/false)
- Introduce new `requireVip` middleware for enforcing VIP-only routes
- Restrict song generation access to VIP users or admins
- Add admin UI endpoints to grant/remove VIP status per user
- Allow admins to configure generation cooldown period via admin panel
- Update generation service to use dynamic cooldown (replacing hardcoded 3 minutes)
- Enhance generate page UI with Re-Gen button, improved placeholder text, and rate-limit state updates
- Update header navigation to show "My Music" and "Generate" only for VIP/admin users
- Centralize cover image processing into a new `imageService.processCoverImage()` helper that resizes and crops images to 1000×1000 JPEG.
- Replace ad-hoc embedded-art handling in admin routes (`writeEmbeddedCover`) and publish route (`uploadCover`) with unified `saveCover` function supporting both uploaded files and embedded picture buffers.
- Ensure all cover uploads (songs, playlists, generated songs) go through the new async processing pipeline with proper temp-file cleanup and error handling.
- Update routes to use `async` handlers where cover processing is involved.
- Add client-side genre dropdown populated from existing genres + option to add new genre via text input
- Introduce resolveGenre() helper to prioritize new-genre input over selection, handling the "__new__" sentinel value
- Update admin routes and form rendering to pass available genres and preserve user selections on validation errors
- Add listGenres() service method and CSS styling for genre field layout
This replaces the simple text input with a more user-friendly dropdown experience while still supporting custom genres.
- Introduces bulk checkboxes on the songs list page to select multiple songs
- Adds a toolbar with dropdown to add selected songs to existing playlists or create a new playlist
- Implements `/songs/bulk-add` POST endpoint for efficient batch addition of tracks to playlists
- Auto-upgrades playlist visibility when adding more-restrictive songs
- Supports preselecting songs via query parameter when creating a new playlist (`?songs=1,2,3`)
- Includes custom-styled checkboxes, toast notifications, and keyboard-friendly UI interactions
- Move /admin/* routes to /mymusic/* for clearer user-focused section naming
- Rename admin/ view templates to mymusic/ with updated paths and URLs
- Add cover art preview in song edit form with current cover thumbnail styling
- Update navigation active states to use new /mymusic* paths
- Change now-playing creator link from window.open() to direct navigation
- Display playlist creator name on public and admin playlist views
- Add shuffle playback button for playlists in player UI
- Implement drag-and-drop reordering for playlist tracks with visual feedback
- Improve email verification UX: redirect pending users to verify page with resend option
- Simplify admin routes by removing redundant role checks (user-based filtering already handled)
- Adjust featured playlist count and add "show all" links on home page
- Add public/logged_in/private visibility levels to songs and playlists via database migration (002_visibility.sql)
- Replace old `is_public` boolean with new `visibility` enum in admin forms, API schemas, and services
- Implement access control logic:
- Guests see only public items that don't contain restricted tracks
- Logged-in users see public + logged_in items (excluding private ones)
- Admins see all; regular users manage their own content
- Auto-upgrade playlist visibility when adding more restrictive songs
- Add UI badges for visibility levels and update admin views to reflect new hierarchy
PWA enhancements:
- Update web manifest with proper name, colors, and icon paths
- Add service worker (sw.js) with cache-first for static assets and network-first for HTML
- Register SW at root (/sw.js) with Service-Worker-Allowed header for full origin scope
- Add theme-color meta tag and navigate.js for client-side routing
Other:
- Update admin nav label from "Admin" to "My Music" to reflect role-based access
- Introduce `songUploadSchema` for flexible uploads where title/artist are optional
- Add `extractAudioMetadata()` to parse ID3/Vorbis tags and embedded cover art from uploaded files
- Merge form values with extracted metadata, falling back to filename-derived title if needed
- Automatically write embedded cover art to `media/covers/` when no manual cover is uploaded
- Update song form UI to indicate optional fields and tag-based fallback behavior
- Switch JSON rendering in partials from `<%-` (unescaped) to `<%=`, improving safety