Funkwhale offers users a facility to assign genre tags to items such as tracks, albums, and artists. The `tags_tag` table is populated automatically when new tags are found in uploaded content, and users can also enter custom tags. By default, the table is empty. This means that a user on a new pod won't see any results when attempting to tag items in the frontend.
To provide the best experience for new Funkwhale users, we should pre-populate this table with [genre tags from Musicbrainz](https://musicbrainz.org/genres). Doing this enables users to easily search for and select the tags they want to assign to their content without needing to create custom tags or upload tagged content.
| `id` | Integer | The randomly generated table ID | `tags_taggeditem.tag_id` foreign key | None |
| `musicbrainz_id` | UUID | The Musicbrainz genre tag `id`. Used to identify the tag in Musicbrainz fetches | None | None |
| `name` | String | The name of the tag. Assigned by Funkwhale during creation for use in URLs. Uses Pascal casing for consistency | None | Must be unique |
| `display_name` | String | The name of the tag as the user entered it or as it was originally written by Musicbrainz. Lowercase, normalizes spaces | None | None |
| `creation_date` | Timestamp with time zone | The date on which the tag was created | None | None |
To keep Funkwhale's database up-to-date with Musicbrainz's genre tags, we must fetch information from Musicbrainz periodically. We can use the following endpoint to fetch the information:
This endpoint accepts the `application/json` header for a JSON response. See the [Musicbrainz API documentation](https://musicbrainz.org/doc/MusicBrainz_API) for more information. The pagination can be controlled by passing the following options:
-`limit`: the number of results to return
-`offset`: the starting point of the page
The fetch task should fetch **all** pages, using the response `genre-count` to determine how many offset positions to pass.
```json
{
"genre-count": 1913,
"genre-offset": 24,
"genres": [
{
"disambiguation": "",
"id": "243975aa-1250-4429-8bd3-97080af44cf7",
"name": "afro trap"
},
{
"name": "afro-cuban jazz",
"id": "cdb11433-1ff1-4c88-be16-717567e1342f",
"disambiguation": ""
},
{
"name": "afro-funk",
"disambiguation": "",
"id": "fc00175b-2be9-4d73-ba91-27b3ca827223"
},
{
"name": "afro-jazz",
"disambiguation": "",
"id": "6f33d775-b4e2-473c-a7db-e34c525cc52d"
},
{
"disambiguation": "",
"id": "a7e0229c-6e53-45f1-a6f2-a791e78b159e",
"name": "afro-zouk"
},
{
"disambiguation": "funk/soul + West African sounds",
The fetch task should run _initially upon first startup_ and then _monthly_ thereafter. The pod admin must be able to disable this job or run it manually at their discretion.
| `name` | `display_name` | Funkwhale should automatically generate a Pascal cased `name` based on this entry |
4. If the `display_name` of a tag **exactly matches** a `name` in the Musicbrainz response but the tag has no `musicbrainz_id`, the `musicbrainz_id` should be populated
### Frontend behavior
#### Tagged uploads
When a user uploads new content with genre tags, the tagged item should be linked to any existing tags and new ones should be created if they're not found.
When a user uploads new content with _no_ genre tags, they should be able to select tags from a dropdown menu. This menu is populated with the tags from the database with the `display_name` shown in the list. When a tag is selected, the item is linked to the associated tag.
1. Store the entered string as the tag's `display_name`
2. Generate a Pascal cased `name` for the tag
3. Associate the targeted object with the new tag
#### Search results
Users should be able to search for tags using Funkwhale's in-app search. In search autocomplete and search results page, the `display_name` should be used. The `name` of the tag should be used to populate the search URL.
#### Cards
The `display_name` of the tag should be shown in pills against cards.
If the admin of a server wants to **disable** MusicBrainz tagging, they should be able to toggle this in their instance settings. If the setting is **disabled**: