mirror of
https://github.com/SickGear/SickGear.git
synced 2024-12-01 00:43:37 +00:00
Add Trakt watchlist to Add show/Trakt Cards.
Change revoke application access at Trakt when account is deleted in SG. Add support for 'DELETE' and 'PUT' requests in libtrakt. Fix use 'GET', 'POST' method in Trakt lib upgrade.
This commit is contained in:
parent
21c235e52c
commit
9cdf57f989
3 changed files with 126 additions and 51 deletions
|
@ -196,6 +196,18 @@ $(document).ready(function(){
|
|||
#else
|
||||
<optgroup label="To get recommended">
|
||||
<option value="trakt_recommended?action=add">Enable Trakt here</option>
|
||||
#end if
|
||||
</optgroup>
|
||||
#if any($sg_var('TRAKT_ACCOUNTS', []))
|
||||
<optgroup label="Trakt watchlisted">
|
||||
#for $account in $sg_var('TRAKT_ACCOUNTS')
|
||||
#if $sg_var('TRAKT_ACCOUNTS').get($account).active and $sg_var('TRAKT_ACCOUNTS').get($account).name
|
||||
<option value="trakt_watchlist?account=$account"#echo ('', selected)[('watchlist-%s' % $account) == $mode]#>for $sg_var('TRAKT_ACCOUNTS').get($account).name</option>
|
||||
#end if
|
||||
#end for
|
||||
#else
|
||||
<optgroup label="To get watchlisted">
|
||||
<option value="trakt_watchlist?action=add">Enable Trakt here</option>
|
||||
#end if
|
||||
</optgroup>
|
||||
#elif 'IMDb' == $browse_type
|
||||
|
|
|
@ -15,26 +15,40 @@ class TraktAccount:
|
|||
def __init__(self, account_id=None, token='', refresh_token='', auth_fail=0, last_fail=None, token_valid_date=None):
|
||||
self.account_id = account_id
|
||||
self._name = ''
|
||||
self._slug = ''
|
||||
self.token = token
|
||||
self.refresh_token = refresh_token
|
||||
self.auth_fail = auth_fail
|
||||
self.last_fail = last_fail
|
||||
self.token_valid_date = token_valid_date
|
||||
|
||||
def get_name_slug(self):
|
||||
try:
|
||||
resp = TraktAPI().trakt_request('users/settings', send_oauth=self.account_id, sleep_retry=20)
|
||||
self.reset_auth_failure()
|
||||
if 'user' in resp:
|
||||
self._name = resp['user']['username']
|
||||
self._slug = resp['user']['ids']['slug']
|
||||
except TraktAuthException:
|
||||
self.inc_auth_failure()
|
||||
self._name = ''
|
||||
except TraktException:
|
||||
pass
|
||||
|
||||
@property
|
||||
def slug(self):
|
||||
if self.token and self.active:
|
||||
if not self._slug:
|
||||
self.get_name_slug()
|
||||
else:
|
||||
self._slug = ''
|
||||
return self._slug
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
if self.token and self.active:
|
||||
if not self._name:
|
||||
try:
|
||||
resp = TraktAPI().trakt_request('users/settings', send_oauth=self.account_id, sleep_retry=20)
|
||||
self.reset_auth_failure()
|
||||
if 'user' in resp:
|
||||
self._name = resp['user']['username']
|
||||
except TraktAuthException:
|
||||
self.inc_auth_failure()
|
||||
self._name = ''
|
||||
except TraktException:
|
||||
pass
|
||||
self.get_name_slug()
|
||||
else:
|
||||
self._name = ''
|
||||
|
||||
|
@ -53,10 +67,10 @@ class TraktAccount:
|
|||
|
||||
@property
|
||||
def token_expired(self):
|
||||
return self.token_valid_date and datetime.datetime.now() > self.token_valid_date
|
||||
return self.token_valid_date and self.token_valid_date < datetime.datetime.now()
|
||||
|
||||
def reset_auth_failure(self):
|
||||
if self.auth_fail != 0:
|
||||
if 0 != self.auth_fail:
|
||||
self.auth_fail = 0
|
||||
self.last_fail = None
|
||||
|
||||
|
@ -68,13 +82,13 @@ class TraktAccount:
|
|||
if self.auth_fail < self.max_auth_fail:
|
||||
if self.last_fail:
|
||||
time_diff = datetime.datetime.now() - self.last_fail
|
||||
if self.auth_fail % 3 == 0:
|
||||
if time_diff > datetime.timedelta(days=1):
|
||||
if 0 == self.auth_fail % 3:
|
||||
if datetime.timedelta(days=1) < time_diff:
|
||||
self.inc_auth_failure()
|
||||
sickbeard.save_config()
|
||||
elif time_diff > datetime.timedelta(minutes=15):
|
||||
elif datetime.timedelta(minutes=15) < time_diff:
|
||||
self.inc_auth_failure()
|
||||
if self.auth_fail == self.max_auth_fail or time_diff > datetime.timedelta(hours=6):
|
||||
if self.auth_fail == self.max_auth_fail or datetime.timedelta(hours=6) < time_diff:
|
||||
sickbeard.save_config()
|
||||
else:
|
||||
self.inc_auth_failure()
|
||||
|
@ -99,19 +113,22 @@ class TraktAPI:
|
|||
return '!!!'.join('%s|%s|%s|%s|%s|%s' % (
|
||||
value.account_id, value.token, value.refresh_token, value.auth_fail,
|
||||
value.last_fail.strftime('%Y%m%d%H%M') if value.last_fail else '0',
|
||||
value.token_valid_date.strftime('%Y%m%d%H%M%S') if value.token_valid_date else '0') for (key, value) in data.items())
|
||||
value.token_valid_date.strftime('%Y%m%d%H%M%S') if value.token_valid_date else '0')
|
||||
for (key, value) in data.items())
|
||||
|
||||
@staticmethod
|
||||
def read_config_string(data):
|
||||
return dict((int(a.split('|')[0]), TraktAccount(
|
||||
int(a.split('|')[0]), a.split('|')[1], a.split('|')[2], int(a.split('|')[3]),
|
||||
datetime.datetime.strptime(a.split('|')[4], '%Y%m%d%H%M') if a.split('|')[4] != '0' else None,
|
||||
datetime.datetime.strptime(a.split('|')[5], '%Y%m%d%H%M%S') if a.split('|')[5] != '0' else None)) for a in data.split('!!!') if data)
|
||||
datetime.datetime.strptime(a.split('|')[5], '%Y%m%d%H%M%S') if a.split('|')[5] != '0' else None))
|
||||
for a in data.split('!!!') if data)
|
||||
|
||||
@staticmethod
|
||||
def add_account(token, refresh_token, token_valid_date):
|
||||
k = max(sickbeard.TRAKT_ACCOUNTS.keys() or [0]) + 1
|
||||
sickbeard.TRAKT_ACCOUNTS[k] = TraktAccount(account_id=k, token=token, refresh_token=refresh_token, token_valid_date=token_valid_date)
|
||||
sickbeard.TRAKT_ACCOUNTS[k] = TraktAccount(account_id=k, token=token, refresh_token=refresh_token,
|
||||
token_valid_date=token_valid_date)
|
||||
sickbeard.save_config()
|
||||
return k
|
||||
|
||||
|
@ -132,6 +149,7 @@ class TraktAPI:
|
|||
@staticmethod
|
||||
def delete_account(account):
|
||||
if account in sickbeard.TRAKT_ACCOUNTS:
|
||||
TraktAPI().trakt_request('/oauth/revoke', send_oauth=account, method='POST')
|
||||
sickbeard.TRAKT_ACCOUNTS.pop(account)
|
||||
sickbeard.save_config()
|
||||
return True
|
||||
|
@ -149,7 +167,7 @@ class TraktAPI:
|
|||
}
|
||||
|
||||
if refresh:
|
||||
if account and account in sickbeard.TRAKT_ACCOUNTS:
|
||||
if None is not account and account in sickbeard.TRAKT_ACCOUNTS:
|
||||
data['grant_type'] = 'refresh_token'
|
||||
data['refresh_token'] = sickbeard.TRAKT_ACCOUNTS[account].refresh_token
|
||||
else:
|
||||
|
@ -170,13 +188,22 @@ class TraktAPI:
|
|||
|
||||
if 'access_token' in resp and 'refresh_token' in resp and 'expires_in' in resp:
|
||||
token_valid_date = now + datetime.timedelta(seconds=sickbeard.helpers.tryInt(resp['expires_in']))
|
||||
if refresh or (not refresh and account and account in sickbeard.TRAKT_ACCOUNTS):
|
||||
return self.replace_account(account, resp['access_token'], resp['refresh_token'], token_valid_date, refresh)
|
||||
else:
|
||||
return self.add_account(resp['access_token'], resp['refresh_token'], token_valid_date)
|
||||
if refresh or (not refresh and None is not account and account in sickbeard.TRAKT_ACCOUNTS):
|
||||
return self.replace_account(account, resp['access_token'], resp['refresh_token'],
|
||||
token_valid_date, refresh)
|
||||
return self.add_account(resp['access_token'], resp['refresh_token'], token_valid_date)
|
||||
|
||||
return False
|
||||
|
||||
def trakt_request(self, path, data=None, headers=None, url=None, count=0, sleep_retry=60, send_oauth=None, **kwargs):
|
||||
def trakt_request(self, path, data=None, headers=None, url=None, count=0, sleep_retry=60,
|
||||
send_oauth=None, method=None, **kwargs):
|
||||
|
||||
if method not in ['GET', 'POST', 'PUT', 'DELETE', None]:
|
||||
return {}
|
||||
if None is method:
|
||||
method = ('GET', 'POST')['data' in kwargs.keys() or data is not None]
|
||||
if path != 'oauth/token' and None is send_oauth and method in ['POST', 'PUT', 'DELETE']:
|
||||
return {}
|
||||
|
||||
count += 1
|
||||
if count > self.max_retrys:
|
||||
|
@ -186,7 +213,7 @@ class TraktAPI:
|
|||
count > 1 and time.sleep(sleep_retry)
|
||||
|
||||
headers = headers or self.headers
|
||||
if send_oauth and send_oauth in sickbeard.TRAKT_ACCOUNTS:
|
||||
if None is not send_oauth and send_oauth in sickbeard.TRAKT_ACCOUNTS:
|
||||
if sickbeard.TRAKT_ACCOUNTS[send_oauth].active:
|
||||
if sickbeard.TRAKT_ACCOUNTS[send_oauth].needs_refresh:
|
||||
self.trakt_token(refresh=True, count=0, account=send_oauth)
|
||||
|
@ -202,54 +229,77 @@ class TraktAPI:
|
|||
|
||||
url = url or self.api_url
|
||||
try:
|
||||
resp = self.session.request(('GET', 'POST')['data' in kwargs.keys()],
|
||||
url + path, **kwargs)
|
||||
resp = self.session.request(method, '%s%s' % (url, path), **kwargs)
|
||||
|
||||
if 'DELETE' == method:
|
||||
result = None
|
||||
if 204 == resp.status_code:
|
||||
result = {'result': 'success'}
|
||||
elif 404 == resp.status_code:
|
||||
result = {'result': 'failed'}
|
||||
if result and None is not send_oauth and send_oauth in sickbeard.TRAKT_ACCOUNTS:
|
||||
sickbeard.TRAKT_ACCOUNTS[send_oauth].reset_auth_failure()
|
||||
return result
|
||||
resp.raise_for_status()
|
||||
return {}
|
||||
|
||||
# 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', logger.WARNING)
|
||||
return self.trakt_request(path, data, headers, url, count=count, sleep_retry=sleep_retry,
|
||||
send_oauth=send_oauth, method=method)
|
||||
# This is pretty much a fatal error if there is no status_code
|
||||
# It means there basically was no response at all
|
||||
# It means there basically was no response at all
|
||||
else:
|
||||
logger.log(u'Could not connect to Trakt. Error: {0}'.format(e), logger.WARNING)
|
||||
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, count=count, sleep_retry=sleep_retry, send_oauth=send_oauth)
|
||||
return self.trakt_request(path, data, headers, url, count=count, sleep_retry=sleep_retry,
|
||||
send_oauth=send_oauth, method=method)
|
||||
|
||||
elif 401 == code and path != 'oauth/token':
|
||||
if send_oauth:
|
||||
if None is not send_oauth:
|
||||
if sickbeard.TRAKT_ACCOUNTS[send_oauth].needs_refresh:
|
||||
if self.trakt_token(refresh=True, count=count, account=send_oauth):
|
||||
return self.trakt_request(path, data, headers, url, count=count, sleep_retry=sleep_retry, send_oauth=send_oauth)
|
||||
else:
|
||||
logger.log(u'Unauthorized. Please check your Trakt settings', logger.WARNING)
|
||||
sickbeard.TRAKT_ACCOUNTS[send_oauth].auth_failure()
|
||||
raise TraktAuthException()
|
||||
else:
|
||||
# sometimes the trakt server sends invalid token error even if it isn't
|
||||
return self.trakt_request(path, data, headers, url, count=count, sleep_retry=sleep_retry,
|
||||
send_oauth=send_oauth, method=method)
|
||||
|
||||
logger.log(u'Unauthorized. Please check your Trakt settings', logger.WARNING)
|
||||
sickbeard.TRAKT_ACCOUNTS[send_oauth].auth_failure()
|
||||
if count >= self.max_retrys:
|
||||
raise TraktAuthException()
|
||||
else:
|
||||
return self.trakt_request(path, data, headers, url, count=count, sleep_retry=sleep_retry, send_oauth=send_oauth)
|
||||
else:
|
||||
raise TraktAuthException()
|
||||
raise TraktAuthException()
|
||||
|
||||
# sometimes the trakt server sends invalid token error even if it isn't
|
||||
sickbeard.TRAKT_ACCOUNTS[send_oauth].auth_failure()
|
||||
if count >= self.max_retrys:
|
||||
raise TraktAuthException()
|
||||
|
||||
return self.trakt_request(path, data, headers, url, count=count, sleep_retry=sleep_retry,
|
||||
send_oauth=send_oauth, method=method)
|
||||
|
||||
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. Trying again', logger.WARNING)
|
||||
self.trakt_request(path, data, headers, url, count=count, sleep_retry=sleep_retry, send_oauth=send_oauth)
|
||||
self.trakt_request(path, data, headers, url, count=count, sleep_retry=sleep_retry,
|
||||
send_oauth=send_oauth, method=method)
|
||||
elif 404 == code:
|
||||
logger.log(u'Trakt error (404) the resource does not exist: %s' % url + path, logger.WARNING)
|
||||
logger.log(u'Trakt error (404) the resource does not exist: %s%s' % (url, path), logger.WARNING)
|
||||
else:
|
||||
logger.log(u'Could not connect to Trakt. Code error: {0}'.format(code), logger.ERROR)
|
||||
return {}
|
||||
except ValueError as e:
|
||||
logger.log(u'Value Error: {0}'.format(e), logger.ERROR)
|
||||
return {}
|
||||
|
||||
# check and confirm Trakt call did not fail
|
||||
if isinstance(resp, dict) and 'failure' == resp.get('status', None):
|
||||
|
@ -257,9 +307,8 @@ class TraktAPI:
|
|||
raise TraktException(resp['message'])
|
||||
if 'error' in resp:
|
||||
raise TraktException(resp['error'])
|
||||
else:
|
||||
raise TraktException('Unknown Error')
|
||||
raise TraktException('Unknown Error')
|
||||
|
||||
if send_oauth and send_oauth in sickbeard.TRAKT_ACCOUNTS:
|
||||
if None is not send_oauth and send_oauth in sickbeard.TRAKT_ACCOUNTS:
|
||||
sickbeard.TRAKT_ACCOUNTS[send_oauth].reset_auth_failure()
|
||||
return resp
|
||||
|
|
|
@ -3321,6 +3321,20 @@ class NewHomeAddShows(Home):
|
|||
'recommendations/shows?limit=%s&' % 100, 'Recommended for <b class="grey-text">%s</b> by Trakt' % name,
|
||||
mode='recommended-%s' % account, send_oauth=account)
|
||||
|
||||
def trakt_watchlist(self, *args, **kwargs):
|
||||
|
||||
if 'add' == kwargs.get('action'):
|
||||
return self.redirect('/config/notifications/#tabs-3')
|
||||
|
||||
account = sickbeard.helpers.tryInt(kwargs.get('account'), None)
|
||||
try:
|
||||
name = sickbeard.TRAKT_ACCOUNTS[account].name
|
||||
except KeyError:
|
||||
return self.trakt_default()
|
||||
return self.browse_trakt(
|
||||
'users/%s/watchlist/shows?limit=%s&' % (sickbeard.TRAKT_ACCOUNTS[account].slug, 100), 'WatchList for <b class="grey-text">%s</b> by Trakt' % name,
|
||||
mode='watchlist-%s' % account, send_oauth=account)
|
||||
|
||||
def trakt_default(self):
|
||||
|
||||
return self.redirect('/home/addShows/%s' % ('trakt_trending', sickbeard.TRAKT_MRU)[any(sickbeard.TRAKT_MRU)])
|
||||
|
@ -3330,7 +3344,7 @@ class NewHomeAddShows(Home):
|
|||
browse_type = 'Trakt'
|
||||
normalised, filtered = ([], [])
|
||||
|
||||
if not sickbeard.USE_TRAKT and 'recommended' in kwargs.get('mode', ''):
|
||||
if not sickbeard.USE_TRAKT and ('recommended' in kwargs.get('mode', '') or 'watchlist' in kwargs.get('mode', '')):
|
||||
error_msg = 'To browse personal recommendations, enable Trakt.tv in Config/Notifications/Social'
|
||||
return self.browse_shows(browse_type, browse_title, filtered, error_msg=error_msg, show_header=1, **kwargs)
|
||||
|
||||
|
|
Loading…
Reference in a new issue