SickGear/lib/libtrakt/trakt.py
JackDandy 74feb39eb6 Change Trakt notification config to only handle PIN authentication with the service.
Fix handling of an erroneous PIN input/authentication flow and clean-up the ui and results output.
Remove all other Trakt deprecated API V1 service features pending reconsideration.
2015-11-16 11:48:15 +00:00

141 lines
5.5 KiB
Python

import requests
import certifi
import json
import sickbeard
import time
from sickbeard import logger
from exceptions import traktException, traktAuthException # , traktServerBusy
class TraktAPI:
def __init__(self, ssl_verify=True, timeout=None):
self.session = requests.Session()
self.verify = ssl_verify and sickbeard.TRAKT_VERIFY and certifi.where()
self.timeout = timeout or sickbeard.TRAKT_TIMEOUT
self.auth_url = sickbeard.TRAKT_BASE_URL
self.api_url = sickbeard.TRAKT_BASE_URL
self.headers = {
'Content-Type': 'application/json',
'trakt-api-version': '2',
'trakt-api-key': sickbeard.TRAKT_CLIENT_ID
}
def trakt_token(self, trakt_pin=None, refresh=False, count=0):
if 3 <= count:
sickbeard.TRAKT_ACCESS_TOKEN = ''
return False
elif 0 < count:
time.sleep(3)
data = {
'client_id': sickbeard.TRAKT_CLIENT_ID,
'client_secret': sickbeard.TRAKT_CLIENT_SECRET,
'redirect_uri': 'urn:ietf:wg:oauth:2.0:oob'
}
if refresh:
data['grant_type'] = 'refresh_token'
data['refresh_token'] = sickbeard.TRAKT_REFRESH_TOKEN
else:
data['grant_type'] = 'authorization_code'
if trakt_pin:
data['code'] = trakt_pin
headers = {
'Content-Type': 'application/json'
}
resp = self.trakt_request('oauth/token', data=data, headers=headers, url=self.auth_url, method='POST', count=count)
if 'access_token' in resp:
sickbeard.TRAKT_TOKEN = resp['access_token']
if 'refresh_token' in resp:
sickbeard.TRAKT_REFRESH_TOKEN = resp['refresh_token']
return True
return False
def validate_account(self):
resp = self.trakt_request('users/settings')
return 'account' in resp
def get_connected_user(self):
if sickbeard.TRAKT_TOKEN:
response = 'Connected to Trakt user account: %s'
if sickbeard.TRAKT_CONNECTED_ACCOUNT and sickbeard.TRAKT_TOKEN == sickbeard.TRAKT_CONNECTED_ACCOUNT[1] and sickbeard.TRAKT_CONNECTED_ACCOUNT[0]:
return response % sickbeard.TRAKT_CONNECTED_ACCOUNT[0]
resp = self.trakt_request('users/settings')
if 'user' in resp:
sickbeard.TRAKT_CONNECTED_ACCOUNT = [resp['user']['username'], sickbeard.TRAKT_TOKEN]
return response % sickbeard.TRAKT_CONNECTED_ACCOUNT[0]
return 'Not connected to Trakt'
def trakt_request(self, path, data=None, headers=None, url=None, method='GET', count=0):
if None is sickbeard.TRAKT_TOKEN:
logger.log(u'You must get a Trakt token. Check your Trakt settings', logger.WARNING)
return {}
headers = headers or self.headers
url = url or self.api_url
count += 1
headers['Authorization'] = 'Bearer ' + sickbeard.TRAKT_TOKEN
try:
resp = self.session.request(method, url + path, headers=headers, timeout=self.timeout,
data=json.dumps(data) if data else [], verify=self.verify)
# check for http errors and raise if any are present
resp.raise_for_status()
# convert response to json
resp = resp.json()
except requests.RequestException as e:
code = getattr(e.response, 'status_code', None)
if not code:
if 'timed out' in e:
logger.log(u'Timeout connecting to Trakt. Try to increase timeout value in Trakt settings', logger.WARNING)
# This is pretty much a fatal error if there is no status_code
# It means there basically was no response at all
else:
logger.log(u'Could not connect to Trakt. Error: {0}'.format(e), logger.WARNING)
elif 502 == code:
# Retry the request, Cloudflare had a proxying issue
logger.log(u'Retrying trakt api request: %s' % path, logger.WARNING)
return self.trakt_request(path, data, headers, url, method, count=count)
elif 401 == code:
if self.trakt_token(refresh=True, count=count):
sickbeard.save_config()
return self.trakt_request(path, data, headers, url, method, count=count)
else:
logger.log(u'Unauthorized. Please check your Trakt settings', logger.WARNING)
raise traktAuthException()
elif code in (500, 501, 503, 504, 520, 521, 522):
# http://docs.trakt.apiary.io/#introduction/status-codes
logger.log(u'Trakt may have some issues and it\'s unavailable. Try again later please', logger.WARNING)
elif 404 == code:
logger.log(u'Trakt error (404) the resource does not exist: %s' % url + path, logger.WARNING)
else:
logger.log(u'Could not connect to Trakt. Code error: {0}'.format(code), logger.ERROR)
return {}
# check and confirm Trakt call did not fail
if isinstance(resp, dict) and 'failure' == resp.get('status', None):
if 'message' in resp:
raise traktException(resp['message'])
if 'error' in resp:
raise traktException(resp['error'])
else:
raise traktException('Unknown Error')
return resp