Copyright Recommendations
Copyright Recommendations are user-submitted suggestions that a specific track is copyright-safe and should be added to the curated safe-songs catalog. Users vote or flag a track via the streamer dashboard; those submissions land in this queue with report_type = recommendation. Admins accept (promoting the track to the safe catalog, optionally into a specific Curated Playlist) or dismiss (rejecting the suggestion).
Where to find it
Admin sidebar → Recommendations (/copyright-recommendations).
Quick start
- Open Admin → Recommendations.
- Default status filter is Pending; flip to Confirmed or Dismissed to review past decisions.
- Read the song / artist / category for each row.
- Click Accept to promote to safe songs. In the dialog, optionally pick a Curated Playlist to drop the track into.
- Click Dismiss to reject the suggestion.
Detailed walkthrough
Status filters
A pill bar across the top: All, Pending, Confirmed, Dismissed. Pending is the default landing state. A \{N\} total counter sits at the right end of the bar; the list loads 50 rows at a time with no pagination controls.
Recommendation cards
Recommendations render as cards, not table rows. Each card shows:
- Song — track title, with an external-link icon to
open.spotify.com/track/…when a Spotify track ID is present. - Artist — track artist.
- Category — the
recommendation_categoryvalue, as an outline badge, when set. - Status — coloured badge.
- Confirmations —
\{N\} confirmations, the count of other users who upvoted the same recommendation.
Card actions, rendered only while the status is pending:
- Accept — opens the Accept Recommendation dialog.
- Dismiss — one-click reject.
Accept Recommendation dialog
- Title: "Accept Recommendation".
- Song name, artist, and the outcome hint "Will be added to global safe list".
- Add to curated playlist (optional) — dropdown of all curated playlists; default is None. Rendered only when at least one curated playlist exists.
- Accept — runs the shared approve mutation with the optional
addToPlaylistId.
Accepting inserts the track into the safe-songs catalog and, when a playlist is selected, also appends it to that playlist's track list.
Common scenarios
- Lo-fi track submitted by community — accept and route into the
loficurated playlist. - Obvious copyrighted track mis-recommended — dismiss; the recommendation moves to the Dismissed tab.
- Duplicate suggestions — the Confirmations column reveals how many users agree. Don't accept the same track twice; the second recommendation can be dismissed.
- Batch processing — cycle through Pending, decide one by one. There is no bulk action.
- Filter state is not in the URL — it lives in React state and resets on reload.
Permissions
| Action | Permission |
|---|---|
| View recommendations | copyright:read |
| Accept / dismiss | copyright:moderate |
copyright:moderate governs the global, cross-account safe/blocked lists and is distinct from copyright:edit. Dashboard entry requires admin:access. System admins implicitly hold both.
API
Recommendations share the copyright_reports table with copyright reports, distinguished by the report_type column (recommendation vs copyright). They use the same approve / dismiss mutations as reports.
This surface is GraphQL-first. There are no /v1/copyright/recommendations or /v1/copyright/reports* REST endpoints — the paths below are the admin app's own Next.js proxy routes.
| UI action | GraphQL | Admin proxy route |
|---|---|---|
| List recommendations (filtered) | adminReports(reportType: "recommendation", status, limit, offset) | GET /api/copyright/recommendations?status=... |
| Accept (with optional playlist) | approveReport(id, addToPlaylistId) | POST /api/copyright/reports/\{id\}/approve |
| Dismiss | dismissReport(id) | POST /api/copyright/reports/\{id\}/dismiss |
| Curated playlists for the dropdown | publicSystemPlaylists | GET /api/copyright/playlists |
The page is server-rendered: page.tsx runs the adminReports and publicSystemPlaylists queries through serverGqlSSR and hands the results to the client list, which refetches through the proxy whenever the status filter changes.
Tips & gotchas
- Accept is destructive-ish — the track enters the global safe catalog. Review the artist + track carefully.
- The playlist dropdown is optional, and disappears entirely when no curated playlists exist. Accepting without picking a playlist still promotes the track to the catalog; it just doesn't appear in any curated playlist yet.
- Dismissed recommendations can be re-submitted by the same user later. The UI does not dedupe across time.
- Reports and recommendations share one mutation; accepting a recommendation is implemented as
approveReportinternally.
Related
- Copyright Reports — user reports against unsafe tracks (opposite polarity)
- Copyright Review — combined vote-candidate review surface
- Curated Playlists — where accepted tracks can be routed
- Abuse Reports — reports filed against the recommendation system itself