feat(lyrics): Tidal as lyrics source#6730
Conversation
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #6730 +/- ##
==========================================
+ Coverage 74.71% 74.86% +0.15%
==========================================
Files 162 162
Lines 20831 20969 +138
Branches 3298 3327 +29
==========================================
+ Hits 15563 15699 +136
- Misses 4508 4509 +1
- Partials 760 761 +1
🚀 New features to boost your workflow:
|
3d016e6 to
0b59185
Compare
semohr
left a comment
There was a problem hiding this comment.
Thanks! It's great to see more activity around the TIDAL plugin. I already did a quick scan of the changes, but I'll do a more thorough review next week.
I think we should keep the requested capabilities (scope) out of the user configuration. While I understand it's convenient to make this configurable during development, I can foresee quite a few user errors if it's exposed.
I see two possible approaches:
- Run the TIDAL plugin with
search.read user.readby default. - Run with only
search.readby default, and automatically requestuser.readwhen thetidal lyricsplugin is enabled.
I'd vote for the first option. It keeps the configuration simpler and avoids potential issues for users. The user.read scope will be needed at some point anyways if we want e.g. to allow users to use their own music.
@snejus Any strong opinion here?
06de8f8 to
81bb966
Compare
81bb966 to
a96a565
Compare
| return self.scope_set(token.get("scope") or token.get("scopes")) | ||
|
|
||
| @cached_property | ||
| def token_has_required_scopes(self) -> bool: |
There was a problem hiding this comment.
Lets put the scope safeguards into the general tidal session. Checking that the token includes the correct scope can be done once on token loading. Inside the load_token function should work. This should allow users to update their tokens.
I get where you come from as technically we don't need the user.read scope for the non lyrics functions (yet). Most of the logic here looks a bit overengineered to me tho, let's keep it simple. Will spare us some maintenance down the line.
| self, track_ids: list[str] | ||
| ) -> Iterable[tuple[str, TidalLyrics]]: | ||
| """Fetch lyric resources for TIDAL track IDs.""" | ||
| tracks_data = self.api.get_tracks( |
There was a problem hiding this comment.
Lets rename this for consistency.
| tracks_data = self.api.get_tracks( | |
| tracks_doc = self.api.get_tracks( |
|
|
||
| return None | ||
|
|
||
| def fetch_tracks_lyrics( |
There was a problem hiding this comment.
Can we change the signature to -> Iterable[TrackInfo | None]: and move the warnings out of this function. Have a look at TidalApi.search_tracks_by_ids I used that approach to keep the order of returned objs to align with the input ids.
| country_code=self.country_code, | ||
| ) | ||
|
|
||
| search_result = search_data.get("data") |
There was a problem hiding this comment.
Do we need to be defensive here? We are basically doing the same in the tidal plugin without being defensive. Am just wondering.
Same for relationships = search_result.get("relationships")
I'm now also wondering which request does need the |
|
I had initially verified lyrics retrieval via a developer app ID, but once I saw there was a public API that purported to retrieve lyrics I cut over to it. That may have been premature though, as I now can't actually get their public API to return lyrics as documented. I'm moving this back into draft while I figure out if the only path to lyrics is via developer app credentials. Sorry for the false start. |
|
The I'm really sad it can't land in the project and want to apologize again for the hassle. If I see something change in the API I'll pick it back up; I was also hoping to add Tidal as an art source. |
No worries :/ The tidal public api can be a bit difficult to use and sometimes even buggy. If you want I can also have a look at it, maybe we can make it work. You have a curl request that should work? |
|
Seems like a known issue https://github.com/orgs/tidal-music/discussions/279 |

Description
tidalas an opt-in lyrics source with plain/synced lyric selection, token scope validation, defensive JSON:API response handling, and match-debug parity with search backends.user.readauthentication requirements, and scope formats.Testing
poetry run ruff format --check --diff beetsplug/tidal/__init__.py beetsplug/tidal/api.py beetsplug/tidal/api_types.py beetsplug/tidal/session.py test/plugins/test_tidal.pypoetry run ruff check beetsplug/tidal/__init__.py beetsplug/tidal/api.py beetsplug/tidal/api_types.py beetsplug/tidal/session.py test/plugins/test_tidal.pypoetry run pytest test/plugins/test_tidal.pypoetry run ruff format --check --diff beetsplug/lyrics.py test/plugins/test_lyrics.pypoetry run ruff check beetsplug/lyrics.py test/plugins/test_lyrics.pypoetry run pytest test/plugins/test_lyrics.pypoetry run docstrfmt --preserve-adornments --check docs/plugins/lyrics.rst docs/plugins/tidal.rst docs/changelog.rstpoetry run sphinx-lint --enable all --disable default-role docs/plugins/lyrics.rst docs/plugins/tidal.rst docs/changelog.rstenv -u NO_COLOR poetry run pytestTo Do
docs/to describe it.)docs/changelog.rstto the bottom of one of the lists near the top of the document.)