Fix race condition in create MyPlexDevice from #620 (#625)

* Make test Plex Pass entitlements a subset

* Fix create MyPlexDevice race condition

* Rename to clientId to be consistent

* Move link method to MyPlexAccount
This commit is contained in:
JonnyWong16 2020-12-15 20:41:04 -08:00 committed by GitHub
parent 5102201969
commit 9d23ec072e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 27 additions and 38 deletions

View file

@ -76,6 +76,7 @@ class MyPlexAccount(PlexObject):
REQUESTS = 'https://plex.tv/api/invites/requests' # get
SIGNIN = 'https://plex.tv/users/sign_in.xml' # get with auth
WEBHOOKS = 'https://plex.tv/api/v2/user/webhooks' # get, post with data
LINK = 'https://plex.tv/api/v2/pins/link' # put
# Hub sections
VOD = 'https://vod.provider.plex.tv/' # get
WEBSHOWS = 'https://webshows.provider.plex.tv/' # get
@ -153,15 +154,15 @@ class MyPlexAccount(PlexObject):
self.services = None
self.joined_at = None
def device(self, name=None, clientIdentifier=None):
def device(self, name=None, clientId=None):
""" Returns the :class:`~plexapi.myplex.MyPlexDevice` that matches the name specified.
Parameters:
name (str): Name to match against.
clientIdentifier (str): clientIdentifier to match against.
clientId (str): clientIdentifier to match against.
"""
for device in self.devices():
if (name and device.name.lower() == name.lower() or device.clientIdentifier == clientIdentifier):
if (name and device.name.lower() == name.lower() or device.clientIdentifier == clientId):
return device
raise NotFound('Unable to find device %s' % name)
@ -684,6 +685,19 @@ class MyPlexAccount(PlexObject):
elem = ElementTree.fromstring(req.text)
return self.findItems(elem)
def link(self, pin):
""" Link a device to the account using a pin code.
Parameters:
pin (str): The 4 digit link pin code.
"""
headers = {
'Content-Type': 'application/x-www-form-urlencoded',
'X-Plex-Product': 'Plex SSO'
}
data = {'code': pin}
self.query(self.LINK, self._session.put, headers=headers, data=data)
class MyPlexUser(PlexObject):
""" This object represents non-signed in users such as friends and linked
@ -1110,7 +1124,6 @@ class MyPlexPinLogin(object):
"""
PINS = 'https://plex.tv/api/v2/pins' # get
CHECKPINS = 'https://plex.tv/api/v2/pins/{pinid}' # get
LINK = 'https://plex.tv/api/v2/pins/link' # put
POLLINTERVAL = 1
def __init__(self, session=None, requestTimeout=None, headers=None):
@ -1125,6 +1138,7 @@ class MyPlexPinLogin(object):
self._abort = False
self._id = None
self._code = None
self._getCode()
self.finished = False
self.expired = False
@ -1132,10 +1146,6 @@ class MyPlexPinLogin(object):
@property
def pin(self):
if self._code:
return self._code
self._getCode()
return self._code
def run(self, callback=None, timeout=None):
@ -1199,24 +1209,6 @@ class MyPlexPinLogin(object):
return False
def link(self, code=None, token=None):
if code is None:
code = self.pin
url = self.LINK
headers = BASE_HEADERS.copy()
headers.update({
'Content-Type': 'application/x-www-form-urlencoded',
'X-Plex-Product': 'Plex SSO',
})
token = token or CONFIG.get('auth.server_token')
if token:
headers['X-Plex-Token'] = token
data = {'code': code}
self._query(url, self._session.put, headers=headers, data=data)
def _getCode(self):
url = self.PINS
response = self._query(url, self._session.post)
@ -1229,9 +1221,6 @@ class MyPlexPinLogin(object):
return self._code
def _checkLogin(self):
if not self._code:
self._getCode()
if not self._id:
return False

View file

@ -382,12 +382,13 @@ def getMyPlexAccount(opts=None): # pragma: no cover
return MyPlexAccount(username, password)
def createMyPlexDevice(headers, timeout=None): # pragma: no cover
def createMyPlexDevice(headers, account, timeout=10): # pragma: no cover
""" Helper function to create a new MyPlexDevice.
Parameters:
headers (dict): Provide the X-Plex- headers for the new device.
A unique X-Plex-Client-Identifier is required.
account (MyPlexAccount): The Plex account to create the device on.
timeout (int): Timeout in seconds to wait for device login.
"""
from plexapi.myplex import MyPlexPinLogin
@ -399,12 +400,10 @@ def createMyPlexDevice(headers, timeout=None): # pragma: no cover
pinlogin = MyPlexPinLogin(headers=headers)
pinlogin.run(timeout=timeout)
pinlogin.link()
account.link(pinlogin.pin)
pinlogin.waitForLogin()
account = getMyPlexAccount()
device = account.device(clientIdentifier=clientIdentifier)
return device
return account.device(clientId=clientIdentifier)
def choose(msg, items, attr): # pragma: no cover

View file

@ -160,14 +160,15 @@ def plex(request):
return PlexServer(SERVER_BASEURL, token, session=session)
@pytest.fixture()
@pytest.fixture(scope="session")
def sync_device(account_synctarget):
try:
device = account_synctarget.device(clientIdentifier=SYNC_DEVICE_IDENTIFIER)
device = account_synctarget.device(clientId=SYNC_DEVICE_IDENTIFIER)
except NotFound:
device = createMyPlexDevice(SYNC_DEVICE_HEADERS, timeout=10)
device = createMyPlexDevice(SYNC_DEVICE_HEADERS, account_synctarget)
assert device
assert "sync-target" in device.provides
return device

View file

@ -212,7 +212,7 @@ def test_myplex_plexpass_attributes(account_plexpass):
assert "sync" in account_plexpass.subscriptionFeatures
assert "premium_music_metadata" in account_plexpass.subscriptionFeatures
assert "plexpass" in account_plexpass.roles
assert set(account_plexpass.entitlements) == utils.ENTITLEMENTS
assert utils.ENTITLEMENTS <= set(account_plexpass.entitlements)
def test_myplex_claimToken(account):