Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added Lyrics w. Timestamps #662

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
Open

Conversation

heinrich26
Copy link

Added get_lyrics_with_timestamps to get lyrics with timestamps. The Method doesn't try to parse the response as normal lyrics, if no lyrics with timestamps are returned. (could be changed to do so tho, the format is the same)

Added `get_lyrics_with_timestamps` to get lyrics with timestamps.
The Method doesn't try to parse the response as normal lyrics, if no lyrics with timestamps are returned. (could be changed to do so tho, the format is the same)
Copy link

codecov bot commented Oct 11, 2024

Codecov Report

Attention: Patch coverage is 97.61905% with 2 lines in your changes missing coverage. Please review.

Project coverage is 94.99%. Comparing base (2b5d19a) to head (a8a1f5f).
Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
ytmusicapi/mixins/browsing.py 92.85% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #662      +/-   ##
==========================================
- Coverage   94.99%   94.99%   -0.01%     
==========================================
  Files          38       40       +2     
  Lines        2278     2337      +59     
==========================================
+ Hits         2164     2220      +56     
- Misses        114      117       +3     
Flag Coverage Δ
unittests 94.99% <97.61%> (-0.01%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Copy link
Owner

@sigma67 sigma67 left a comment

Choose a reason for hiding this comment

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

Hi @heinrich26 , pretty cool that you were able to make this work!

I take it you're basically emulating the mobile client to fetch the timed data? Since you're modifying the client info.

Could we replace the previous get_lyrics with this? I wouldn't want to have two methods for the same purpose in the API if one provides a superset of information.

Finally, to get this merged:

  • please ensure the linters pass
  • please add a test for the function

@heinrich26
Copy link
Author

heinrich26 commented Oct 11, 2024 via email

Copy link
Owner

@sigma67 sigma67 left a comment

Choose a reason for hiding this comment

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

Thanks, looks much better! :)

As a general rule of thumb: Don't change more than necessary in a single PR. It makes it harder for the maintainer to review the changes (more time consuming), and harder to trace back potential future issues to specific PRs. It will also prolong the time it takes for the PR to get merged.

Note for this review: Please don't resolve threads, I will do so myself after verifying the changes

@@ -204,7 +206,7 @@ def search(
if filter and "playlists" in filter:
filter = "playlists"
elif scope == scopes[1]:
filter = scopes[1]
filter = scopes[1] # type:ignore
Copy link
Owner

Choose a reason for hiding this comment

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

please undo all changes to this file

ytmusicapi/mixins/uploads.py Outdated Show resolved Hide resolved
@@ -181,7 +181,7 @@ def __init__(
try:
cookie = self.base_headers.get("cookie")
self.sapisid = sapisid_from_cookie(cookie)
self.origin = self.base_headers.get("origin", self.base_headers.get("x-origin"))
self.origin = cast(str, self.base_headers.get("origin", self.base_headers.get("x-origin")))
Copy link
Owner

Choose a reason for hiding this comment

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

Please undo changes to this file

@@ -2,14 +2,16 @@

from ytmusicapi.setup import setup, setup_oauth
from ytmusicapi.ytmusic import YTMusic
from .mixins.browsing import Lyrics, TimedLyrics, LyricLine
Copy link
Owner

Choose a reason for hiding this comment

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

Please undo the changes to this file. They are not needed and I don't want to start exporting everything from root

Copy link
Author

Choose a reason for hiding this comment

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

I was thinking similar, but then you'll need to create a submodule for these classes to be accessible

Copy link
Owner

Choose a reason for hiding this comment

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

You can already access them: from ytmusicapi.mixins.browsing import TimedLyrics

It's similar for https://github.com/sigma67/ytmusicapi/blob/main/ytmusicapi/parsers/podcasts.py

Although with #621 in mind, it might be wise to move them to a ytmusicapi.models module (one for podcasts and one for lyrics, I guess)


from ytmusicapi.exceptions import YTMusicUserError


OrderType = Literal['a_to_z', 'z_to_a', 'recently_added']
Copy link
Owner

Choose a reason for hiding this comment

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

This is a good change in general, but the name is too broad. Since you introduced an ArtistOrderType elsewhere, this should have a more specific name as well. Maybe LibraryOrderType ?

Copy link
Author

Choose a reason for hiding this comment

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

yes

return lyrics
if timestamps:
# restore the old context
self.context["context"]["client"] = copied_context_client # type: ignore
Copy link
Owner

Choose a reason for hiding this comment

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

type ignore should not be needed

Copy link
Author

Choose a reason for hiding this comment

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

this is technically not a type ignore, but it will supress the typechecker error:

copied_context_client might not be initialized


if timestamps:
# change the client to get lyrics with timestamps (mobile only)
copied_context_client = self.context["context"]["client"].copy()
Copy link
Owner

Choose a reason for hiding this comment

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

This could be used in more places in the future, so I would suggest moving it to a separate utility function that returns a deepcopy of the context changed to the mobile context (maybe get_mobile_context next to initialize_context in helpers.py)

Copy link
Author

Choose a reason for hiding this comment

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

Agreed, however I wouldn't deepcopy the context, because someone might want to do weird stuff with references inside self.context. Using shallow-copy here, should structually restore everything to how it was before the call. (so I think)

a cool thing would be using a context manager for use with, like

with yt.as_mobile():
    ...
# back to desktop

(contextlib) makes a function a context-manager

Copy link
Owner

Choose a reason for hiding this comment

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

Interesting idea, I like it. But it won't be pretty to wrap the entire function body in a with statement?

And wouldn't it cause problems during parallel execution? If I call a two endpoint with the same YTMusic instance at the same time, wouldn't one of them break if you perform a shallow copy?

ytmusicapi/mixins/browsing.py Show resolved Hide resolved
@overload
def get_lyrics(self, browseId: str, timestamps: Literal[True] = True) -> Optional[Lyrics|TimedLyrics]:
"""
Returns lyrics of a song or video. When `timestamps` is set, lyrics are returned with
Copy link
Owner

Choose a reason for hiding this comment

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

The docstring is now duplicated in three places, please fix

Only the actual function should have the docstring, overloads don't need it

@@ -876,7 +1068,7 @@ def get_basejs_url(self):
if match is None:
raise YTMusicError("Could not identify the URL for base.js player.")

return YTM_DOMAIN + match.group(1)
return cast(str, YTM_DOMAIN + match.group(1))
Copy link
Owner

Choose a reason for hiding this comment

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

please don't change unrelated functions

Copy link
Author

Choose a reason for hiding this comment

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

cast doesn't do anything but assert a type to the type checker.
However, this should be removed and instead a return type should be added. (fixes the same issue)

Copy link
Owner

Choose a reason for hiding this comment

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

ok, could you either revert the change or add the return type then? I'd prefer the latter


def _send_request(self, endpoint: str, body: dict, additionalParams: str = "") -> dict:
"""for sending post requests to YouTube Music"""
...
Copy link
Owner

Choose a reason for hiding this comment

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

The dots are not needed, please remove?

Copy link
Author

Choose a reason for hiding this comment

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

Pylance will report an error if not.
(if you would remove the docstring, it would throw an error anyway, as the function is technically empty. See: https://docs.python.org/3/library/typing.html#typing.overload)

Copy link
Owner

Choose a reason for hiding this comment

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

This project uses mypy, so please don't worry about pylance findings. For mypy it's ok to have a docstring in place of ...

It's a matter of personal preference - for me the docstring has a purpose and fulfills the requirement of no empty function body, so I like it better than the nondescript ...

Copy link
Owner

Choose a reason for hiding this comment

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

Side note: I have triggered the lint jobs so you can see the mypy and ruff findings remaining.

I recommend taking a look at https://github.com/sigma67/ytmusicapi/blob/main/CONTRIBUTING.rst

where pre-commit is described - if that passes, the lint jobs should pass as well.

@sigma67 sigma67 added the enhancement New feature or request label Oct 16, 2024
heinrich26 and others added 3 commits October 16, 2024 23:33
@heinrich26 heinrich26 requested a review from sigma67 October 16, 2024 21:58
@heinrich26
Copy link
Author

finally used pre-commit to fix those formatting complaints 🤷‍♂️

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants