Posts about the various geek projects I tinker with (old posts, page 1)

Google API's

I've been exporting info from my iRacing Stats application to html to do weekly and end of season updates on my iRacing blog. This process had been fairly manual; I'd manually upload the graph images to blogger, create a new blog post and paste in the exported html and edit the img tags to point to the uploaded images. Its not that it was too painful, but since it was a repetitive task I wanted to see if I could automate it.

So I dived into the Google API doco and pretty quickly worked out how to get my python application to post a new blog update. Getting the images uploaded was more painful, since blogger actually uses picasa to store images and the picasa API is terrible. I ended up using Google Drive to store the images, which means I needed to handle a few more steps than just uploading; namely changing the permissions to public viewing, uploading to a folder and retrieving the public url.

I've got it all working now, including prompting the user for only their blog's URL from which it app pulls the BlogID (rather than having to have the user go off and find it). All in all I'm quite proud of getting it all working. I just hope Google don't go changing their APIs in the near future.

I'm sure the knowledge I've gained here will be useful in many other projects.

Here's a bunch of links which I found helpful:

Here's a bit of code:

from oauth2client import file, client, tools  
from apiclient.discovery import build  
from apiclient.http import MediaFileUpload  
from httplib2 import Http

def blogger_post(outfile):  
 try:  
  html_file = open(outfile)  
  html_lines = html_file.read()  
  chop_start = html_lines.find('')  
  chop_end = html_lines.find('')  
  html_lines = html_lines[chop_start+6:chop_end]

  CLIENT_SECRET = 'client_secrets.json'  
  SCOPE = 'https://www.googleapis.com/auth/blogger'  
  store = file.Storage('storage_blogger.json')  
  creds = store.get()

  if not creds or creds.invalid:  
   flow = client.flow_from_clientsecrets(CLIENT_SECRET, SCOPE)  
   creds = tools.run(flow, store)

  service = build('blogger', 'v3', creds.authorize(Http()))  
  body = {  
   "kind": "blogger#post",  
   "id": cfg.config['Blogger']['blogid'],  
   "title": os.path.basename(os.path.splitext(outfile)[0]),  
   "content":html_lines  
   }

  request = service.posts().insert(blogId=cfg.config['Blogger']['blogid'], isDraft=True, body=body)  
  response = request.execute()  
  return response['url']  
 except:  
  return "Failed"

def blogger_img_upload(filename):  
 try:  
  CLIENT_SECRET = 'client_secrets.json'  
  SCOPE = ('https://www.googleapis.com/auth/drive', 'https://www.googleapis.com/auth/drive.file')  
  store = file.Storage('storage_drive.json')  
  creds = store.get()

  if not creds or creds.invalid:  
   flow = client.flow_from_clientsecrets(CLIENT_SECRET, SCOPE)  
   creds = tools.run(flow, store)

  service = build('drive', 'v2', creds.authorize(Http()))

  q = "title = 'iRacing Stats Graphs' and mimeType = 'application/vnd.google-apps.folder'"

  request = service.files().list(q=q)  
  response = request.execute()

  if len(response['items']) == 0:  
   body = {  
    "title": "iRacing Stats Graphs",  
    "mimeType": "application/vnd.google-apps.folder"  
    }

   request = service.files().insert(body=body)  
   response = request.execute()  
   folderId = response['id']  
  else:  
   folderId = response['items'][0]['id']

  pub.sendMessage('Uploading', graph=os.path.basename(os.path.splitext(filename)[0]))  
  body = {  
   "title": os.path.basename(os.path.splitext(filename)[0]),  
   }  
  body['parents'] = [{'id': folderId}]  
  media_body = MediaFileUpload(filename)

  request = service.files().insert(body=body, media_body=media_body)  
  response = request.execute()  
  fileId = response['id']

  body = {  
   "type": "anyone",  
   "role": "reader"  
   }  
  response = service.permissions().insert(fileId=fileId, body=body).execute()  
  response = service.files().get(fileId=fileId).execute()  
  return response['webContentLink'].split('&')[0]  
 except:  
  print("Upload of %s to blogger failed" % os.path.basename(os.path.splitext(filename)[0]))  
  return "Failed"

def blogger_config(url):  
 try:  
  CLIENT_SECRET = 'client_secrets.json'  
  SCOPE = 'https://www.googleapis.com/auth/blogger'  
  store = file.Storage('storage_blogger.json')  
  creds = store.get()

  if not creds or creds.invalid:  
   flow = client.flow_from_clientsecrets(CLIENT_SECRET, SCOPE)  
   creds = tools.run(flow, store)

  service = build('blogger', 'v3', creds.authorize(Http()))  
  response = service.blogs().getByUrl(url=url).execute()  
  cfg.write_blogid(response['id'])  
  return True  
 except:  
  pub.sendMessage('Alert', msg="Unable to find BlogID of: %s" % url, title="Blogger Config Failed")  
  return False

PhotoFrame PC Update

The Digital PhotoFrame PC located in my kitchen sees near daily use; in the morning while I make coffee I trigger playback of the Australian Broadcasting Commision's 90 second news headlines and NPR's 5 minute news update. While I'm preparing dinner I'll often use it to stream jazz from KJZZ.org. Occasionally I'll use it to display a recipe to follow along with.

All in all there isn't much reason that I'd want to change the setup, it currently functions exactly as I want. However, it is running Windows XP which is no longer support by Microsoft. I don't like the idea of having an unsecurable machine running on the network so I've started planning on switching it to run Lubuntu Linux.

Obviously this would require that I recreate all the AutoIT scripts which currently trigger all of the above things. I'm very confident that I could easily do this via Python based scripts. I've also started looking for one to one replacements for the handful of other features I make use of:

  • Variety to handle background image display and rotation.
  • Conky or LXDE screenlet for weather and clock.