From b4de7fe22f6c6dd8073877a913245e4fa3c6e3c8 Mon Sep 17 00:00:00 2001 From: rembo10 Date: Thu, 21 Jun 2012 15:44:15 +0530 Subject: [PATCH] First commit for the caching mechanism --- headphones/cache.py | 216 +++++++++++++++++++++++++++++++++++++++++ headphones/webserve.py | 21 ++++ 2 files changed, 237 insertions(+) create mode 100644 headphones/cache.py diff --git a/headphones/cache.py b/headphones/cache.py new file mode 100644 index 00000000..24da24c9 --- /dev/null +++ b/headphones/cache.py @@ -0,0 +1,216 @@ +# This file is part of Headphones. +# +# Headphones is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Headphones is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Headphones. If not, see . + +import os +import glob, urllib2 + +import lib.simplejson as simplejson + +import headphones +from headphones import helpers, logger + +lastfm_apikey = "690e1ed3bc00bc91804cd8f7fe5ed6d4" + +class Cache(object): + """ + This class deals with getting, storing and serving up artwork (album + art, artist images, etc) and info/descriptions (album info, artist descrptions) + to and from the cache folder. This can be called from within a web interface, + for example, using the helper functions getInfo(id) and getArtwork(id), to utilize the cached + images rather than having to retrieve them every time the page is reloaded. + + So you can call cache.getArtwork(id) which will return an absolute path + to the image file on the local machine, or if the cache directory + doesn't exist, or can not be written to, it will return a url to the image. + + Call cache.getInfo(id) to grab the artist/album info; will return the text description + + The basic format for art in the cache is .. + and for info it is ..txt + """ + + path_to_art_cache = os.path.join(headphones.CACHE_DIR, 'artwork') + path_to_info_cache = os.path.join(headphones.CACHE_DIR, 'info') + + id = None + id_type = None # 'artist' or 'album' + + artwork_files = [] + info_files = [] + + def __init__(self): + + pass + + def _exists(self, type): + + if type == 'artwork': + + self.artwork_files = glob.glob(os.path.join(self.path_to_art_cache, self.id + '*')) + + if self.artwork_files: + return True + else: + return False + + else: + + self.info_files = glob.glob(os.path.join(self.path_to_info_cache, self.id + '*')) + + if self.info_files: + return True + else: + return False + + def _get_age(self, date): + # There's probably a better way to do this + split_date = date.split('-') + days_old = int(split_date[0])*365 + int(split_date[1])*30 + int(split_date[2]) + + return days_old + + + def _is_current(self, file): + + base_filename = os.path.basename(file) + date = base_filename.split('.')[1] + + # Calculate how old the cached file is based on todays date & file date stamp + # helpers.today() returns todays date in yyyy-mm-dd format + if self._get_age(helpers.today()) - self._get_age(date) < 30: + return True + else: + return False + + def get_artwork_from_cache(self, id, id_type): + ''' + Pass a musicbrainz id to this function, and a type (either artist or album) + ''' + + self.id = id + self.id_type = id_type + + if self._exists('artwork') and self._is_current(self.artwork_files[0]): + return self.artwork_files[0] + else: + self._update_cache() + if self._exists('artwork'): + return self.artwork_files[0] + + def get_info_from_cache(self, id, id_type): + + self.id = id + self.id_type = id_type + + if self._exists('info') and self._is_current(self.info_files[0]): + f = open(self.info_files[0], 'r').read() + return f + else: + self._update_cache() + if self._exists('info'): + f = open(self.info_files[0],'r').read() + return f + + def _update_cache(self): + ''' + Since we call the same url for both info and artwork, we'll update both at the same time + ''' + + url = "http://ws.audioscrobbler.com/2.0/?method=" + self.id_type + ".getInfo&mbid=" + self.id + "&api_key=" + lastfm_apikey + "&format=json" + + logger.debug('Retrieving artist information from: ' + url) + try: + result = urllib2.urlopen(url).read() + artist = simplejson.JSONDecoder().decode(result) + artist_bio = artist['artist']['bio']['content'] + artist_image_url = artist['artist']['image'][4]['#text'] + except: + logger.warn('Could not open url: ' + url) + return + + if artist_bio: + + # Make sure the info dir exists: + if not os.path.isdir(self.path_to_info_cache): + try: + os.makedirs(self.path_to_info_cache) + except Exception, e: + logger.error('Unable to create info cache dir. Error: ' + e) + + # Delete any old files and replace it with a new one + for info_file in self.info_files: + try: + os.remove(info_file) + except: + logger.error('Error deleting file from the cache: ' + info_file) + + bio_path = os.path.join(self.path_to_info_cache, self.id + '.' + helpers.today() + '.txt') + try: + f = open(bio_path, 'w') + f.write(artist_bio) + f.close() + except Exception, e: + logger.error('Unable to write to the cache dir: ' + e) + + if artist_image_url: + + try: + artwork = urllib2.urlopen(artist_image_url).read() + except Exception, e: + logger.error('Unable to open url "' + artist_image_url + '". Error: ' + str(e)) + + if artwork: + + # Make sure the info dir exists: + if not os.path.isdir(self.path_to_art_cache): + try: + os.makedirs(self.path_to_art_cache) + except Exception, e: + logger.error('Unable to create artwork cache dir. Error: ' + str(e)) + #Delete the old stuff + for artwork_file in self.artwork_files: + try: + os.remove(artwork_file) + except: + logger.error('Error deleting file from the cache: ' + artwork_file) + + artwork_path = os.path.join(self.path_to_art_cache, self.id + '.' + helpers.today() + '.' + artist_image_url.replace('/','%%%').replace(':','_%_')) + try: + f = open(artwork_path, 'wb') + f.write(artwork) + f.close() + except Exception, e: + logger.error('Unable to write to the cache dir: ' + str(e)) + +def getArtwork(id, id_type): + + c = Cache() + artwork_path = c.get_artwork_from_cache(id, id_type) + return artwork_path + +def getInfo(id, id_type): + + c = Cache() + info_path = c.get_info_from_cache(id, id_type) + return info_path + +def getArtworkURL(id, id_type): + + c = Cache() + artwork_path = c.get_artwork_from_cache(id, id_type) + filename = os.path.basename(artwork_path) + encoded_url = filename.split('.')[2:] + normal_url = encoded_url.replace('%%%','/').replace('_%_',':') + return normal_url diff --git a/headphones/webserve.py b/headphones/webserve.py index b06e9584..da6bf2e1 100644 --- a/headphones/webserve.py +++ b/headphones/webserve.py @@ -591,3 +591,24 @@ class WebInterface(object): return data api.exposed = True + + def getInfo(self, id, id_type): + + from headphones import cache + return cache.getInfo(id, id_type) + + getInfo.exposed = True + + def getArtwork(self, id, id_type): + + from headphones import cache + return cache.getArtwork(id, id_type) + + getArtwork.exposed = True + + def getArtworkURL(self,id, id_type): + + from headphones import cache + return cache.getArtworkURL(id, id_type) + + getArtworkURL.exposed = True