[rhythmbox] context: add basic request caching
- From: Jonathan Matthew <jmatthew src gnome org>
- To: svn-commits-list gnome org
- Cc:
- Subject: [rhythmbox] context: add basic request caching
- Date: Sun, 1 Nov 2009 06:00:59 +0000 (UTC)
commit 7551eadf08df41a6e1832cd026922cb029b089b8
Author: Jonathan Matthew <jonathan fibula d14n org>
Date: Sat Oct 31 22:06:48 2009 +1000
context: add basic request caching
plugins/context/context/AlbumTab.py | 51 +++++++++++++++++---------------
plugins/context/context/ArtistTab.py | 39 +++++++++++++++++-------
plugins/context/context/ContextView.py | 27 ++++++++++++++---
plugins/context/tmpl/artist-tmpl.html | 5 +++
4 files changed, 82 insertions(+), 40 deletions(-)
---
diff --git a/plugins/context/context/AlbumTab.py b/plugins/context/context/AlbumTab.py
index 9143c91..bda03a9 100644
--- a/plugins/context/context/AlbumTab.py
+++ b/plugins/context/context/AlbumTab.py
@@ -123,9 +123,8 @@ class AlbumView (gobject.GObject):
self.styles = self.basepath + '/tmpl/main.css'
def album_list_ready (self, ds):
- list = ds.get_top_albums ()
self.file = self.album_template.render (error = ds.get_error(),
- list = list,
+ list = ds.get_top_albums(),
artist = ds.get_artist(),
stylesheet = self.styles)
self.load_view ()
@@ -137,11 +136,14 @@ class AlbumDataSource (gobject.GObject):
'albums-ready' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ())
}
- def __init__ (self):
+ def __init__ (self, info_cache, ranking_cache):
gobject.GObject.__init__ (self)
self.albums = None
self.error = None
+ self.artist = None
self.max_albums_fetched = 8
+ self.info_cache = info_cache
+ self.ranking_cache = ranking_cache
def extract (self, data, position):
"""
@@ -165,19 +167,20 @@ class AlbumDataSource (gobject.GObject):
url = "%sartist.gettopalbums&artist=%s&api_key=%s" % (LastFM.URL_PREFIX,
artist.replace(" ", "+"),
LastFM.API_KEY)
- try:
- ld = rb.Loader ()
- ld.get_url (url, self.fetch_album_list_cb, artist)
- except Exception, e:
- print "problem fetching %s: %s" % (artist, e)
- return
+ cachekey = 'lastfm:artist:gettopalbums:%s' % artist
+ self.ranking_cache.fetch(cachekey, url, self.parse_album_list, artist)
- def fetch_album_list_cb (self, data, artist):
+ def parse_album_list (self, data, artist):
if data is None:
print "Nothing fetched for %s top albums" % artist
return
- parsed = dom.parseString (data)
+ try:
+ parsed = dom.parseString (data)
+ except Exception, e:
+ print "Error parsing album list: %s" % e
+ return False
+
lfm = parsed.getElementsByTagName ('lfm')[0]
if lfm.attributes['status'].value == 'failed':
self.error = lfm.childNodes[1].firstChild.data
@@ -208,41 +211,40 @@ class AlbumDataSource (gobject.GObject):
return self.albums
def fetch_album_info (self, artist, album, index):
+ cachekey = "lastfm:album:getinfo:%s:%s" % (artist, album)
url = "%salbum.getinfo&artist=%s&album=%s&api_key=%s" % (LastFM.URL_PREFIX,
artist.replace(" ", "+"),
album.replace(" ", "+"),
LastFM.API_KEY)
-
- ld = rb.Loader()
- ld.get_url (url, self.fetch_album_tracklist, album, index)
+ self.info_cache.fetch(cachekey, url, self.fetch_album_tracklist, album, index)
def fetch_album_tracklist (self, data, album, index):
if data is None:
self.assemble_info(None, None, None)
- parsed = dom.parseString (data)
-
try:
+ parsed = dom.parseString (data)
self.albums[index]['id'] = parsed.getElementsByTagName ('id')[0].firstChild.data
except Exception, e:
- print "Problem parsing id, exiting: %s" % e
- return None
+ print "Error parsing album tracklist: %s" % e
+ return False
self.albums[index]['releasedate'] = self.extract(parsed.getElementsByTagName ('releasedate'),0)
self.albums[index]['summary'] = self.extract(parsed.getElementsByTagName ('summary'), 0)
+ cachekey = "lastfm:album:tracks:%s" % self.albums[index]['id']
url = "%splaylist.fetch&playlistURL=lastfm://playlist/album/%s&api_key=%s" % (
LastFM.URL_PREFIX, self.albums[index]['id'], LastFM.API_KEY)
- ld = rb.Loader()
- ld.get_url (url, self.assemble_info, album, index)
+ self.info_cache.fetch(cachekey, url, self.assemble_info, album, index)
def assemble_info (self, data, album, index):
+ rv = True
if data is None:
print "nothing fetched for %s tracklist" % album
else:
- parsed = dom.parseString (data)
try:
+ parsed = dom.parseString (data)
list = parsed.getElementsByTagName ('track')
tracklist = []
album_length = 0
@@ -254,11 +256,12 @@ class AlbumDataSource (gobject.GObject):
self.albums[index]['tracklist'] = tracklist
self.albums[index]['duration'] = album_length
except Exception, e:
- print "Problem : %s" % e
+ print "Error parsing album playlist: %s" % e
+ rv = False
- gtk.gdk.threads_enter ()
self.album_info_fetched -= 1
print "%s albums left to process" % self.album_info_fetched
- gtk.gdk.threads_leave ()
if self.album_info_fetched == 0:
self.emit('albums-ready')
+
+ return rv
diff --git a/plugins/context/context/ArtistTab.py b/plugins/context/context/ArtistTab.py
index 368206f..5d1f776 100644
--- a/plugins/context/context/ArtistTab.py
+++ b/plugins/context/context/ArtistTab.py
@@ -126,9 +126,10 @@ class ArtistView (gobject.GObject):
# If called any other time, the behavior is undefined
try:
info = ds.get_artist_info ()
- small, med, big = info['images']
- summary, full_bio = info['bio']
+ small, med, big = info['images'] or (None, None, None)
+ summary, full_bio = info['bio'] or (None, None)
self.file = self.template.render (artist = ds.get_current_artist (),
+ error = ds.get_error (),
image = med,
fullbio = full_bio,
shortbio = summary,
@@ -147,15 +148,17 @@ class ArtistDataSource (gobject.GObject):
'artist-top-albums-ready' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ()),
}
- def __init__ (self):
+ def __init__ (self, info_cache, ranking_cache):
gobject.GObject.__init__ (self)
self.current_artist = None
+ self.error = None
self.artist = {
'info' : {
'data' : None,
'signal' : 'artist-info-ready',
'function' : 'getinfo',
+ 'cache' : info_cache,
'parsed' : False,
},
@@ -163,6 +166,7 @@ class ArtistDataSource (gobject.GObject):
'data' : None,
'signal' : 'artist-similar-ready',
'function' : 'getsimilar',
+ 'cache' : info_cache,
'parsed' : False,
},
@@ -170,6 +174,7 @@ class ArtistDataSource (gobject.GObject):
'data' : None,
'signal' : 'artist-top-albums-ready',
'function' : 'gettopalbums',
+ 'cache' : ranking_cache,
'parsed' : False,
},
@@ -177,6 +182,7 @@ class ArtistDataSource (gobject.GObject):
'data' : None,
'signal' : 'artist-top-tracks-ready',
'function' : 'gettoptracks',
+ 'cache' : ranking_cache,
'parsed' : False,
},
}
@@ -194,10 +200,12 @@ class ArtistDataSource (gobject.GObject):
def fetch_top_tracks (self, artist):
artist = artist.replace (" ", "+")
+ function = self.artist['top_tracks']['function']
+ cache = self.artist['top_tracks']['cache']
+ cachekey = "lastfm:artist:%s:%s" % (function, artist)
url = '%sartist.%s&artist=%s&api_key=%s' % (LastFM.URL_PREFIX,
- self.artist['top_tracks']['function'], artist, LastFM.API_KEY)
- ld = rb.Loader()
- ld.get_url (url, self.fetch_artist_data_cb, self.artist['top_tracks'])
+ function, artist, LastFM.API_KEY)
+ cache.fetch(cachekey, url, self.fetch_artist_data_cb, self.artist['top_tracks'])
def fetch_artist_data (self, artist):
"""
@@ -207,24 +215,33 @@ class ArtistDataSource (gobject.GObject):
before any of the get_* methods.
"""
self.current_artist = artist
+ self.error = None
artist = artist.replace(" ", "+")
for key, value in self.artist.items():
+ cachekey = "lastfm:artist:%s:%s" % (value['function'], artist)
url = '%sartist.%s&artist=%s&api_key=%s' % (LastFM.URL_PREFIX,
value['function'], artist, LastFM.API_KEY)
- ld = rb.Loader()
- ld.get_url (url, self.fetch_artist_data_cb, value)
+ value['cache'].fetch(cachekey, url, self.fetch_artist_data_cb, value)
def fetch_artist_data_cb (self, data, category):
if data is None:
print "no data fetched for artist %s" % category['function']
return
- category['data'] = dom.parseString (data)
- category['parsed'] = False
- self.emit (category['signal'])
+
+ try:
+ category['data'] = dom.parseString (data)
+ category['parsed'] = False
+ self.emit (category['signal'])
+ except Exception, e:
+ print "Error parsing artist %s: %s" % (category['function'], e)
+ return False
def get_current_artist (self):
return self.current_artist
+ def get_error (self):
+ return self.error
+
def get_top_albums (self):
if not self.artist['top_albums']['parsed']:
albums = []
diff --git a/plugins/context/context/ContextView.py b/plugins/context/context/ContextView.py
index 58011be..5848665 100644
--- a/plugins/context/context/ContextView.py
+++ b/plugins/context/context/ContextView.py
@@ -26,8 +26,8 @@
import rb, rhythmdb
import gtk, gobject
-import pango
import webkit
+import os
import ArtistTab as at
import AlbumTab as abt
@@ -55,7 +55,24 @@ class ContextView (gobject.GObject):
self.current_album = None
self.current_song = None
self.visible = True
-
+
+ # cache for artist/album information: valid for a month, can be used indefinitely
+ # if offline, discarded if unused for six months
+ self.info_cache = rb.URLCache(name = 'info',
+ path = os.path.join('context-pane', 'info'),
+ refresh = 30,
+ discard = 180)
+ # cache for rankings (artist top tracks and top albums): valid for a week,
+ # can be used for a month if offline
+ self.ranking_cache = rb.URLCache(name = 'ranking',
+ path = os.path.join('context-pane', 'ranking'),
+ refresh = 7,
+ lifetime = 30)
+
+ # maybe move this into an idle handler?
+ self.info_cache.clean()
+ self.ranking_cache.clean()
+
self.init_gui ()
self.init_tabs()
@@ -121,7 +138,7 @@ class ContextView (gobject.GObject):
self.visible = False
def change_tab (self, tab, newtab):
- print "swaping tab from %s to %s" % (self.current, newtab)
+ print "swapping tab from %s to %s" % (self.current, newtab)
if (self.current != newtab):
self.tab[self.current].deactivate()
self.tab[newtab].activate()
@@ -132,10 +149,10 @@ class ContextView (gobject.GObject):
self.ds = {}
self.view = {}
- self.ds['artist'] = at.ArtistDataSource ()
+ self.ds['artist'] = at.ArtistDataSource (self.info_cache, self.ranking_cache)
self.view['artist'] = at.ArtistView (self.shell, self.plugin, self.webview, self.ds['artist'])
self.tab['artist'] = at.ArtistTab (self.shell, self.buttons, self.ds['artist'], self.view['artist'])
- self.ds['album'] = abt.AlbumDataSource()
+ self.ds['album'] = abt.AlbumDataSource(self.info_cache, self.ranking_cache)
self.view['album'] = abt.AlbumView(self.shell, self.plugin, self.webview, self.ds['album'])
self.tab['album'] = abt.AlbumTab(self.shell, self.buttons, self.ds['album'], self.view['album'])
self.ds['lyrics'] = lt.LyricsDataSource (self.db)
diff --git a/plugins/context/tmpl/artist-tmpl.html b/plugins/context/tmpl/artist-tmpl.html
index 1249078..bea180a 100644
--- a/plugins/context/tmpl/artist-tmpl.html
+++ b/plugins/context/tmpl/artist-tmpl.html
@@ -22,6 +22,7 @@
</script>
</head>
<body class="artist">
+%if error is None:
<h1>${artist}</h1>
<img src="${image}" />
<div id="shortbio" class="shown">
@@ -41,5 +42,9 @@ Read less
Read less
</button>
</div>
+%else:
+<h1>Unable to retrieve artist information:</h1>
+<p class="error">${error}$</p>
+%endif
</body>
</html>
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]