Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions gamdl/api/apple_music_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,14 @@ async def get_song(

return song

async def get_syllable_lyrics(self, song_id: str) -> dict | None:
syllable_lyrics = await self._amp_request(
f"/v1/catalog/{self.storefront}/songs/{song_id}/syllable-lyrics",
)
logger.debug(f"Syllable lyrics: {syllable_lyrics}")

return syllable_lyrics

async def get_music_video(
self,
music_video_id: str,
Expand Down
2 changes: 1 addition & 1 deletion gamdl/downloader/downloader_song.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ async def get_download_item(
webplayback = await self.interface.apple_music_api.get_webplayback(song_id)
download_item.media_tags = await self.interface.get_tags(
webplayback,
download_item.lyrics.unsynced if download_item.lyrics else None,
(download_item.lyrics.synced if download_item.lyrics and download_item.lyrics.synced else download_item.lyrics.unsynced) if download_item.lyrics else None,
self.use_album_date,
)
if self.fetch_extra_tags:
Expand Down
73 changes: 54 additions & 19 deletions gamdl/interface/interface_song.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import json
import logging
import re
from xml.dom import minidom
from xml.etree import ElementTree

import m3u8
Expand Down Expand Up @@ -54,31 +53,57 @@ async def get_lyrics(
)
)["data"][0]

if (
"lyrics" in song_metadata["relationships"]
and "data" in song_metadata["relationships"]["lyrics"]
and len(song_metadata["relationships"]["lyrics"]["data"]) > 0
and "attributes" in song_metadata["relationships"]["lyrics"]["data"][0]
and song_metadata["relationships"]["lyrics"]["data"][0]["attributes"].get(
"ttml"
lyrics_ttml = None
try:
lyrics_response = await self.apple_music_api.get_syllable_lyrics(
self.get_media_id_of_library_media(song_metadata)
)
is not None
):
lyrics = self._get_lyrics(
song_metadata["relationships"]["lyrics"]["data"][0]["attributes"][
"ttml"
],
synced_lyrics_format,
if (
lyrics_response
and "data" in lyrics_response
and len(lyrics_response["data"]) > 0
and "attributes" in lyrics_response["data"][0]
):
lyrics_ttml = lyrics_response["data"][0]["attributes"].get("ttml")
except Exception as exc: # preserve existing behavior if endpoint fails
logger.debug(
f"Failed to fetch syllable lyrics endpoint, falling back to metadata lyrics: {exc}"
)
logging.debug(f"Lyrics: {lyrics}")

return lyrics
if lyrics_ttml is None:
if (
"lyrics" in song_metadata["relationships"]
and "data" in song_metadata["relationships"]["lyrics"]
and len(song_metadata["relationships"]["lyrics"]["data"]) > 0
and "attributes" in song_metadata["relationships"]["lyrics"]["data"][0]
and song_metadata["relationships"]["lyrics"]["data"][0]["attributes"].get(
"ttml"
)
is not None
):
lyrics_ttml = song_metadata["relationships"]["lyrics"]["data"][0]["attributes"].get(
"ttml"
)

if lyrics_ttml is None:
return None

lyrics = self._get_lyrics(
lyrics_ttml,
synced_lyrics_format,
)
logging.debug(f"Lyrics: {lyrics}")

return lyrics

def _get_lyrics(
self,
lyrics_ttml: str,
synced_lyrics_format: SyncedLyricsFormat,
) -> Lyrics:
ElementTree.register_namespace("", "http://www.w3.org/ns/ttml")
ElementTree.register_namespace("itunes", "http://music.apple.com/lyric-ttml-internal")
ElementTree.register_namespace("ttm", "http://www.w3.org/ns/ttml#metadata")
lyrics_ttml_et = ElementTree.fromstring(lyrics_ttml)
unsynced_lyrics = []
synced_lyrics = []
Expand All @@ -101,9 +126,19 @@ def _get_lyrics(

if synced_lyrics_format == SyncedLyricsFormat.TTML:
if not synced_lyrics:
synced_lyrics.append(
minidom.parseString(lyrics_ttml).toprettyxml()
for elem in lyrics_ttml_et.iter():
if elem.tail:
current_text = elem.text or ""
elem.text = current_text + elem.tail
elem.tail = None

ElementTree.indent(lyrics_ttml_et, space=" ")
pretty_xml = ElementTree.tostring(
lyrics_ttml_et,
encoding="unicode",
)
synced_lyrics.append(pretty_xml)

continue

index += 1
Expand Down