feat(genres): allow custom track genres#14424
Merged
Merged
Conversation
- discovery indexer: drop genre allowlist enforcement at write time; cap genre at 100 chars; allowlist retained as canonical reference for read-side trending/metrics aggregations - sdk: relax track + album + playlist upload schemas to z.string().min(1).max(100); regenerate from updated swagger (generated Genre enum dropped); manual Genre enum re-exported from index with PascalCase keys to preserve @audius/sdk consumer compatibility; new GenreString = Genre | string helper type - web: SelectGenreField rewritten to TextField + native <datalist> so users can pick a known genre via autocomplete or type a custom one Read-side genre filters (trending, get_genre_metrics) still scope to the canonical set — tracks tagged with custom genres won't appear in those endpoints. Mobile genre picker is untouched and will be addressed in a follow-up PR. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
8 tasks
|
7 tasks
Contributor
🌐 Web preview readyPreview URL: https://audius-web-preview-pr-14424.audius.workers.dev Unique preview for this PR (deployed from this branch). |
dylanjeffers
added a commit
to AudiusProject/api
that referenced
this pull request
May 29, 2026
Fresh branch off main — supersedes #874. ## Summary - `api/v1_track.go`: dropped the `oneof='Electronic' 'Rock' ...` validators on `CreateTrackRequest.Genre` (required) and `UpdateTrackRequest.Genre` (omitempty); replaced with `min=1,max=100` and `max=100`. - `api/swagger/swagger-v1.yaml`: `genre` schema changed from string-enum to plain string with `maxLength: 100`. Canonical values moved into the description so generated docs/SDKs still surface them as autocomplete suggestions. - `indexer/constants.go`: added `MaxTrackGenreLength = 100` next to `Entity_Track`. Forward-looking — when the Go indexer takes over track entity-event consumption from the Python `discovery-provider`, this is the rule the new handler should enforce, mirroring discovery-provider's `CHARACTER_LIMIT_GENRE`. Pairs with [AudiusProject/apps#14424](AudiusProject/apps#14424) — the SDK regen, Python indexer relaxation, and web UI live there. **Ship this first (or together)** so the Python indexer relaxation isn't gated on a Go API that still rejects custom genres at the validate-tag layer. ## Test plan - [x] Existing test fixtures using `genre: "Electronic"` still pass (those values are still valid strings). - [x] `go build ./indexer` clean. - [ ] POST `/v1/tracks` with `genre: "Phonk"` succeeds. - [ ] POST with `genre: ""` returns 400 (`min=1`). - [ ] POST with a 101-char genre returns 400 (`max=100`). - [ ] Generated swagger docs at `/v1/swagger.yaml` show the new schema with description containing canonical values. - [ ] When the Go indexer's track-event consumer is implemented, it references `indexer.MaxTrackGenreLength` rather than introducing its own enum check. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Resolves import/order lint errors in AlbumsApi.test.ts and PlaylistsApi.test.ts that failed SDK CI. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
6 tasks
dylanjeffers
added a commit
that referenced
this pull request
May 29, 2026
…14427) Follow-up to #14424. The first genres PR shipped custom-genre support on the SDK and web edit form, but the audit afterward found three gaps that block the same UX on mobile and silently re-tighten the rule in common's form schema. ## Summary ### 1. Mobile track upload/edit picker (Tier 1 #1) `packages/mobile/src/screens/edit-track-screen/screens/SelectGenreScreen.tsx` was a strict `ListSelectionScreen` populated from `GENRES` — users on mobile had no way to enter a custom genre. Rewritten to: - Disable `ListSelectionScreen`'s internal search and pass a controlled `TextInput` in the `header` prop instead. - Compute the suggestion list from the typed input: filter `GENRES` by substring match, and when the input is non-empty and doesn't match any known label/value exactly, prepend a synthetic `{ value: <input>, label: 'Use "<input>" as a custom genre' }` item at the top. - Tap any item — known or custom — commits via the existing `onChange`/`setValue` flow. - `maxLength={100}` caps to match the SDK/API limit. ### 2. Form schema + GenreString re-export (Tier 1 #2) - `packages/common/src/schemas/upload/uploadFormSchema.ts:58` was still `z.enum(Object.values(Genre))` despite the SDK schema relaxing in #14424. The web `SelectGenreField` lets users type "Phonk", but this form-level validator would reject it before the submit handler ever called the SDK. Changed to `z.string().min(1).max(100)`. - `packages/common/src/utils/genres.ts` now re-exports `GenreString` from `@audius/sdk` so downstream consumers can opt into the loose type for read-side / metadata paths. I audited all 12 files flagged in the post-merge audit. The state/filter typings (`Search.ts`, `trending/types.ts`, `trending/actions.ts`, `lineups/useTrending.ts`, `search/useSearchResults.ts`, `quickSearch.ts`, `Analytics.ts`) are deliberately left as strict `Genre` — those represent user selections from the canonical filter UI, not received track data. Two files (`hooks/useTrackMetadata.ts` and `store/upload/types.ts`) already handled string values fine. ### 3. Mobile upload example (Tier 1 #3) `packages/mobile/examples/upload/App.tsx` had its own inline hardcoded `GENRES` chip list. Dropped the array, replaced the horizontal chip ScrollView with a freeform `TextInput` (placeholder hints common genres). Reference example for SDK consumers no longer implies "genres must be from this list." ## Verification - [x] `tsc --noEmit` in `packages/common` — clean - [x] `tsc --noEmit` in `packages/web` — clean - [x] `tsc --noEmit` in `packages/mobile` — clean - [ ] Mobile manual test: open the edit-track flow, navigate to Select Genre, type "Phonk", tap the "Use 'Phonk'…" row, confirm the form reflects "Phonk" and the upload succeeds end-to-end. - [ ] Mobile manual test: type a known prefix like "Tech" — confirm "Techno" and "Tech House" suggestions appear and tap committing them still works. - [ ] Web regression: confirm the custom-genre flow shipped in #14424 still works (no functional change there; only the form schema was loosened, which could only ever accept *more* inputs). ## Still deferred (Tier 2/3 from the audit) - Search and trending genre filters on web (`SearchFilters.tsx`, `TrendingPageContent.tsx`, `TrendingGenreSelectionPage.tsx`) and mobile (`SearchFilters.tsx`, `TrendingFilter*.tsx`) — still scoped to the canonical list. Even if loosened, the Python read-side aggregations (`index_trending.py`, `get_genre_metrics.py`) still `WHERE genre IN genre_allowlist`, so no data would back custom-genre filters until that's also addressed. - Write-side normalization ("Hip-Hop" vs "Hip Hop" vs "hip hop" still become distinct genres). - Mobile signup-flow `SelectGenresScreen` — preference picker, intentionally stays canonical. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fresh branch off main — supersedes #14419.
Summary
z.enum(Genre)toz.string().min(1).max(100). SDK regenerated from updated swagger (generatedGenreenum dropped). ManualGenreenum is now the canonical export from@audius/sdkwith PascalCase keys, plus a newGenreString = Genre | stringhelper type.SelectGenreFieldrewritten from<SelectField>to<TextField>+ native<datalist>— users can pick a known genre via autocomplete or type a custom one.maxLength=100gives the built-in harmony character counter.Pairs with AudiusProject/api genres/custom — the Go API must ship first (or together) so it stops rejecting custom-genre POSTs before this indexer relaxation lands.
What we deliberately did NOT change
index_trending.pyandget_genre_metrics.pystill scope togenre_allowlist. Custom-genre tracks silently won't appear in trending or genre-metrics endpoints — intentional MVP cut.packages/mobilehas its own genre picker that wasn't touched — follow-up PR needed.Breaking change (browser dist only)
packages/sdk/src/sdk/types/Genre.tskeys changed from SCREAMING_SNAKE to PascalCase to match the previously-generated enum that downstream consumers all imported. External integrations referencingwindow.audiusSdk.Genre.HIP_HOP_RAPetc. via the browser dist need to update toGenre.HipHopRap.Test plan
tsc --noEmitinpackages/sdk— cleanvitest runinpackages/sdk— 17 files, 196 passed, 2 skippednode ./src/sdk/api/generator/gen.js --spec <local-edited-swagger>against the updated swagger🤖 Generated with Claude Code