Catalog Metadata

The supporting metadata that classifies and describes the catalog: genres, moods, labels, contributor roles, image variants and the GeoNames-backed places dataset. These models are shared across Albums, Artists and Tracks.

1 Genre

A musical genre. Genres form a two-level hierarchy via the ParentSlug self-link, and attach to both Tracks (track_genres) and Artists (artist_genres) as many-to-many relations.

apps/api/src/Features/Genres/Models/Genre.cs
public class Genre : BaseModel
{
    public required string Slug { get; set; }
    public string? ParentSlug { get; set; }
    public bool IsActive { get; set; } = true;
    public int DisplayOrder { get; set; }
}
FieldTypeDescription
Id Guid Primary key. Inherited from BaseModel.
Slug string · required · ≤60 Stable, unique machine identifier (e.g. lo-fi-hip-hop). The human-readable name is resolved per-locale via i18n — genres do not store a display name.
ParentSlug string? · ≤60 Slug of the parent genre, or null for a top-level genre. Indexed. Builds the genre tree.
IsActive bool · default true Whether the genre is selectable. Indexed; lets a genre be retired without deleting history.
DisplayOrder int Sort order in pickers and listings. Indexed.

Table: genres. Slug is uniquely indexed.

2 Mood

A curated, editorial mood tag — distinct from the continuous mood scores computed by the audio enricher (see TrackAudioFeatures). Moods attach to Tracks (track_moods) and Albums (album_moods).

apps/api/src/Features/Moods/Models/Mood.cs
public class Mood : BaseModel
{
    public required string Slug { get; set; }
    public bool IsActive { get; set; } = true;
    public int DisplayOrder { get; set; }
}
FieldTypeDescription
Id Guid Primary key. Inherited from BaseModel.
Slug string · required · ≤60 Unique machine identifier (e.g. chill, energetic). Display name resolved via i18n.
IsActive bool · default true Whether the mood is selectable. Indexed.
DisplayOrder int Sort order in pickers and listings. Indexed.

Table: moods. Slug is uniquely indexed. Unlike Genre, Mood has no hierarchy.

3 Label

A record label. An Album may reference a Label via Album.LabelId.

apps/api/src/Features/Labels/Models/Label.cs
FieldTypeDescription
Id Guid Primary key. Inherited from BaseModel.
Name string · required · ≤160 The label's canonical name.
DisplayName string? · ≤160 Optional alternative name for display.
Type LabelType enum Kind of label. Default SelfReleased. Indexed.
OwnerUserId / Owner Guid? + User? The user who owns the label. OnDelete = SetNull. Indexed.
Country string? · ≤2 ISO 3166-1 alpha-2 country code.
WebsiteUrl string? · ≤500 The label's website.
LogoUrl string? · ≤500 URL of the label's logo.
IsSystemDefault bool Marks the single system-default label (used for self-released albums with no explicit label). A unique filtered index allows only one row with this set to true.

LabelType

ValueIntMeaning
SelfReleased0The artist released it themselves. Default.
Indie1An independent label.
Major2A major label.
Virtual3A virtual / AI-native label.

4 ContributorRole

The shared role catalog used by both TrackContributor and AlbumContributor. Values are grouped by numeric range so a category can be queried arithmetically — Role / 100 yields the category number (e.g. 500 / 100 == 5 → all production roles).

apps/api/src/Features/Tracks/Enums/ContributorRole.cs

Performance · 300+

ValueIntValueInt
MainArtist300Instrumentalist305
FeaturedArtist301Soloist306
Vocalist302GroupMember307
LeadVocalist303Conductor308
BackgroundVocalist304Orchestra309

Writing · 400+

ValueIntValueInt
Composer400Arranger403
Lyricist401Adapter404
ComposerLyricist402

Production · 500+

ValueIntValueInt
Producer500VocalProducer503
ExecutiveProducer501Remixer504
StudioProducer502

Engineering · 600+

ValueIntValueInt
RecordingEngineer600AudioEngineer603
MixingEngineer601ProgrammingEngineer604
MasteringEngineer602

Business · 700+

ValueIntMeaning
Publisher700The publishing entity.
AandR702Artists & Repertoire.

5 Images

Album and Artist artwork is stored as a set of pre-resized variants. Both AlbumImage and ArtistImage implement the shared IImageVariantRecord interface, so the same rendering and source-set logic works for either.

apps/api/src/Shared/Models/IImageVariantRecord.cs
public interface IImageVariantRecord
{
    ImageVariant Variant { get; }
    ImageSize Size { get; }
    string ObjectKey { get; }
    bool IsPrimary { get; }
}

ImageVariant

The crop/shape of an image variant.

ValueIntMeaning
Square01:1 crop — covers, avatars.
Banner1Wide banner crop — page headers.
Hero2Large hero crop — feature sections.
Portrait3Tall portrait crop.

ImageSize

The resolution tier of an image variant.

ValueIntMeaning
Thumb0Smallest — list thumbnails.
Small1Small cards.
Medium2Default detail size.
Large3Large hero/detail rendering.
Original4The original uploaded resolution.
One primary per group

Both image tables carry a unique filtered index (ix_album_images_primary_unique / ix_artist_images_primary_unique) that allows at most one row with IsPrimary = true per (parentId, Variant, Size). See docs/image-format-strategy.md for the generation and format strategy.

6 Places & Geography

The places dataset provides structured geography for artist origin (Artist.OriginStateId / OriginCityId). It is backed by GeoNames: the primary keys are GeoNames geonameId values, stable across dataset refreshes. These three models do not inherit BaseModel — they use a long primary key.

PlaceState

A state / province / region — backed by GeoNames admin1 entries (e.g. BR.27 = São Paulo, US.CA = California).

apps/api/src/Features/Places/Models/PlaceState.cs
FieldTypeDescription
IdlongPrimary key — the GeoNames geonameId.
CountryCodestring · requiredISO 3166-1 alpha-2 country code.
ExternalCodestring · requiredGeoNames admin1 code (e.g. "27", "CA").
Namestring · requiredThe state name.
AsciiNamestring · requiredASCII-only variant of Name, used for fuzzy search.
CitiesICollection<PlaceCity>The cities within the state.

PlaceCity

A city — backed by GeoNames cities1000 entries (population ≥ 1000).

apps/api/src/Features/Places/Models/PlaceCity.cs
FieldTypeDescription
IdlongPrimary key — the GeoNames geonameId.
CountryCodestring · requiredISO 3166-1 alpha-2 country code.
StateId / Statelong? + navFK to the parent PlaceState. Null when GeoNames has no admin1 mapping.
Namestring · requiredThe city name.
AsciiNamestring · requiredASCII-only variant of Name, used for fuzzy search.
PopulationlongCity population (from GeoNames).
Latitude / Longitudedecimal?Geographic coordinates.
Timezonestring?IANA timezone identifier.

PlaceTranslation

A localized name for a place (state or city). Because states and cities share the same GeoNames geonameId space, PlaceId references either places_states.id or places_cities.id — there is no hard FK.

apps/api/src/Features/Places/Models/PlaceTranslation.cs
FieldTypeDescription
PlaceIdlongThe geonameId of the state or city being translated.
Localestring · requiredOne of the 5 supported locales: en-US, pt-BR, pt-PT, es-ES, es-419.
Namestring · requiredThe localized place name.
IsPreferredboolMarks the preferred translation for the locale when several exist.