From aa71d354b22f50c1f6d4d8fa9353c277fd2374ce Mon Sep 17 00:00:00 2001 From: Marco Pesenti Gritti Date: Mon, 25 Sep 2006 17:15:44 +0000 Subject: Merge branch 'master' of git+ssh://dev.laptop.org/git/sugar --- diff --git a/services/presence/Buddy.py b/services/presence/Buddy.py index 618902e..2c5d9b6 100644 --- a/services/presence/Buddy.py +++ b/services/presence/Buddy.py @@ -104,7 +104,7 @@ class Buddy(object): """Represents another person on the network and keeps track of the activities and resources they make available for sharing.""" - def __init__(self, bus_name, object_id, service): + def __init__(self, bus_name, object_id, service, icon_cache): if not bus_name: raise ValueError("DBus bus name must be valid") if not object_id or type(object_id) != type(1): @@ -137,6 +137,8 @@ class Buddy(object): if service is not None: self.add_service(service) + self._icon_cache = icon_cache + def object_path(self): return dbus.ObjectPath(self._object_path) @@ -148,25 +150,37 @@ class Buddy(object): if result_status == network.RESULT_SUCCESS: if icon and len(icon): icon = base64.b64decode(icon) - logging.debug("Buddy icon for '%s' is size %d" % (self._nick_name, len(icon))) self._set_icon(icon) + self._icon_cache.add_icon(icon) if (result_status == network.RESULT_FAILED or not icon) and self._icon_tries < 3: self._icon_tries = self._icon_tries + 1 logging.debug("Failed to retrieve buddy icon for '%s' on try %d of %d" % (self._nick_name, \ self._icon_tries, 3)) - gobject.timeout_add(1000, self._request_buddy_icon, service) + gobject.timeout_add(1000, self._get_buddy_icon, service) return False - def _request_buddy_icon(self, service): - """Contact the buddy to retrieve the buddy icon.""" + def _get_buddy_icon(self, service, retry=False): + """Get the buddy's icon. Check the cache first, if its + not there get the icon from the buddy over the network.""" + if retry != True: + # Only hit the cache once + icon_hash = service.get_one_property('icon-hash') + if icon_hash is not None: + icon = self._icon_cache.get_icon(icon_hash) + if icon: + logging.debug("%s: icon cache hit for %s." % (self._nick_name, icon_hash)) + self._set_icon(icon) + return False + + logging.debug("%s: icon cache miss, adding icon to cache." % self._nick_name) from sugar.p2p import Stream buddy_stream = Stream.Stream.new_from_service(service, start_reader=False) writer = buddy_stream.new_writer(service) success = writer.custom_request("get_buddy_icon", self._request_buddy_icon_cb, service) if not success: del writer, buddy_stream - gobject.timeout_add(1000, self._request_buddy_icon, service) + gobject.timeout_add(1000, self._get_buddy_icon, service, True) return False def _get_service_key(self, service): @@ -210,8 +224,7 @@ class Buddy(object): # A buddy isn't valid until its official presence # service has been found and resolved self._valid = True - logging.debug('Requesting buddy icon %s' % self._nick_name) - self._request_buddy_icon(service) + self._get_buddy_icon(service) self._color = service.get_one_property(_BUDDY_KEY_COLOR) if self._color: self._dbus_helper.PropertyChanged([_BUDDY_KEY_COLOR]) @@ -343,8 +356,8 @@ class Buddy(object): class Owner(Buddy): """Class representing the owner of the machine. This is the client portion of the Owner, paired with the server portion in Owner.py.""" - def __init__(self, ps, bus_name, object_id): - Buddy.__init__(self, bus_name, object_id, None) + def __init__(self, ps, bus_name, object_id, icon_cache): + Buddy.__init__(self, bus_name, object_id, None, icon_cache) self._nick_name = env.get_nick_name() self._color = env.get_color() self._ps = ps diff --git a/services/presence/BuddyIconCache.py b/services/presence/BuddyIconCache.py new file mode 100644 index 0000000..59d5e88 --- /dev/null +++ b/services/presence/BuddyIconCache.py @@ -0,0 +1,61 @@ +import os, time, md5 +from sugar import env +from sugar import util + +class BuddyIconCache(object): + """Caches icons on disk and finds them based on md5 hash.""" + def __init__(self): + ppath = env.get_profile_path() + self._cachepath = os.path.join(ppath, "cache", "buddy-icons") + if not os.path.exists(self._cachepath): + os.makedirs(self._cachepath) + + self._cache = {} + + # 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 type(printable_hash) != type(u""): + raise RuntimeError("printable_hash must be a unicode string.") + try: + fname = self._cache[printable_hash] + return self._get_icon_data(fname) + except KeyError: + pass + return None + + def add_icon(self, icon_data): + if len(icon_data) == 0: + return + + m = md5.new() + m.update(icon_data) + printable_hash = util.printable_hash(m.digest()) + if self._cache.has_key(printable_hash): + del m + return + + # Write the icon to disk and add an entry to our cache for it + m.update(time.asctime()) + fname = util.printable_hash(m.digest()) + fd = open(os.path.join(self._cachepath, fname), "w") + fd.write(icon_data) + fd.close() + self._cache[printable_hash] = fname + del m diff --git a/services/presence/Makefile.am b/services/presence/Makefile.am index c095cb6..f64bc02 100644 --- a/services/presence/Makefile.am +++ b/services/presence/Makefile.am @@ -6,11 +6,12 @@ $(service_DATA): $(service_in_files) Makefile @sed -e "s|\@bindir\@|$(bindir)|" $< > $@ sugardir = $(pkgdatadir)/services/presence -sugar_PYTHON = \ +sugar_PYTHON = \ __init__.py \ Activity.py \ Buddy.py \ - PresenceService.py \ + BuddyIconCache.py \ + PresenceService.py \ Service.py bin_SCRIPTS = sugar-presence-service diff --git a/services/presence/PresenceService.py b/services/presence/PresenceService.py index 73c2efa..77c67ee 100644 --- a/services/presence/PresenceService.py +++ b/services/presence/PresenceService.py @@ -5,6 +5,7 @@ import Activity import random import logging from sugar import util +import BuddyIconCache _SA_UNRESOLVED = 0 @@ -302,9 +303,11 @@ class PresenceService(object): self._bus_name = dbus.service.BusName(_PRESENCE_SERVICE, bus=self._session_bus) self._dbus_helper = PresenceServiceDBusHelper(self, self._bus_name) + self._icon_cache = BuddyIconCache.BuddyIconCache() + # Our owner object objid = self._get_next_object_id() - self._owner = Buddy.Owner(self, self._bus_name, objid) + self._owner = Buddy.Owner(self, self._bus_name, objid, self._icon_cache) self._buddies[self._owner.get_name()] = self._owner self._started = False @@ -423,7 +426,7 @@ class PresenceService(object): except KeyError: source_addr = service.get_source_address() objid = self._get_next_object_id() - buddy = Buddy.Buddy(self._bus_name, objid, service) + buddy = Buddy.Buddy(self._bus_name, objid, service, self._icon_cache) self._buddies[name] = buddy self._dbus_helper.ServiceAppeared(service.object_path()) if not buddy_was_valid and buddy.is_valid(): diff --git a/shell/model/Owner.py b/shell/model/Owner.py index 097d9c2..3bf3a55 100644 --- a/shell/model/Owner.py +++ b/shell/model/Owner.py @@ -8,6 +8,7 @@ from sugar import env import logging from sugar.p2p import Stream from sugar.presence import PresenceService +from sugar import util from model.Invites import Invites import dbus @@ -24,11 +25,17 @@ class ShellOwner(object): user_dir = profile.get_path() self._icon = None + self._icon_hash = "" for fname in os.listdir(user_dir): if not fname.startswith("buddy-icon."): continue fd = open(os.path.join(user_dir, fname), "r") self._icon = fd.read() + if self._icon: + # Get the icon's hash + import md5, binascii + digest = md5.new(self._icon).digest() + self._icon_hash = util.printable_hash(digest) fd.close() break @@ -49,7 +56,7 @@ class ShellOwner(object): def announce(self): # Create and announce our presence color = conf.get_profile().get_color() - props = {'color':color.to_string()} + props = {'color': color.to_string(), 'icon-hash': self._icon_hash} self._service = self._pservice.register_service(self._nick, PRESENCE_SERVICE_TYPE, properties=props) logging.debug("Owner '%s' using port %d" % (self._nick, self._service.get_port())) -- cgit v0.9.1