-
Notifications
You must be signed in to change notification settings - Fork 1.5k
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
Make spotDl faster and more resilient #999
Conversation
…cessing can continue on error.
Whoops. |
What happened ? |
…g parsing and early exit tests. Allow user to provide spotifyId and spotifySecret via CLI.
I got pulled away and realized I wanted to address some things for fun. I've updated the PR description 👍 |
spotdl/test/test_arg_behavior.py
Outdated
with self.assertRaises(SystemExit) as captor: | ||
with patch('spotdl.search.spotifyClient.SpotifyClient') as mock: | ||
with patch('spotdl.download.downloader.DownloadManager') as dlMock: | ||
app = SpotDlApp() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great that you've created some tests, just a small hint - you don't need to have separate with
statement to use multiple context managers. You can chain them by using comma, that would eliminate the right-way drift:
with self.assertRaises(SystemExit) as captor, \
patch('spotdl.search.spotifyClient.SpotifyClient') as mock, \
patch('spotdl.download.downloader.DownloadManager') as dlMock:
app = SpotDlApp()
spotdl/spotDlApp.py
Outdated
default_client_id = '4fe3fecfe5334023a1472516cc99d805' | ||
default_client_secret = '0f02b7c483c04257984695007a4a8d5c' | ||
|
||
def __parseArgs(self, cmdLine) -> Namespace: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this function should start with a single underscore (https://dbader.org/blog/meaning-of-underscores-in-python)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had forgotten about that entirely since the last time I really used python -- got me feeling all nostalgic haha. Thanks for the reminder!
spotdl/search/spotifyClient.py
Outdated
@@ -6,67 +6,63 @@ | |||
from spotipy.oauth2 import SpotifyClientCredentials | |||
|
|||
|
|||
class SpotifyClient(): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The parenthesis is not needed here.
In general, it's a good idea for replacing the ugly global
with a class. I wanted to do that myself in #992, but my focus was different there.
@@ -27,7 +26,7 @@ def __init__(self, rawTrackMeta, rawAlbumMeta, rawArtistMeta, youtubeLink): | |||
#! Note, since the following are class methods, an instance of songObj is initialized | |||
#! and passed to them | |||
@classmethod | |||
def from_url(cls, spotifyURL: str): | |||
def from_url(cls, spotifyURL: str, spotifyClient): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You don't need to pass spotifyClient
, as you can get its instance from anywhere.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right, we could new up an instance if we wanted to.
While passing the spotifyClient
in via params creates a bit of noise and, perhaps, a bit of inconvenience, I prefer this approach for a couple reasons.
- This allows DI to take place at the platform boundary/application entrypoint. Each instance of the class needs credential information, which is now made available via CLI args (with the existing as a fallback).
- For testability, I wanted to method params or constructor injection
I also went back and created a SpotifyClientFactory
which allows the SpotDlApp
instance to init our instance in a more testable way. Being sure to inject dependencies into SpotDlApp
means that, as development continues, the project can move towards boundary to boundary 'unit' testing, which covers the expected functionality of both individual classes as well as the way they're all collectively configured to function.
Cool. @MHausBermel I'll have a look. We probably won't be taking all changes from this PR as we are currently shifting to using Asyncio instead of multiprocessing. The other ideas seem interesting. Give me till the weekend. |
And @aklajnert nice to see you looking around. |
…tion of SpotifyClientFactory.
Thanks for the feedback, I appreciate it!
I introduced the You are both much better versed in python than myself, but on the off chance this is actually helpful, this is the command I use to run the tests from the root of the repo: |
When will this be merged? It seems pretty helpful and would solve a few issues for me. |
These a few other things were working on. So things are a little stuck up. |
Hi all. Joining in a bit late on this. @MHausBermel could you
|
I was trying to process a large playlist (~560 songs), which brought these flaws to light, so I decided to take a quick, hacky jab at improving it.
In this work:
spotifyClient
singleton pattern in favor of dependency injectionSpotifyDlApp
class into which our configured dependencies may be injected for testabilityargparse
-- I find the API easy to use, and really easy to add on to without deep-diving into the documentationOther notes: