Genre tags
The issue
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.
The solution
To provide the best experience for new Funkwhale users, we should pre-populate this table with genre tags from Musicbrainz. 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.
Having these tags easily available also facilitates better tagging within Funkwhale in future, reducing the reliance on external tools such as Picard.
Feature behavior
Backend behavior
The tags_tag
table contains the following fields:
Field |
Data type |
Description |
Relations |
Constraints |
---|---|---|---|---|
|
Integer |
The randomly generated table ID |
|
None |
|
UUID |
The Musicbrainz genre tag |
None |
None |
|
String |
The name of the tag. Assigned by Funkwhale during creation for use in URLs. Uses Pascal casing for consistency |
None |
Must be unique |
|
Timestamp with time zone |
The date on which the tag was created |
None |
None |
Musicbrainz fetch task
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:
https://musicbrainz.org/ws/2/genre/all
This endpoint accepts the application/json
header for a JSON response. See the Musicbrainz API documentation for more information. The pagination can be controlled by passing the following options:
limit
: the number of results to returnoffset
: 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.
{
"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",
"id": "fcc58a18-9326-4c92-8b29-c294d44379c3",
"name": "afrobeat"
},
{
"id": "b8793fdb-bbc8-4418-a6f8-05eafbbe07ef",
"disambiguation": "West African urban/pop music",
"name": "afrobeats"
},
{
"name": "afropiano",
"id": "d42b567f-0952-424b-959d-bee6e5961cc0",
"disambiguation": ""
},
{
"disambiguation": "",
"id": "52349b68-9cad-496e-8785-00d53f410246",
"name": "afroswing"
},
{
"name": "agbadza",
"disambiguation": "",
"id": "c6d1e78b-ac82-4bb8-89d5-21e3226dc906"
},
{
"disambiguation": "",
"id": "b8ae0a3c-5826-4104-9663-fe8f828effa9",
"name": "agbekor"
},
{
"name": "aggrotech",
"disambiguation": "",
"id": "c844c144-90a8-4288-981e-e38275592688"
},
{
"name": "ahwash",
"id": "4802e6e4-f403-41d1-8e58-76e5cf4df81d",
"disambiguation": ""
},
{
"id": "50cc5641-b4f9-40b7-bf7a-6d903ac6c1c5",
"disambiguation": "",
"name": "aita"
},
{
"id": "aebbce35-0e8b-40e9-b04c-bebbbda124d0",
"disambiguation": "",
"name": "akishibu-kei"
},
{
"name": "al jeel",
"disambiguation": "",
"id": "0f8d3ff4-8cda-42c4-b462-10352cd01606"
},
{
"name": "algerian chaabi",
"id": "998efb76-2f98-41c8-8c5f-74c32e405e9f",
"disambiguation": ""
},
{
"name": "algorave",
"id": "e0a9d0d1-b86f-4344-82a9-022a84627087",
"disambiguation": ""
},
{
"name": "alloukou",
"disambiguation": "",
"id": "e367c884-d94d-4fba-abc4-8ac51d167ccf"
},
{
"disambiguation": "",
"id": "ef1d11cc-e70f-4885-ad6c-103f060d33b2",
"name": "alpenrock"
},
{
"disambiguation": "",
"id": "5f9cba3d-1a9f-46cd-8c49-7ed78d1f3354",
"name": "alternative country"
},
{
"name": "alternative dance",
"id": "8301f73c-9166-4108-bfeb-4fd22dc19083",
"disambiguation": ""
},
{
"name": "alternative folk",
"id": "0b48a36c-630f-4ee7-8cf3-480e3dd8be65",
"disambiguation": ""
},
{
"name": "alternative hip hop",
"disambiguation": "",
"id": "924943cd-73c8-45c0-96eb-74f2a15e5d6e"
},
{
"disambiguation": "",
"id": "7c4d0994-4c49-4c74-8763-df27fc0084cc",
"name": "alté"
}
]
}
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.
The task should use the following logic:
Call the Musicbrainz API to fetch new data
Verify the listed entries against the Funkwhale tag table. The
id
field in the response should be checked against themusicbrainz_id
fieldAny entries that do not currently exist in Funkwhale should be added with the following mapping:
Musicbrainz response field |
Tags table column |
Notes |
---|---|---|
|
|
|
|
|
Funkwhale should automatically generate a Pascal cased |
If the
name
of a tag exactly matches aname
in the Musicbrainz response but the tag has nomusicbrainz_id
, themusicbrainz_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.
In-app tagging
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 name
shown in the list. When a tag is selected, the item is linked to the associated tag.
If a user inserts a new tag, Funkwhale should:
Store the entered string as the tag’s
name
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 name
should be used. The name
of the tag should be used to populate the search URL.
Cards
The name
of the tag should be shown in pills against cards.
Admin options
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:
The sync task should stop running
Availability
Admin panel
App frontend
CLI
Responsible parties
Backend group:
Update the tracks table to support the new information
Update the API to support the new information, or create a new v2 endpoint
Create the new fetch task
Add admin controls for the new task
Documentation group:
Document the new task and settings for admins