Album
The release container. Every Track belongs to exactly one Album — a
single is just an Album with one track. The Album owns release
metadata, copyright, AI disclosure, label affiliation, cover artwork
and contributor credits.
apps/api/src/Features/Albums/Models/Album.cs
1 Full schema
Album inherits from BaseModel
(Id, CreatedAtUtc, UpdatedAtUtc)
and is mapped to the albums table.
public class Album : BaseModel
{
public required string Title { get; set; }
public AlbumStatus Status { get; set; } = AlbumStatus.Draft;
public DateTime? PublishedAtUtc { get; set; }
public ICollection<Artist> Artists { get; set; } = [];
public ICollection<Track> Tracks { get; set; } = [];
// Editorial content
public string? Description { get; set; }
public string? EditorialNotes { get; set; }
public string DefaultLocale { get; set; } = "en";
// Type / format
public ReleaseType Type { get; set; } = ReleaseType.Single;
// Dates
public DateTime ReleaseDate { get; set; }
public DateTimePrecision ReleaseDatePrecision { get; set; } = DateTimePrecision.Year;
public DateTime? OriginalReleaseDate { get; set; }
public bool IsReissue { get; set; }
// Denormalized
public int TotalTracks { get; set; }
// Copyright
public int? PLineYear { get; set; }
public string? PLineHolder { get; set; }
public int? CLineYear { get; set; }
public string? CLineHolder { get; set; }
// AI disclosure
public bool AiGenerated { get; set; } = true;
public AiDisclosureLevel AiDisclosure { get; set; } = AiDisclosureLevel.FullyAiGenerated;
public string? AiToolUsed { get; set; }
public string? HumanContributionDescription { get; set; }
// Label
public Guid? LabelId { get; set; }
public Label? Label { get; set; }
// Identifiers
public string? Upc { get; set; }
public string? CatalogNumber { get; set; }
// Cover meta
public Guid? PrimaryCoverImageId { get; set; }
public AlbumImage? PrimaryCoverImage { get; set; }
public string? DominantColor { get; set; }
public string? PaletteJson { get; set; }
// Type-specific optionals
public string? VenueName { get; set; }
public DateTime? RecordedAtUtc { get; set; }
public string? RelatedTitle { get; set; }
public Guid? OriginalReleaseId { get; set; }
public Album? OriginalRelease { get; set; }
// Additional navigations
public ICollection<AlbumImage> Images { get; set; } = [];
public ICollection<AlbumContributor> Contributors { get; set; } = [];
public ICollection<Mood> Moods { get; set; } = [];
}
2 Identity & status
| Field | Type | Description |
Id |
Guid |
Primary key. Inherited from BaseModel. |
Title |
string · required · ≤255 |
Album title. |
Status |
AlbumStatus enum |
Lifecycle state. Default Draft. See
§9.
|
PublishedAtUtc |
DateTime? |
When the album was published. null while a draft. |
TotalTracks |
int |
Denormalised track count, kept in sync with the
Tracks collection to avoid a join on listings.
|
3 Editorial
| Field | Type | Description |
Description |
string? · ≤500 |
Short public description shown on the album page. |
EditorialNotes |
string? · text |
Longer internal/editorial notes (no length cap). |
DefaultLocale |
string · ≤8 · default "en" |
Default locale for the album's textual metadata, used when a
localised variant is missing.
|
4 Type & dates
| Field | Type | Description |
Type |
ReleaseType enum |
Release format (Single, EP, Album, Compilation, …). Default
Single. Indexed. See §9.
|
ReleaseDate |
DateTime |
The release date on Beatix. Indexed. |
ReleaseDatePrecision |
DateTimePrecision enum |
How precise ReleaseDate is — Year, YearMonth or
Day. Lets the UI render "2026" vs "May 2026" vs a full date.
|
OriginalReleaseDate |
DateTime? |
The original release date for reissues/remasters, when it
differs from ReleaseDate.
|
IsReissue |
bool |
Flags the album as a reissue of an earlier release. |
5 Commercial & identifiers
Copyright fields here are the defaults for the
album's tracks. A Track may override any of them via its
*Override fields — see
Track · Commercial.
| Field | Type | Description |
PLineYear / PLineHolder |
int? / string? ≤255 |
Recording rights (℗) — year and holder. |
CLineYear / CLineHolder |
int? / string? ≤255 |
Composition rights (©) — year and holder. |
Upc |
string? · ≤13 |
Universal Product Code for the release. A unique, filtered
index enforces uniqueness when not null.
|
CatalogNumber |
string? · ≤80 |
The label's internal catalog number for the release. |
LabelId / Label |
Guid? + nav |
Optional FK to the releasing Label.
OnDelete = SetNull.
|
6 AI disclosure
Beatix is an AI-music platform, so AI provenance is first-class
metadata. By default a new album is treated as fully AI-generated.
| Field | Type | Description |
AiGenerated |
bool · default true |
Whether any part of the release was AI-generated. |
AiDisclosure |
AiDisclosureLevel enum |
The disclosure level — NotAiGenerated, AiAssisted,
FullyAiGenerated or AiCover. See §9.
|
AiToolUsed |
string? · ≤80 |
Name of the AI tool/model used (e.g. "Suno v4", "Udio"). |
HumanContributionDescription |
string? · text |
Free-text description of the human contribution (writing,
production, performance) for the AiAssisted case.
|
7 Cover & artwork
| Field | Type | Description |
PrimaryCoverImageId / PrimaryCoverImage |
Guid? + AlbumImage? |
FK to the chosen primary cover variant.
OnDelete = SetNull.
|
DominantColor |
string? · ≤7 |
Dominant cover colour as a hex string (e.g. #1a2342), used for theming. |
PaletteJson |
string? · jsonb |
Extracted colour palette stored as JSON, for gradient/UI accents. |
The actual image files and their resized variants live in
AlbumImage rows. The image generation and
format strategy is documented in
docs/image-format-strategy.md.
8 Type-specific fields
These optionals are only meaningful for certain
ReleaseType values. They are nullable and ignored
otherwise.
| Field | Type | Used by | Description |
VenueName |
string? ≤200 |
Live |
Where the live album was recorded. |
RecordedAtUtc |
DateTime? |
Live |
When the live recording took place. |
RelatedTitle |
string? ≤200 |
Soundtrack |
The film/show/game the soundtrack belongs to. |
OriginalReleaseId / OriginalRelease |
Guid? + self-nav |
Remix |
Self-reference to the album this one remixes.
OnDelete = SetNull.
|
9 Enums
AlbumStatus
| Value | Int | Meaning |
| Draft | 0 | Being created; not public. Default. |
| Published | 1 | Visible in the public catalog. |
| Archived | 2 | Removed from the catalog but preserved. |
| Deleted | 3 | Soft-deleted. |
ReleaseType
| Value | Int | Meaning |
Single | 0 | One or a few tracks. The default. |
EP | 1 | Extended play — more than a single, shorter than an album. |
Album | 2 | Full-length album. |
Compilation | 3 | Curated collection of pre-existing tracks. |
Soundtrack | 4 | Soundtrack — pairs with RelatedTitle. |
Live | 5 | Live release — pairs with VenueName / RecordedAtUtc. |
Mixtape | 6 | Mixtape. |
Remix | 7 | Remix release — pairs with OriginalReleaseId. |
Demo | 8 | Demo release. |
GeneratedPack | 100 | Beatix-specific: a batch-generated pack of AI tracks. |
AiSampler | 101 | Beatix-specific: an AI sampler release. |
DateTimePrecision
| Value | Int | Meaning |
Year | 0 | Only the year is known. The default. |
YearMonth | 1 | Year and month are known. |
Day | 2 | The full date is known. |
AiDisclosureLevel
| Value | Int | Meaning |
NotAiGenerated | 0 | Entirely human-made. |
AiAssisted | 1 | Human-led with AI assistance — describe in HumanContributionDescription. |
| FullyAiGenerated | 2 | Fully AI-generated. The default for new albums. |
AiCover | 3 | An AI-generated cover of an existing work. |
10 AlbumContributor
A rich association between an Album and an Artist, carrying a credit
role. This is album-level credits; track-level credits use the
parallel TrackContributor.
apps/api/src/Features/Albums/Models/AlbumContributor.cs
| Field | Type | Description |
AlbumId / Album |
Guid + nav |
Parent album. OnDelete = Cascade. |
ArtistId / Artist |
Guid + nav |
The credited artist. OnDelete = Restrict. |
Role |
ContributorRole enum |
The credited role. See the
ContributorRole reference.
|
RoleDetail |
string? · ≤120 |
Free-text refinement of the role. |
Instrument |
string? · ≤80 |
Instrument played, when relevant. |
CreditedAs |
string? · ≤255 |
The name to display in credits, if different from the artist's canonical name. |
IsPrimary |
bool |
Whether this is a primary credit (vs a secondary one). |
Order |
int |
Display order. Indexed together with AlbumId. |
11 AlbumImage
A single resized variant of an album's artwork. An album has one
AlbumImage row per (Variant,
Size) pair. AlbumImage implements the shared
IImageVariantRecord interface.
apps/api/src/Features/Albums/Models/AlbumImage.cs
| Field | Type | Description |
AlbumId / Album |
Guid + nav |
Parent album. OnDelete = Cascade. |
Variant |
ImageVariant enum |
Crop/shape — Square, Banner, Hero, Portrait. See Image variants. |
Size |
ImageSize enum |
Resolution tier — Thumb, Small, Medium, Large, Original. |
ObjectKey |
string · required · ≤500 |
S3 object key of the stored file. |
Width / Height |
int |
Pixel dimensions of this variant. |
SizeBytes |
long |
File size in bytes. |
ContentHash |
string · required · ≤64 |
Content hash for dedup/cache-busting. Indexed. |
IsPrimary |
bool |
Marks the primary image for its (Variant, Size). A unique
filtered index (ix_album_images_primary_unique)
allows only one primary per group.
|
12 Relationships
| Property |
Cardinality |
Join / FK |
OnDelete |
Artists |
Many-to-Many |
album_artists |
Cascade (join table) |
Tracks |
One-to-Many |
tracks.AlbumId |
Restrict |
Moods |
Many-to-Many |
album_moods |
Cascade (join table) |
Images |
One-to-Many |
album_images.AlbumId |
Cascade |
Contributors |
One-to-Many |
album_contributors.AlbumId |
Cascade |
Label |
Many-to-One (optional) |
albums.LabelId |
SetNull |
PrimaryCoverImage |
Many-to-One (optional) |
albums.PrimaryCoverImageId |
SetNull |
OriginalRelease |
Self-reference (optional) |
albums.OriginalReleaseId |
SetNull |
Restrict on Tracks
An album cannot be deleted while it still has tracks — the
fk_tracks_album FK uses Restrict. Delete
or move the tracks first. This protects against accidentally
wiping a release.