Skip to main content
Lyrics provide text content for tracks, supporting both plain text and time-synchronized formats. You can add multiple language versions for international audiences.

Overview

The lyrics management system enables you to:

Add lyrics

Attach lyrics to any track in your catalog

Time synchronization

Create karaoke-style synchronized lyrics

Multi-language

Support multiple language versions per track

JSONB storage

Flexible structure for lyrics data

Lyrics entity structure

The Lyrics entity contains the following fields:
@Entity()
export class Lyrics extends AbstractEntity {
  // CONTENT
  @Column({ type: 'jsonb' })
  content: { time?: string; text: string }[];

  // TRACK ID
  @Column({ name: 'track_id', nullable: false, type: 'uuid' })
  trackId!: UUID;

  // TRACK
  @ManyToOne(() => Track, (track) => track.lyrics)
  @JoinColumn({ name: 'track_id' })
  track!: Track;

  // LANGUAGE
  @Column({
    name: 'language',
    nullable: false,
    type: 'varchar',
    default: 'en',
  })
  language!: string;
}

Core fields

FieldTypeRequiredDescription
idUUIDYesUnique identifier (inherited from AbstractEntity)
contentJSONB arrayYesArray of lyrics lines with optional timestamps
trackIdUUIDYesReference to the parent track
languagestringYesISO language code (default: en)
createdAttimestampYesRecord creation time (inherited)
updatedAttimestampYesLast update time (inherited)

Content structure

Lyrics are stored as JSONB arrays, where each element represents a line:
For non-synchronized lyrics, omit the time field:
[
  { "text": "Verse one, line one" },
  { "text": "Verse one, line two" },
  { "text": "" },
  { "text": "Chorus starts here" }
]
The time field is optional, allowing you to mix synchronized and non-synchronized content or gradually add timestamps to existing lyrics.

Relationships

Each lyrics record belongs to exactly one track. A track can have multiple lyrics records (for different languages).
@ManyToOne(() => Track, (track) => track.lyrics)
@JoinColumn({ name: 'track_id' })
track!: Track;

Common operations

Create lyrics

Add lyrics to a track:
POST /lyrics

{
  "trackId": "550e8400-e29b-41d4-a716-446655440000",
  "language": "en",
  "content": [
    { "time": "00:00.00", "text": "[Intro]" },
    { "time": "00:10.50", "text": "Walking down the street" },
    { "time": "00:15.30", "text": "With a rhythm in my feet" },
    { "text": "" },
    { "time": "00:30.00", "text": "Music in the air tonight" }
  ]
}
Response:
{
  "message": "Lyrics created successfully",
  "data": {
    "id": "...",
    "trackId": "550e8400-e29b-41d4-a716-446655440000",
    "language": "en",
    "content": [ /* content array */ ],
    "createdAt": "2024-01-15T10:30:00Z",
    "updatedAt": "2024-01-15T10:30:00Z"
  }
}

Fetch lyrics

Retrieve lyrics with optional filtering:
GET /lyrics?page=0&size=10&trackId=<uuid>
Query parameters:
  • page - Page number (default: 0)
  • size - Items per page (default: 10)
  • trackId - Filter by track (optional)
Get paginated list of all lyrics:
GET /lyrics?page=0&size=20

Get lyrics by ID

Retrieve a specific lyrics record:
GET /lyrics/:id
Response:
{
  "message": "Lyrics fetched successfully",
  "data": {
    "id": "...",
    "trackId": "...",
    "language": "en",
    "content": [
      { "time": "00:10.50", "text": "Walking down the street" },
      { "time": "00:15.30", "text": "With a rhythm in my feet" }
    ]
  }
}

Language support

Lyrics support multiple languages using ISO 639-1 language codes:

English

en (default)

Spanish

es

French

fr

German

de

Japanese

ja

Korean

ko

Adding multiple language versions

You can add lyrics in different languages for the same track:
// English version
POST /lyrics
{
  "trackId": "track-uuid",
  "language": "en",
  "content": [
    { "text": "Hello world" }
  ]
}

// Spanish version
POST /lyrics
{
  "trackId": "track-uuid",
  "language": "es",
  "content": [
    { "text": "Hola mundo" }
  ]
}

Time format

For synchronized lyrics, use the format MM:SS.ss or HH:MM:SS.ss:
[
  { "time": "00:15.50", "text": "First line" },
  { "time": "00:20.30", "text": "Second line" },
  { "time": "01:05.00", "text": "One minute later" }
]
Timestamps are stored as strings, giving you flexibility in format. Choose a format and be consistent within each lyrics record.

Use cases

Simple song lyrics

Add plain text lyrics without timestamps:
const lyrics = await fetch('/lyrics', {
  method: 'POST',
  body: JSON.stringify({
    trackId: trackId,
    language: 'en',
    content: [
      { text: 'Verse 1:' },
      { text: 'Walking through the night' },
      { text: 'Everything feels right' },
      { text: '' },
      { text: 'Chorus:' },
      { text: 'Dancing in the moonlight' },
      { text: 'Everything is alright' }
    ]
  })
});

Karaoke-style lyrics

Create synchronized lyrics for karaoke display:
const syncedLyrics = await fetch('/lyrics', {
  method: 'POST',
  body: JSON.stringify({
    trackId: trackId,
    language: 'en',
    content: [
      { time: '00:00.00', text: '[Intro]' },
      { time: '00:10.00', text: 'Let me sing you a song' },
      { time: '00:15.50', text: 'That will make you dance along' },
      { time: '00:20.30', text: 'To the rhythm and the beat' },
      { time: '00:25.00', text: 'Move your body and your feet' }
    ]
  })
});

Multi-language track

Add lyrics in multiple languages:
// Original English lyrics
await createLyrics({
  trackId: trackId,
  language: 'en',
  content: [{ text: 'Welcome to the show' }]
});

// Japanese translation
await createLyrics({
  trackId: trackId,
  language: 'ja',
  content: [{ text: 'ショーへようこそ' }]
});

// Spanish translation
await createLyrics({
  trackId: trackId,
  language: 'es',
  content: [{ text: 'Bienvenido al espectáculo' }]
});

Gradual timestamp addition

Start with plain text and add timestamps later:
// Initial creation without timestamps
POST /lyrics
{
  "trackId": "...",
  "content": [
    { "text": "Line one" },
    { "text": "Line two" },
    { "text": "Line three" }
  ]
}

// Later, update with timestamps
PATCH /lyrics/:id
{
  "content": [
    { "time": "00:10.00", "text": "Line one" },
    { "time": "00:15.00", "text": "Line two" },
    { "time": "00:20.00", "text": "Line three" }
  ]
}

JSONB advantages

Storing lyrics as JSONB provides several benefits:

Flexible schema

Add custom fields without schema changes (e.g., speaker, harmony, style)

Efficient queries

Query and index JSON content directly in PostgreSQL

Array operations

Manipulate lyrics lines using array operations

No parsing overhead

Store and retrieve structured data without serialization

Extended content structure

You can extend the content structure with additional fields:
[
  {
    "time": "00:10.00",
    "text": "Main vocal line",
    "harmony": "Background vocals",
    "speaker": "Lead Singer"
  },
  {
    "time": "00:15.00",
    "text": "Next line",
    "style": "whispered"
  }
]
While the entity defines the basic structure with time and text, the JSONB column accepts any valid JSON structure.

Releases

Organize tracks into releases

Artists

Credit songwriters and performers

Build docs developers (and LLMs) love