Skip to content

feat(lyrics): Tidal as lyrics source#6730

Open
treyturner wants to merge 4 commits into
beetbox:masterfrom
treyturner:feat/tidal_as_lyrics_source
Open

feat(lyrics): Tidal as lyrics source#6730
treyturner wants to merge 4 commits into
beetbox:masterfrom
treyturner:feat/tidal_as_lyrics_source

Conversation

@treyturner

@treyturner treyturner commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Description

  • Add TIDAL API support for fetching track lyric relationships, including typed lyric resources and configurable OAuth scopes.
  • Add tidal as 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.
  • Document TIDAL lyrics configuration, user.read authentication 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.py
  • poetry run ruff check beetsplug/tidal/__init__.py beetsplug/tidal/api.py beetsplug/tidal/api_types.py beetsplug/tidal/session.py test/plugins/test_tidal.py
  • poetry run pytest test/plugins/test_tidal.py
  • poetry run ruff format --check --diff beetsplug/lyrics.py test/plugins/test_lyrics.py
  • poetry run ruff check beetsplug/lyrics.py test/plugins/test_lyrics.py
  • poetry run pytest test/plugins/test_lyrics.py
  • poetry run docstrfmt --preserve-adornments --check docs/plugins/lyrics.rst docs/plugins/tidal.rst docs/changelog.rst
  • poetry run sphinx-lint --enable all --disable default-role docs/plugins/lyrics.rst docs/plugins/tidal.rst docs/changelog.rst
  • env -u NO_COLOR poetry run pytest

To Do

  • Documentation. (If you've added a new command-line flag, for example, find the appropriate page under docs/ to describe it.)
  • Changelog. (Add an entry to docs/changelog.rst to the bottom of one of the lists near the top of the document.)
  • Tests. (Very much encouraged but not strictly required.)

@github-actions github-actions Bot added the lyrics lyrics plugin label Jun 11, 2026
@codecov

codecov Bot commented Jun 11, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 97.93103% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 74.86%. Comparing base (ec729d3) to head (a96a565).
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
beetsplug/lyrics.py 98.56% 1 Missing and 1 partial ⚠️
beetsplug/tidal/api.py 75.00% 1 Missing ⚠️
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     
Files with missing lines Coverage Δ
beetsplug/tidal/__init__.py 85.40% <100.00%> (ø)
beetsplug/tidal/session.py 40.00% <100.00%> (ø)
beetsplug/tidal/api.py 34.11% <75.00%> (+1.58%) ⬆️
beetsplug/lyrics.py 89.59% <98.56%> (+2.30%) ⬆️
🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@treyturner treyturner force-pushed the feat/tidal_as_lyrics_source branch from 3d016e6 to 0b59185 Compare June 11, 2026 22:45
@treyturner treyturner marked this pull request as ready for review June 11, 2026 22:51
@treyturner treyturner requested review from a team and semohr as code owners June 11, 2026 22:51
@semohr semohr self-assigned this Jun 12, 2026

@semohr semohr left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.read by default.
  • Run with only search.read by default, and automatically request user.read when the tidal lyrics plugin 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?

Comment thread beetsplug/tidal/__init__.py Outdated
Comment thread docs/plugins/lyrics.rst Outdated
@semohr semohr added the tidal label Jun 12, 2026
@treyturner treyturner force-pushed the feat/tidal_as_lyrics_source branch 3 times, most recently from 06de8f8 to 81bb966 Compare June 12, 2026 13:23
@treyturner treyturner force-pushed the feat/tidal_as_lyrics_source branch from 81bb966 to a96a565 Compare June 12, 2026 16:28
Comment thread beetsplug/lyrics.py
return self.scope_set(token.get("scope") or token.get("scopes"))

@cached_property
def token_has_required_scopes(self) -> bool:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread beetsplug/tidal/api.py
Comment thread beetsplug/tidal/api.py
Comment thread beetsplug/lyrics.py
self, track_ids: list[str]
) -> Iterable[tuple[str, TidalLyrics]]:
"""Fetch lyric resources for TIDAL track IDs."""
tracks_data = self.api.get_tracks(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lets rename this for consistency.

Suggested change
tracks_data = self.api.get_tracks(
tracks_doc = self.api.get_tracks(

Comment thread beetsplug/lyrics.py

return None

def fetch_tracks_lyrics(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread docs/plugins/tidal.rst
Comment thread beetsplug/lyrics.py
country_code=self.country_code,
)

search_result = search_data.get("data")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

@semohr

semohr commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

FYI: I just enabled the user.read for our tidal app. Im now wondering how requesting the user.read scope worked in the first place 🤔
image

@semohr

semohr commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

Document TIDAL lyrics configuration, user.read authentication requirements, and scope formats.

I'm now also wondering which request does need the user.read scope in the first place? 😄

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

lyrics lyrics plugin tidal

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants