getLyricsBySongId

Add support for synchronized lyrics, multiple languages, and retrieval by song ID.

OpenSubsonic version: 1

OpenSubsonic extension name songLyrics (As returned by getOpenSubsonicExtensions)

Retrieves all structured lyrics from the server for a given song. The lyrics can come from embedded tags (SYLT/USLT), LRC file/text file, or any other external source.

http://your-server/rest/getLyricsBySongId

Parameters

ParameterReq.OpenS.DefaultComment
idYesYesThe track ID.
enhancedNoYesfalseWhen true, the response includes cueLine arrays, cue byteStart / byteEnd offsets into cueLine.value, and non-main kind tracks (translations, pronunciations). When false or omitted, only kind="main" entries are returned with no cueLine data. Added in songLyrics version 2.

Example

Result

A subsonic-response element with a nested lyricsList

Version 1 (default)

{
  "subsonic-response": {
    "status": "ok",
    "version": "1.16.1",
    "type": "AwesomeServerName",
    "serverVersion": "0.1.3 (tag)",
    "openSubsonic": true,
    "lyricsList": {
      "structuredLyrics": [
        {
          "displayArtist": "Muse",
          "displayTitle": "Hysteria",
          "lang": "eng",
          "offset": -100,
          "synced": true,
          "line": [
            {
              "start": 0,
              "value": "It's bugging me"
            },
            {
              "start": 2000,
              "value": "Grating me"
            },
            {
              "start": 3001,
              "value": "And twisting me around..."
            }
          ]
        },
        {
          "displayArtist": "Muse",
          "displayTitle": "Hysteria",
          "lang": "und",
          "offset": 100,
          "synced": false,
          "line": [
            {
              "value": "It's bugging me"
            },
            {
              "value": "Grating me"
            },
            {
              "value": "And twisting me around..."
            }
          ]
        }
      ]
    }
  }
}
<subsonic-response status="ok" version="1.16.1" type="AwesomeServerName" serverVersion="0.1.3 (tag)" openSubsonic="true">
  <lyricsList>
    <structuredLyrics displayArtist="Muse" displayTitle="Hysteria" lang="en" offset="-100" synced="true">
      <line start="0">It's bugging me</line>
      <line start="2000">Grating me</line>
      <line start="3001">And twisting me around...</line>
    </structuredLyrics>
    <structuredLyrics displayArtist="Muse" displayTitle="Hysteria" lang="en" offset="100" synced="false">
      <line>It's bugging me</line>
      <line>Grating me</line>
      <line>And twisting me around...</line>
    </structuredLyrics>
  </lyricsList>
</subsonic-response>
Does not exist.

Version 2 (enhanced=true)

When enhanced=true is passed, the response includes kind to classify lyric tracks, cueLine arrays with word/syllable-level timing, cue byteStart / byteEnd offsets into cueLine.value, optional per-entry agents metadata for agent attribution, and additional tracks such as translations and pronunciations.

{
  "subsonic-response": {
    "status": "ok",
    "version": "1.16.1",
    "type": "AwesomeServerName",
    "serverVersion": "0.1.3 (tag)",
    "openSubsonic": true,
    "lyricsList": {
      "structuredLyrics": [
        {
          "kind": "main",
          "lang": "ko",
          "synced": true,
          "line": [
            { "start": 2747, "value": "눈을 뜬 순간" },
            { "start": 6214, "value": "모든 게 달라졌어" }
          ],
          "cueLine": [
            {
              "index": 0,
              "start": 2747,
              "end": 6214,
              "value": "눈을 뜬 순간",
              "cue": [
                { "start": 2747, "end": 3018, "value": "눈", "byteStart": 0, "byteEnd": 2 },
                { "start": 3018, "end": 3179, "value": "을", "byteStart": 3, "byteEnd": 5 },
                { "start": 3179, "end": 3582, "value": " ", "byteStart": 6, "byteEnd": 6 },
                { "start": 3582, "end": 4100, "value": "뜬", "byteStart": 7, "byteEnd": 9 },
                { "start": 4100, "end": 4500, "value": " ", "byteStart": 10, "byteEnd": 10 },
                { "start": 4500, "end": 5200, "value": "순", "byteStart": 11, "byteEnd": 13 },
                { "start": 5200, "end": 6214, "value": "간", "byteStart": 14, "byteEnd": 16 }
              ]
            },
            {
              "index": 1,
              "start": 6214,
              "end": 9000,
              "value": "모든 게 달라졌어",
              "cue": [
                { "start": 6214, "end": 6800, "value": "모", "byteStart": 0, "byteEnd": 2 },
                { "start": 6800, "end": 7200, "value": "든", "byteStart": 3, "byteEnd": 5 },
                { "start": 7200, "end": 7600, "value": " ", "byteStart": 6, "byteEnd": 6 },
                { "start": 7600, "end": 8000, "value": "게", "byteStart": 7, "byteEnd": 9 },
                { "start": 8000, "end": 8400, "value": " ", "byteStart": 10, "byteEnd": 10 },
                { "start": 8400, "end": 9000, "value": "달라졌어", "byteStart": 11, "byteEnd": 22 }
              ]
            }
          ]
        },
        {
          "kind": "translation",
          "lang": "eng",
          "synced": true,
          "line": [
            { "start": 2747, "value": "The moment I opened my eyes" },
            { "start": 6214, "value": "Everything had changed" }
          ]
        },
        {
          "kind": "pronunciation",
          "lang": "ko-Latn",
          "synced": true,
          "line": [
            { "start": 2747, "value": "nuneul tteun sungan" },
            { "start": 6214, "value": "modeun ge dallajyeosseo" }
          ],
          "cueLine": [
            {
              "index": 0,
              "start": 2747,
              "end": 6214,
              "value": "nuneul tteun sungan",
              "cue": [
                { "start": 2747, "end": 3179, "value": "nuneul", "byteStart": 0, "byteEnd": 5 },
                { "start": 3582, "end": 4100, "value": "tteun", "byteStart": 7, "byteEnd": 11 },
                { "start": 4500, "end": 6214, "value": "sungan", "byteStart": 13, "byteEnd": 18 }
              ]
            },
            {
              "index": 1,
              "start": 6214,
              "end": 9000,
              "value": "modeun ge dallajyeosseo",
              "cue": [
                { "start": 6214, "end": 7200, "value": "modeun", "byteStart": 0, "byteEnd": 5 },
                { "start": 7600, "end": 8000, "value": "ge", "byteStart": 7, "byteEnd": 8 },
                { "start": 8400, "end": 9000, "value": "dallajyeosseo", "byteStart": 10, "byteEnd": 22 }
              ]
            }
          ]
        }
      ]
    }
  }
}
<subsonic-response status="ok" version="1.16.1" type="AwesomeServerName" serverVersion="0.1.3 (tag)" openSubsonic="true">
  <lyricsList>
    <structuredLyrics kind="main" lang="ko" synced="true">
      <line start="2747">눈을 뜬 순간</line>
      <line start="6214">모든 게 달라졌어</line>
      <cueLine index="0" start="2747" end="6214" value="눈을 뜬 순간">
        <cue start="2747" end="3018" byteStart="0" byteEnd="2"></cue>
        <cue start="3018" end="3179" byteStart="3" byteEnd="5"></cue>
        <cue start="3179" end="3582" byteStart="6" byteEnd="6"> </cue>
        <cue start="3582" end="4100" byteStart="7" byteEnd="9"></cue>
        <cue start="4100" end="4500" byteStart="10" byteEnd="10"> </cue>
        <cue start="4500" end="5200" byteStart="11" byteEnd="13"></cue>
        <cue start="5200" end="6214" byteStart="14" byteEnd="16"></cue>
      </cueLine>
      <cueLine index="1" start="6214" end="9000" value="모든 게 달라졌어">
        <cue start="6214" end="6800" byteStart="0" byteEnd="2"></cue>
        <cue start="6800" end="7200" byteStart="3" byteEnd="5"></cue>
        <cue start="7200" end="7600" byteStart="6" byteEnd="6"> </cue>
        <cue start="7600" end="8000" byteStart="7" byteEnd="9"></cue>
        <cue start="8000" end="8400" byteStart="10" byteEnd="10"> </cue>
        <cue start="8400" end="9000" byteStart="11" byteEnd="22">달라졌어</cue>
      </cueLine>
    </structuredLyrics>
    <structuredLyrics kind="translation" lang="eng" synced="true">
      <line start="2747">The moment I opened my eyes</line>
      <line start="6214">Everything had changed</line>
    </structuredLyrics>
    <structuredLyrics kind="pronunciation" lang="ko-Latn" synced="true">
      <line start="2747">nuneul tteun sungan</line>
      <line start="6214">modeun ge dallajyeosseo</line>
      <cueLine index="0" start="2747" end="6214" value="nuneul tteun sungan">
        <cue start="2747" end="3179" byteStart="0" byteEnd="5">nuneul</cue>
        <cue start="3582" end="4100" byteStart="7" byteEnd="11">tteun</cue>
        <cue start="4500" end="6214" byteStart="13" byteEnd="18">sungan</cue>
      </cueLine>
      <cueLine index="1" start="6214" end="9000" value="modeun ge dallajyeosseo">
        <cue start="6214" end="7200" byteStart="0" byteEnd="5">modeun</cue>
        <cue start="7600" end="8000" byteStart="7" byteEnd="8">ge</cue>
        <cue start="8400" end="9000" byteStart="10" byteEnd="22">dallajyeosseo</cue>
      </cueLine>
    </structuredLyrics>
  </lyricsList>
</subsonic-response>
Does not exist.
Example with background vocals (agents + agentId)

When a source distinguishes both a lead/default vocal layer and background vocals within the same lyric line, the server emits a shared agents array on that structuredLyrics entry and splits the lyric into separate cueLines with the same index. Each cueLine references one agent via agentId, contains that agent/layer’s renderable text in value, and the cueLine whose referenced agent has role: "main" comes first. The parent line remains the combined fallback line:

{
  "line": [
    { "start": 1000, "value": "Hello echo" }
  ],
  "agents": [
    { "id": "lead", "role": "main", "name": "Lead Vocal" },
    { "id": "backing", "role": "bg" }
  ],
  "cueLine": [
    {
      "index": 0,
      "agentId": "lead",
      "start": 1000,
      "end": 3000,
      "value": "Hello",
      "cue": [
        { "start": 1000, "end": 1400, "value": "He", "byteStart": 0, "byteEnd": 1 },
        { "start": 1400, "end": 1800, "value": "llo", "byteStart": 2, "byteEnd": 4 }
      ]
    },
    {
      "index": 0,
      "agentId": "backing",
      "start": 1000,
      "end": 3000,
      "value": "echo",
      "cue": [
        { "start": 2000, "end": 2500, "value": "echo", "byteStart": 0, "byteEnd": 3 }
      ]
    }
  ]
}
<line start="1000">Hello echo</line>
<agent id="lead" role="main" name="Lead Vocal" />
<agent id="backing" role="bg" />
<cueLine index="0" agentId="lead" start="1000" end="3000" value="Hello">
  <cue start="1000" end="1400" byteStart="0" byteEnd="1">He</cue>
  <cue start="1400" end="1800" byteStart="2" byteEnd="4">llo</cue>
</cueLine>
<cueLine index="0" agentId="backing" start="1000" end="3000" value="echo">
  <cue start="2000" end="2500" byteStart="0" byteEnd="3">echo</cue>
</cueLine>
Does not exist.
Example with multiple agents (TTML-style attribution)

When a source has multiple named singers (e.g. a duet from TTML with ttm:agent and ttm:name), the server stores those identities once in agents and each cueLine references the relevant singer or group via agentId:

{
  "agents": [
    { "id": "lead", "role": "main", "name": "Chris Martin" },
    { "id": "guest", "role": "voice", "name": "Jin" },
    { "id": "choir", "role": "group", "name": "All" }
  ],
  "cueLine": [
    {
      "index": 0,
      "agentId": "lead",
      "start": 1000,
      "end": 4000,
      "value": "You and I",
      "cue": [
        { "start": 1000, "end": 1800, "value": "You ", "byteStart": 0, "byteEnd": 3 },
        { "start": 1800, "end": 2400, "value": "and ", "byteStart": 4, "byteEnd": 7 },
        { "start": 2400, "end": 3200, "value": "I", "byteStart": 8, "byteEnd": 8 }
      ]
    },
    {
      "index": 1,
      "agentId": "guest",
      "start": 4000,
      "end": 7000,
      "value": "Under this sky",
      "cue": [
        { "start": 4000, "end": 4800, "value": "Un", "byteStart": 0, "byteEnd": 1 },
        { "start": 4800, "end": 5400, "value": "der ", "byteStart": 2, "byteEnd": 5 },
        { "start": 5400, "end": 5900, "value": "this ", "byteStart": 6, "byteEnd": 10 },
        { "start": 5900, "end": 7000, "value": "sky", "byteStart": 11, "byteEnd": 13 }
      ]
    },
    {
      "index": 2,
      "agentId": "choir",
      "start": 7000,
      "end": 10000,
      "value": "Together tonight",
      "cue": [
        { "start": 7000, "end": 8000, "value": "To", "byteStart": 0, "byteEnd": 1 },
        { "start": 8000, "end": 8800, "value": "ge", "byteStart": 2, "byteEnd": 3 },
        { "start": 8800, "end": 9200, "value": "ther ", "byteStart": 4, "byteEnd": 8 },
        { "start": 9200, "end": 10000, "value": "tonight", "byteStart": 9, "byteEnd": 15 }
      ]
    }
  ]
}
<agent id="lead" role="main" name="Chris Martin" />
<agent id="guest" role="voice" name="Jin" />
<agent id="choir" role="group" name="All" />
<cueLine index="0" agentId="lead" start="1000" end="4000" value="You and I">
  <cue start="1000" end="1800" byteStart="0" byteEnd="3">You </cue>
  <cue start="1800" end="2400" byteStart="4" byteEnd="7">and </cue>
  <cue start="2400" end="3200" byteStart="8" byteEnd="8">I</cue>
</cueLine>
<cueLine index="1" agentId="guest" start="4000" end="7000" value="Under this sky">
  <cue start="4000" end="4800" byteStart="0" byteEnd="1">Un</cue>
  <cue start="4800" end="5400" byteStart="2" byteEnd="5">der </cue>
  <cue start="5400" end="5900" byteStart="6" byteEnd="10">this </cue>
  <cue start="5900" end="7000" byteStart="11" byteEnd="13">sky</cue>
</cueLine>
<cueLine index="2" agentId="choir" start="7000" end="10000" value="Together tonight">
  <cue start="7000" end="8000" byteStart="0" byteEnd="1">To</cue>
  <cue start="8000" end="8800" byteStart="2" byteEnd="3">ge</cue>
  <cue start="8800" end="9200" byteStart="4" byteEnd="8">ther </cue>
  <cue start="9200" end="10000" byteStart="9" byteEnd="15">tonight</cue>
</cueLine>
Does not exist.
Example with ambiguous repeated text

When cueLine.value contains untimed text between timed cues, byteStart / byteEnd identifies the exact substring each timed cue covers:

{
  "cueLine": [
    {
      "index": 0,
      "start": 0,
      "end": 2400,
      "value": "Oh love love me tonight",
      "cue": [
        { "start": 0, "end": 300, "value": "Oh", "byteStart": 0, "byteEnd": 1 },
        { "start": 900, "end": 1300, "value": "love", "byteStart": 8, "byteEnd": 11 },
        { "start": 1300, "end": 1600, "value": "me", "byteStart": 13, "byteEnd": 14 },
        { "start": 1600, "end": 2400, "value": "tonight", "byteStart": 16, "byteEnd": 22 }
      ]
    }
  ]
}
<cueLine index="0" start="0" end="2400" value="Oh love love me tonight">
  <cue start="0" end="300" byteStart="0" byteEnd="1">Oh</cue>
  <cue start="900" end="1300" byteStart="8" byteEnd="11">love</cue>
  <cue start="1300" end="1600" byteStart="13" byteEnd="14">me</cue>
  <cue start="1600" end="2400" byteStart="16" byteEnd="22">tonight</cue>
</cueLine>
Does not exist.

Response fields

FieldTypeReq.OpenS.Details
lyricsListlyricsListYesYesList of structured lyrics

Implementation notes