-
Notifications
You must be signed in to change notification settings - Fork 1.8k
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 subsonic plugin #3001
Added subsonic plugin #3001
Conversation
Tox test was passed in my environment
|
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.
Looking good! I have some suggestions that I've left inline.
Also, we have a number of similar plugins around that update other various music library services, and they're generally called *update
. See mpdupdate
, embyupdate
, plexupdate
, sonosupdate
, and kodiupdate
. So, even though it's kind of long, I think we should probably change the name of this one to subsonicupdate
.
docs/changelog.rst
Outdated
@@ -18,6 +18,7 @@ New features: | |||
:user:`jams2` | |||
* Automatically upload to Google Play Music library on track import. | |||
:user:`shuaiscott` | |||
* Added Subsonic automatic library update plugin |
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 can be nicely linked if you instead use:
Add a new :doc:`/plugins/subsonic` that can automatically update your Subsonic library.
beetsplug/subsonic.py
Outdated
print("Operation completed successfully!") | ||
else: | ||
self._log.error(u'Unknown error code returned from server.') | ||
print("Unknown error code returned from server.") |
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 don't think there's any need to both log and print these messages—just using self._log.error
should suffice.
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.
Sorry, this is left behind from my "debugging". I wanted to remove the "print" statements but forgot.
beetsplug/subsonic.py
Outdated
@@ -0,0 +1,69 @@ | |||
# -*- coding: utf-8 -*- |
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.
Please include our standard license block comment at the top of the file. Then the documentation you've written below can go into a docstring instead of comments. (See some other plugins for examples.)
beetsplug/subsonic.py
Outdated
host = self.config['host'].get() | ||
port = self.config['port'].get() | ||
user = self.config['user'].get() | ||
passw = self.config['pass'].get() |
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.
Please use as_str()
instead of get()
to assert that values in the config are actually strings. (Unless you want the port to be a number, in which case use as_number
.)
beetsplug/subsonic.py
Outdated
user = self.config['user'].get() | ||
passw = self.config['pass'].get() | ||
r = string.ascii_letters + string.digits | ||
url = "http://"+str(host)+":"+str(port)+"/rest/startScan" |
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.
There's no need to use str()
here. Also, consider using format strings:
url = "http://{}:{}/rest/startScan".format(host, port)
beetsplug/subsonic.py
Outdated
salt = "".join([random.choice(r) for n in range(6)]) | ||
t = passw + salt | ||
token = hashlib.md5() | ||
token.update(t.encode('utf-8')) |
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.
Consider breaking up these lines of code and adding some comments to say what you're doing: generating a salt, with which you hash a password to send to the server.
docs/plugins/subsonic.rst
Outdated
Subsonic Plugin | ||
================ | ||
|
||
``Subsonic`` is a very simple plugin for beets that lets you automatically |
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.
Use lower-case subsonic
when you refer to the name of the plugin that the user should put into their configuration file.
beetsplug/subsonic.py
Outdated
|
||
def loaded(self): | ||
host = self.config['host'].get() | ||
port = self.config['port'].get() |
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.
Consider using self.config.add()
to provide defaults for these options. For example, host
might default to 127.0.0.1
, and it looks like there's a reasonable default port too. See other plugins for examples of how to provide defaults.
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 avoided that as people could have (like me) the two software running on different machines.
While I agree that a default configuration is better than none at all, it still poses the risk of having a non-working plugin.
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.
Hmm... I’m not quite sure I follow. Why would having multiple instances be incompatible with having a default? I imagine most people will want to customize most things, but perhaps not all the options all of the time.
Thanks for the feedback @sampsyo . |
Yeah, let’s do it. Keeping the plugin focused, at least for now, would be nice. (As you can see, we have multiple plugins that interact with MPD too.) |
Hi @sampsyo |
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.
Looking good! Here are some comments on the current version.
beetsplug/subsonicupdate.py
Outdated
def __init__(self): | ||
super(SubsonicUpdate, self).__init__() | ||
|
||
# Set default configuration values |
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.
Comments should be indented with the code they apply to.
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.
Corrected.
beetsplug/subsonicupdate.py
Outdated
response = requests.post(url, params=payload) | ||
|
||
# Log an eventual error reported by the server or success on status code 200 | ||
if (response.status_code == 0): |
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.
No parentheses are needed around if
conditions 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.
Corrected.
beetsplug/subsonicupdate.py
Outdated
elif (response.status_code == 200): | ||
self._log.info('Operation completed successfully!') | ||
else: | ||
self._log.error(u'Unknown error code returned from server.') |
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 might consider using a constant mapping from codes to messages. For example, you could define (at the top of this file):
ERROR_MESSAGES = {
0: u'Generic error, please try again later.'
}
and use that to look up the right message here.
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 see what you mean, I will take this in consideration for the next "version" of the plugin.
docs/plugins/subsonicupdate.rst
Outdated
- **host**: localhost | ||
- **port**: 4040 | ||
- **user**: admin | ||
- **pass**: admin |
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 usual style in the beets documentation is to put the defaults inline. For example, you can say:
The hostname (or IP address) for the Subsonic server. Default:
localhost
.
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.
Corrected.
beetsplug/subsonicupdate.py
Outdated
super(SubsonicUpdate, self).__init__() | ||
|
||
# Set default configuration values | ||
self.config['subsonic'].add({ |
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'm not sure you mean self.config['subsonic']
here, which will expect something like this in the configuration YAML:
subsonicupdate:
subsonic:
host: ...
port: ...
You can either use self.config
alone (to put the options directly under the plugin name) or use the global configuration, as in config['subsonic']
after importing the global config
object, to create a top-level subsonic
entry.
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 was due to my misunderstanding of how the config works, removed the "self" references and imported "config" from beets.
beetsplug/subsonicupdate.py
Outdated
salt = "".join([random.choice(r) for n in range(6)]) | ||
t = passw + salt | ||
|
||
# Hash the password making sure it's UTF-8 format |
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'm not sure "making sure it's UTF-8 format" makes sense as a comment here. What's actually happening is that the code is hashing the UTF-8 bytes for the password string. Maybe just leave this detail out?
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.
Removed comment.
beetsplug/subsonicupdate.py
Outdated
} | ||
url = "http://{}:{}/rest/startScan".format(host, port) | ||
|
||
# Send the request and store the response |
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 is a case where I think the comment might be a little too much detail—this line is clear enough by itself.
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.
Removed comment.
Addressed the last issues/suggestions. |
I resolved a merge conflict and refined the changelog entry text to match our usual style. It looks like you're experimenting with logging stuff, right? When that's sorted out, I think we're ready to merge. ✨ |
Well to be honest this is a whole experiment to me 😄 |
OK, cool. Please either leave the logging statements in there (uncommented) or remove them for now—either way, we can revisit improvements in the future. |
That’ll do it. Thanks again!! ✨ |
Added subsonic plugin