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 |

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.)