Move common google api auth functionality into common

So we can reuse it for google sheets
pull/54/head
Mike Lang 6 years ago
parent 2b4d2cce90
commit 3647d091f8

@ -0,0 +1,62 @@
import time
import gevent
import requests
class GoogleAPIClient(object):
"""Manages access to google apis and maintains an active access token.
Make calls using client.request(), which is a wrapper for requests.request().
"""
ACCESS_TOKEN_ERROR_RETRY_INTERVAL = 10
# Refresh token 10min before it expires (it normally lasts an hour)
ACCESS_TOKEN_REFRESH_TIME_BEFORE_EXPIRY = 600
def __init__(self, client_id, client_secret, refresh_token):
self.client_id = client_id
self.client_secret = client_secret
self.refresh_token = refresh_token
self._first_get_access_token = gevent.spawn(self.get_access_token)
@property
def access_token(self):
"""Blocks if access token unavailable yet"""
self._first_get_access_token.join()
return self._access_token
def get_access_token(self):
"""Authenticates against google's API and retrieves a token we will use in
subsequent requests.
This function gets called automatically when needed, there should be no need to call it
yourself."""
while True:
try:
start_time = time.time()
resp = requests.post('https://www.googleapis.com/oauth2/v4/token', data={
'client_id': self.client_id,
'client_secret': self.client_secret,
'refresh_token': self.refresh_token,
'grant_type': 'refresh_token',
})
resp.raise_for_status()
data = resp.json()
self._access_token = data['access_token']
expires_in = (start_time + data['expires_in']) - time.time()
if expires_in < self.ACCESS_TOKEN_REFRESH_TIME_BEFORE_EXPIRY:
self.logger.warning("Access token expires in {}s, less than normal leeway time of {}s".format(
expires_in, self.ACCESS_TOKEN_REFRESH_TIME_BEFORE_EXPIRY,
))
gevent.spawn_later(expires_in - self.ACCESS_TOKEN_REFRESH_TIME_BEFORE_EXPIRY, self.get_access_token)
except Exception:
self.logger.exception("Failed to fetch access token, retrying")
self.wait(self.ACCESS_TOKEN_ERROR_RETRY_INTERVAL)
else:
break
def request(self, method, url, headers={}, **kwargs):
# merge in auth header
headers = dict(headers, Authorization='Bearer {}'.format(self.access_token))
return requests.request(method, url, headers=headers, **kwargs)

@ -1,63 +1,15 @@
import logging import logging
import time
import gevent from common.googleapis import GoogleAPIClient
import requests
class Youtube(object): class Youtube(object):
"""Manages access to youtube and maintains an active access token""" """Manages youtube API operations"""
ACCESS_TOKEN_ERROR_RETRY_INTERVAL = 10
# Refresh token 10min before it expires (it normally lasts an hour)
ACCESS_TOKEN_REFRESH_TIME_BEFORE_EXPIRY = 600
def __init__(self, client_id, client_secret, refresh_token): def __init__(self, client_id, client_secret, refresh_token):
self.logger = logging.getLogger(type(self).__name__) self.logger = logging.getLogger(type(self).__name__)
self.client_id = client_id self.client = GoogleAPIClient(client_id, client_secret, refresh_token)
self.client_secret = client_secret
self.refresh_token = refresh_token
self._first_get_access_token = gevent.spawn(self.get_access_token)
@property
def access_token(self):
"""Blocks if access token unavailable yet"""
self._first_get_access_token.join()
return self._access_token
def get_access_token(self):
"""Authenticates against the youtube API and retrieves a token we will use in
subsequent requests.
This function gets called automatically when needed, there should be no need to call it
yourself."""
while True:
try:
start_time = time.time()
resp = requests.post('https://www.googleapis.com/oauth2/v4/token', data={
'client_id': self.client_id,
'client_secret': self.client_secret,
'refresh_token': self.refresh_token,
'grant_type': 'refresh_token',
})
resp.raise_for_status()
data = resp.json()
self._access_token = data['access_token']
expires_in = (start_time + data['expires_in']) - time.time()
if expires_in < self.ACCESS_TOKEN_REFRESH_TIME_BEFORE_EXPIRY:
self.logger.warning("Access token expires in {}s, less than normal leeway time of {}s".format(
expires_in, self.ACCESS_TOKEN_REFRESH_TIME_BEFORE_EXPIRY,
))
gevent.spawn_later(expires_in - self.ACCESS_TOKEN_REFRESH_TIME_BEFORE_EXPIRY, self.get_access_token)
except Exception:
self.logger.exception("Failed to fetch access token, retrying")
self.wait(self.ACCESS_TOKEN_ERROR_RETRY_INTERVAL)
else:
break
def auth_headers(self):
return {'Authorization': 'Bearer {}'.format(self.access_token)}
def upload_video(self, title, description, tags, data, hidden=False): def upload_video(self, title, description, tags, data, hidden=False):
"""Data may be a string, file-like object or iterator. Returns id.""" """Data may be a string, file-like object or iterator. Returns id."""
@ -72,7 +24,7 @@ class Youtube(object):
json['status'] = { json['status'] = {
'privacyStatus': 'unlisted', 'privacyStatus': 'unlisted',
} }
resp = requests.post( resp = self.client.request('POST',
'https://www.googleapis.com/upload/youtube/v3/videos', 'https://www.googleapis.com/upload/youtube/v3/videos',
headers=self.auth_headers(), headers=self.auth_headers(),
params={ params={
@ -83,7 +35,7 @@ class Youtube(object):
) )
resp.raise_for_status() resp.raise_for_status()
upload_url = resp.headers['Location'] upload_url = resp.headers['Location']
resp = requests.post(upload_url, headers=self.auth_headers(), data=data) resp = self.client.request('POST', upload_url, headers=self.auth_headers(), data=data)
resp.raise_for_status() resp.raise_for_status()
return resp.json()['id'] return resp.json()['id']
@ -97,7 +49,7 @@ class Youtube(object):
# Break up into groups of 10 videos. I'm not sure what the limit is so this is reasonable. # Break up into groups of 10 videos. I'm not sure what the limit is so this is reasonable.
for i in range(0, len(ids), 10): for i in range(0, len(ids), 10):
group = ids[i:i+10] group = ids[i:i+10]
resp = requests.get( resp = self.client.request('GET',
'https://www.googleapis.com/youtube/v3/videos', 'https://www.googleapis.com/youtube/v3/videos',
headers=self.auth_headers(), headers=self.auth_headers(),
params={ params={

Loading…
Cancel
Save