new icon cache system

This commit is contained in:
Guillaume Desmottes 2007-02-26 17:18:52 +01:00
parent a1f5684944
commit 4c0352e9bc
4 changed files with 89 additions and 68 deletions

View File

@ -32,7 +32,7 @@ class Buddy(dbus.service.Object):
"""Represents another person on the network and keeps track of the """Represents another person on the network and keeps track of the
activities and resources they make available for sharing.""" activities and resources they make available for sharing."""
def __init__(self, bus_name, object_id, icon_cache, handle=None): def __init__(self, bus_name, object_id, handle=None):
if not bus_name: if not bus_name:
raise ValueError("DBus bus name must be valid") raise ValueError("DBus bus name must be valid")
if not object_id or not isinstance(object_id, int): if not object_id or not isinstance(object_id, int):
@ -46,10 +46,9 @@ class Buddy(dbus.service.Object):
self._activities = {} # Activity ID -> Activity self._activities = {} # Activity ID -> Activity
self._icon_cache = icon_cache
self.handles = {} # tp client -> handle self.handles = {} # tp client -> handle
self._icon = None
self._nick_name = None self._nick_name = None
self._color = None self._color = None
self._key = None self._key = None
@ -149,7 +148,7 @@ class Buddy(dbus.service.Object):
return None return None
return self._activities[self._current_activity] return self._activities[self._current_activity]
def _set_icon(self, icon): def set_icon(self, icon):
"""Can only set icon for other buddies. The Owner """Can only set icon for other buddies. The Owner
takes care of setting it's own icon.""" takes care of setting it's own icon."""
if icon != self._icon: if icon != self._icon:
@ -181,8 +180,8 @@ class Buddy(dbus.service.Object):
class Owner(Buddy): class Owner(Buddy):
"""Class representing the owner of the machine. This is the client """Class representing the owner of the machine. This is the client
portion of the Owner, paired with the server portion in Owner.py.""" portion of the Owner, paired with the server portion in Owner.py."""
def __init__(self, ps, bus_name, object_id, icon_cache): def __init__(self, ps, bus_name, object_id):
Buddy.__init__(self, bus_name, object_id, icon_cache) Buddy.__init__(self, bus_name, object_id)
self._ps = ps self._ps = ps
self._nick_name = profile.get_nick_name() self._nick_name = profile.get_nick_name()

View File

@ -1,4 +1,5 @@
# Copyright (C) 2007, Red Hat, Inc. # Copyright (C) 2007, Red Hat, Inc.
# Copyright (C) 2007, Collabora Ltd.
# #
# This program is free software; you can redistribute it and/or modify # This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by
@ -14,64 +15,64 @@
# along with this program; if not, write to the Free Software # along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
import os, time, md5
from sugar import env from sugar import env
from sugar import util from sugar import util
import os.path
import cPickle
class BuddyIconCache(object): class BuddyIconCache(object):
"""Caches icons on disk and finds them based on md5 hash.""" """Caches icons on disk and finds them based on the jid of their owners."""
def __init__(self): def __init__(self):
ppath = env.get_profile_path() ppath = env.get_profile_path()
self._cachepath = os.path.join(ppath, "cache", "buddy-icons") self._cachepath = os.path.join(ppath, "cache", "buddy-icons", "cache")
if not os.path.exists(self._cachepath): if not os.path.exists(self._cachepath):
os.makedirs(self._cachepath) self._cache = {}
else:
self._load_cache()
self._cache = {} def _load_cache(self):
# Read all cached icons and their sums
for fname in os.listdir(self._cachepath):
m = md5.new()
data = self._get_icon_data(fname)
if len(data) == 0:
continue
m.update(data)
printable_hash = util.printable_hash(m.digest())
self._cache[printable_hash] = fname
del m
def _get_icon_data(self, fname):
fd = open(os.path.join(self._cachepath, fname), "r")
data = fd.read()
fd.close()
del fd
return data
def get_icon(self, printable_hash):
if not isinstance(printable_hash, unicode):
raise RuntimeError("printable_hash must be a unicode string.")
try: try:
fname = self._cache[printable_hash] self._cache = cPickle.load(open(self._cachepath, "r"))
return self._get_icon_data(fname) except:
except KeyError: self._cache = {}
pass
def _save_cache(self):
out = open(self._cachepath, "w")
cPickle.dump(self._cache, out, protocol=2)
def get_icon(self, jid, token):
hit = self._cache.get(jid)
if hit:
t, icon = hit[0], hit[1]
if t == token:
return icon
return None return None
def add_icon(self, icon_data): def store_icon(self, jid, token, data):
if len(icon_data) == 0: self._cache[jid] = (token, data)
return self._save_cache()
m = md5.new() if __name__ == "__main__":
m.update(icon_data) my_cache = BuddyIconCache()
printable_hash = util.printable_hash(m.digest())
if self._cache.has_key(printable_hash): # look for the icon in the cache
del m icon = my_cache.get_icon("test@olpc.collabora.co.uk", "aaaa")
return print icon
# Write the icon to disk and add an entry to our cache for it my_cache.store_icon("test@olpc.collabora.co.uk", "aaaa", "icon1")
m.update(time.asctime())
fname = util.printable_hash(m.digest()) # now we're sure that the icon is in the cache
fd = open(os.path.join(self._cachepath, fname), "w") icon = my_cache.get_icon("test@olpc.collabora.co.uk", "aaaa")
fd.write(icon_data) print icon
fd.close()
self._cache[printable_hash] = fname # new icon
del m my_cache.store_icon("test@olpc.collabora.co.uk", "bbbb", "icon2")
# the icon in the cache is not valid now
icon = my_cache.get_icon("test@olpc.collabora.co.uk", "aaaa")
print icon

View File

@ -26,8 +26,6 @@ from linklocal_plugin import LinkLocalPlugin
from buddy import Buddy, Owner from buddy import Buddy, Owner
from activity import Activity from activity import Activity
import buddyiconcache
_PRESENCE_SERVICE = "org.laptop.Sugar.Presence" _PRESENCE_SERVICE = "org.laptop.Sugar.Presence"
_PRESENCE_INTERFACE = "org.laptop.Sugar.Presence" _PRESENCE_INTERFACE = "org.laptop.Sugar.Presence"
@ -48,14 +46,12 @@ class PresenceService(dbus.service.Object):
self._handles = {} # tp client -> (handle -> Buddy) self._handles = {} # tp client -> (handle -> Buddy)
self._activities = {} # activity id -> Activity self._activities = {} # activity id -> Activity
self._icon_cache = buddyiconcache.BuddyIconCache()
bus = dbus.SessionBus() bus = dbus.SessionBus()
self._bus_name = dbus.service.BusName(_PRESENCE_SERVICE, bus=bus) self._bus_name = dbus.service.BusName(_PRESENCE_SERVICE, bus=bus)
# Create the Owner object # Create the Owner object
objid = self._get_next_object_id() objid = self._get_next_object_id()
self._owner = Owner(self, self._bus_name, objid, self._icon_cache) self._owner = Owner(self, self._bus_name, objid)
self._buddies[self._owner.get_key()] = self._owner self._buddies[self._owner.get_key()] = self._owner
self._registry = ManagerRegistry() self._registry = ManagerRegistry()
@ -68,6 +64,7 @@ class PresenceService(dbus.service.Object):
self._server_plugin.connect('status', self._server_status_cb) self._server_plugin.connect('status', self._server_status_cb)
self._server_plugin.connect('contact-online', self._contact_online) self._server_plugin.connect('contact-online', self._contact_online)
self._server_plugin.connect('contact-offline', self._contact_offline) self._server_plugin.connect('contact-offline', self._contact_offline)
self._server_plugin.connect('avatar-updated', self._avatar_updated)
self._server_plugin.start() self._server_plugin.start()
# Set up the link local connection # Set up the link local connection
@ -86,9 +83,9 @@ class PresenceService(dbus.service.Object):
if not buddy: if not buddy:
# we don't know yet this buddy # we don't know yet this buddy
objid = self._get_next_object_id() objid = self._get_next_object_id()
buddy = Buddy(self._bus_name, objid, self._icon_cache, handle=handle) buddy = Buddy(self._bus_name, objid, handle=handle)
buddy.set_key(key) buddy.set_key(key)
print "create buddy" print "create buddy", key
self._buddies[key] = buddy self._buddies[key] = buddy
new_buddy = True new_buddy = True
@ -120,6 +117,12 @@ class PresenceService(dbus.service.Object):
self._next_object_id = self._next_object_id + 1 self._next_object_id = self._next_object_id + 1
return self._next_object_id return self._next_object_id
def _avatar_updated(self, tp, handle, avatar):
buddy = self._handles[tp].get(handle)
if buddy:
buddy.set_icon(avatar)
@dbus.service.signal(_PRESENCE_INTERFACE, signature="o") @dbus.service.signal(_PRESENCE_INTERFACE, signature="o")
def ActivityAppeared(self, activity): def ActivityAppeared(self, activity):
pass pass

View File

@ -18,6 +18,7 @@
import gobject import gobject
from sugar import profile from sugar import profile
from sugar import util from sugar import util
from buddyiconcache import BuddyIconCache
import logging import logging
from telepathy.client import ConnectionManager, ManagerRegistry, Connection, Channel from telepathy.client import ConnectionManager, ManagerRegistry, Connection, Channel
@ -38,14 +39,18 @@ class ServerPlugin(gobject.GObject):
'contact-offline': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, 'contact-offline': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
([gobject.TYPE_PYOBJECT])), ([gobject.TYPE_PYOBJECT])),
'status': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, 'status': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
([gobject.TYPE_INT, gobject.TYPE_INT])) ([gobject.TYPE_INT, gobject.TYPE_INT])),
'avatar-updated': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
([gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT]))
} }
def __init__(self, registry): def __init__(self, registry):
gobject.GObject.__init__(self) gobject.GObject.__init__(self)
self._icon_cache = BuddyIconCache()
self._registry = registry self._registry = registry
self._online_contacts = set() self._online_contacts = set() # handles of online contacts
self._account = self._get_account_info() self._account = self._get_account_info()
self._ever_connected = False self._ever_connected = False
@ -154,8 +159,9 @@ class ServerPlugin(gobject.GObject):
# hack # hack
self._conn._valid_interfaces.add(CONN_INTERFACE_AVATARS) self._conn._valid_interfaces.add(CONN_INTERFACE_AVATARS)
self._conn[CONN_INTERFACE_AVATARS].connect_to_signal('AvatarUpdated', self._avatar_updated_cb)
#if CONN_INTERFACE_AVATARS in self._conn: #if CONN_INTERFACE_AVATARS in self._conn:
# #tokens = self._conn[CONN_INTERFACE_AVATARS].RequestAvatarTokens(subscribe_handles) # tokens = self._conn[CONN_INTERFACE_AVATARS].RequestAvatarTokens(subscribe_handles)
# #for handle, token in zip(subscribe_handles, tokens): # #for handle, token in zip(subscribe_handles, tokens):
# for handle in subscribe_handles: # for handle in subscribe_handles:
@ -212,15 +218,15 @@ class ServerPlugin(gobject.GObject):
self._conn[CONN_INTERFACE].Disconnect() self._conn[CONN_INTERFACE].Disconnect()
def _contact_go_offline(self, handle): def _contact_go_offline(self, handle):
name = self._conn[CONN_INTERFACE].InspectHandles(CONNECTION_HANDLE_TYPE_CONTACT, [handle])[0] jid = self._conn[CONN_INTERFACE].InspectHandles(CONNECTION_HANDLE_TYPE_CONTACT, [handle])[0]
print name, "offline" print jid, "offline"
self._online_contacts.remove(handle) self._online_contacts.remove(handle)
self.emit("contact-offline", handle) self.emit("contact-offline", handle)
def _contact_go_online(self, handle): def _contact_go_online(self, handle):
name = self._conn[CONN_INTERFACE].InspectHandles(CONNECTION_HANDLE_TYPE_CONTACT, [handle])[0] jid = self._conn[CONN_INTERFACE].InspectHandles(CONNECTION_HANDLE_TYPE_CONTACT, [handle])[0]
print name, "online" print jid, "online"
# TODO: use the OLPC interface to get the key # TODO: use the OLPC interface to get the key
key = handle key = handle
@ -241,3 +247,15 @@ class ServerPlugin(gobject.GObject):
elif online and status in ["offline", "invisible"]: elif online and status in ["offline", "invisible"]:
self._contact_go_offline(handle) self._contact_go_offline(handle)
def _avatar_updated_cb(self, handle, new_avatar_token):
jid = self._conn[CONN_INTERFACE].InspectHandles(CONNECTION_HANDLE_TYPE_CONTACT, [handle])[0]
icon = self._icon_cache.get_icon(jid, new_avatar_token)
if not icon:
# cache miss
avatar, mime_type = self._conn[CONN_INTERFACE_AVATARS].RequestAvatar(handle)
icon = ''.join(map(chr, avatar))
self._icon_cache.store_icon(jid, new_avatar_token, icon)
self.emit("avatar-updated", handle, icon)