embyupdate: Fix authentication header problem

There was a problem with the authentication header in the latest
versions. The header creation function changed to fix that. Username and
passwort authentication should work again.

The `host` config variable takes now a full hostname. For example
`http://localhost` instead of just `localhost`. This makes it easier to
use https hosts.
This commit is contained in:
Marvin Steadfast 2016-08-26 10:21:46 +02:00
parent 78171ee560
commit a282d4abc5
4 changed files with 99 additions and 41 deletions

View file

@ -3,25 +3,38 @@
"""Updates the Emby Library whenever the beets library is changed.
emby:
host: localhost
host: http://localhost
port: 8096
username: user
password: password
"""
from __future__ import division, absolute_import, print_function
from beets import config
from beets.plugins import BeetsPlugin
from six.moves.urllib.parse import urlencode
from six.moves.urllib.parse import urljoin, parse_qs, urlsplit, urlunsplit
import hashlib
import requests
from six.moves.urllib.parse import urlencode
from six.moves.urllib.parse import urljoin, parse_qs, urlsplit, urlunsplit
from beets import config
from beets.plugins import BeetsPlugin
def api_url(host, port, endpoint):
"""Returns a joined url.
Takes host, port and endpoint and generates a valid emby API url.
:param host: Hostname of the emby server
:param port: Portnumber of the emby server
:param endpoint: API endpoint
:type host: str
:type port: int
:type endpoint: str
:returns: Full API url
:rtype: str
"""
joined = urljoin('http://{0}:{1}'.format(host, port), endpoint)
joined = urljoin('{0}:{1}'.format(host, port), endpoint)
scheme, netloc, path, query_string, fragment = urlsplit(joined)
query_params = parse_qs(query_string)
@ -33,6 +46,13 @@ def api_url(host, port, endpoint):
def password_data(username, password):
"""Returns a dict with username and its encoded password.
:param username: Emby username
:param password: Emby password
:type username: str
:type password: str
:returns: Dictionary with username and encoded password
:rtype: dict
"""
return {
'username': username,
@ -43,24 +63,45 @@ def password_data(username, password):
def create_headers(user_id, token=None):
"""Return header dict that is needed to talk to the Emby API.
:param user_id: Emby user ID
:param token: Authentication token for Emby
:type user_id: str
:type token: str
:returns: Headers for requests
:rtype: dict
"""
headers = {
'Authorization': 'MediaBrowser',
'UserId': user_id,
'Client': 'other',
'Device': 'empy',
'DeviceId': 'beets',
'Version': '0.0.0'
}
headers = {}
authorization = (
'MediaBrowser UserId="{user_id}", '
'Client="other", '
'Device="beets", '
'DeviceId="beets", '
'Version="0.0.0"'
).format(user_id=user_id)
headers['x-emby-authorization'] = authorization
if token:
headers['X-MediaBrowser-Token'] = token
headers['x-mediabrowser-token'] = token
return headers
def get_token(host, port, headers, auth_data):
"""Return token for a user.
:param host: Emby host
:param port: Emby port
:param headers: Headers for requests
:param auth_data: Username and encoded password for authentication
:type host: str
:type port: int
:type headers: dict
:type auth_data: dict
:returns: Access Token
:rtype: str
"""
url = api_url(host, port, '/Users/AuthenticateByName')
r = requests.post(url, headers=headers, data=auth_data)
@ -70,6 +111,15 @@ def get_token(host, port, headers, auth_data):
def get_user(host, port, username):
"""Return user dict from server or None if there is no user.
:param host: Emby host
:param port: Emby port
:username: Username
:type host: str
:type port: int
:type username: str
:returns: Matched Users
:rtype: list
"""
url = api_url(host, port, '/Users/Public')
r = requests.get(url)
@ -84,7 +134,7 @@ class EmbyUpdate(BeetsPlugin):
# Adding defaults.
config['emby'].add({
u'host': u'localhost',
u'host': u'http://localhost',
u'port': 8096
})

View file

@ -48,6 +48,8 @@ And there are a few bug fixes too:
omitted.
* With :ref:`ignore_hidden` enabled, non-UTF-8 filenames would cause a crash.
This is fixed. :bug:`2168`
* :doc:`/plugins/embyupdate`: Fixes authentication header problem that caused
a problem that it was not possible to get tokens from the Emby API.
The last release, 1.3.19, also erroneously reported its version as "1.3.18"
when you typed ``beet version``. This has been corrected.

View file

@ -6,7 +6,7 @@ EmbyUpdate Plugin
To use ``embyupdate`` plugin, enable it in your configuration (see :ref:`using-plugins`). Then, you'll probably want to configure the specifics of your Emby server. You can do that using an ``emby:`` section in your ``config.yaml``, which looks like this::
emby:
host: localhost
host: http://localhost
port: 8096
username: user
apikey: apikey
@ -25,8 +25,8 @@ Configuration
The available options under the ``emby:`` section are:
- **host**: The Emby server name.
Default: ``localhost``
- **host**: The Emby server host. You have to include ``http://`` or ``https://``.
Default: ``http://localhost``
- **port**: The Emby server port.
Default: 8096
- **username**: A username of a Emby user that is allowed to refresh the library.

View file

@ -14,7 +14,7 @@ class EmbyUpdateTest(unittest.TestCase, TestHelper):
self.load_plugins('embyupdate')
self.config['emby'] = {
u'host': u'localhost',
u'host': u'http://localhost',
u'port': 8096,
u'username': u'username',
u'password': u'password'
@ -47,12 +47,14 @@ class EmbyUpdateTest(unittest.TestCase, TestHelper):
self.assertEqual(
embyupdate.create_headers('e8837bc1-ad67-520e-8cd2-f629e3155721'),
{
'Authorization': 'MediaBrowser',
'UserId': 'e8837bc1-ad67-520e-8cd2-f629e3155721',
'Client': 'other',
'Device': 'empy',
'DeviceId': 'beets',
'Version': '0.0.0'
'x-emby-authorization': (
'MediaBrowser '
'UserId="e8837bc1-ad67-520e-8cd2-f629e3155721", '
'Client="other", '
'Device="beets", '
'DeviceId="beets", '
'Version="0.0.0"'
)
}
)
@ -61,13 +63,15 @@ class EmbyUpdateTest(unittest.TestCase, TestHelper):
embyupdate.create_headers('e8837bc1-ad67-520e-8cd2-f629e3155721',
token='abc123'),
{
'Authorization': 'MediaBrowser',
'UserId': 'e8837bc1-ad67-520e-8cd2-f629e3155721',
'Client': 'other',
'Device': 'empy',
'DeviceId': 'beets',
'Version': '0.0.0',
'X-MediaBrowser-Token': 'abc123'
'x-emby-authorization': (
'MediaBrowser '
'UserId="e8837bc1-ad67-520e-8cd2-f629e3155721", '
'Client="other", '
'Device="beets", '
'DeviceId="beets", '
'Version="0.0.0"'
),
'x-mediabrowser-token': 'abc123'
}
)
@ -132,12 +136,14 @@ class EmbyUpdateTest(unittest.TestCase, TestHelper):
content_type='application/json')
headers = {
'Authorization': 'MediaBrowser',
'UserId': 'e8837bc1-ad67-520e-8cd2-f629e3155721',
'Client': 'other',
'Device': 'empy',
'DeviceId': 'beets',
'Version': '0.0.0'
'x-emby-authorization': (
'MediaBrowser '
'UserId="e8837bc1-ad67-520e-8cd2-f629e3155721", '
'Client="other", '
'Device="beets", '
'DeviceId="beets", '
'Version="0.0.0"'
)
}
auth_data = {
@ -147,7 +153,7 @@ class EmbyUpdateTest(unittest.TestCase, TestHelper):
}
self.assertEqual(
embyupdate.get_token('localhost', 8096, headers, auth_data),
embyupdate.get_token('http://localhost', 8096, headers, auth_data),
'4b19180cf02748f7b95c7e8e76562fc8')
@responses.activate
@ -196,7 +202,7 @@ class EmbyUpdateTest(unittest.TestCase, TestHelper):
status=200,
content_type='application/json')
response = embyupdate.get_user('localhost', 8096, 'username')
response = embyupdate.get_user('http://localhost', 8096, 'username')
self.assertEqual(response[0]['Id'],
'2ec276a2642e54a19b612b9418a8bd3b')