Merge branch 'master' of git+ssh://dev.laptop.org/git/sugar

This commit is contained in:
Marco Pesenti Gritti 2006-09-25 19:15:44 +02:00
commit aa71d354b2
5 changed files with 100 additions and 15 deletions

View File

@ -104,7 +104,7 @@ class Buddy(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, service): def __init__(self, bus_name, object_id, service, icon_cache):
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 type(object_id) != type(1): if not object_id or type(object_id) != type(1):
@ -137,6 +137,8 @@ class Buddy(object):
if service is not None: if service is not None:
self.add_service(service) self.add_service(service)
self._icon_cache = icon_cache
def object_path(self): def object_path(self):
return dbus.ObjectPath(self._object_path) return dbus.ObjectPath(self._object_path)
@ -148,25 +150,37 @@ class Buddy(object):
if result_status == network.RESULT_SUCCESS: if result_status == network.RESULT_SUCCESS:
if icon and len(icon): if icon and len(icon):
icon = base64.b64decode(icon) icon = base64.b64decode(icon)
logging.debug("Buddy icon for '%s' is size %d" % (self._nick_name, len(icon)))
self._set_icon(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: if (result_status == network.RESULT_FAILED or not icon) and self._icon_tries < 3:
self._icon_tries = self._icon_tries + 1 self._icon_tries = self._icon_tries + 1
logging.debug("Failed to retrieve buddy icon for '%s' on try %d of %d" % (self._nick_name, \ logging.debug("Failed to retrieve buddy icon for '%s' on try %d of %d" % (self._nick_name, \
self._icon_tries, 3)) self._icon_tries, 3))
gobject.timeout_add(1000, self._request_buddy_icon, service) gobject.timeout_add(1000, self._get_buddy_icon, service)
return False return False
def _request_buddy_icon(self, service): def _get_buddy_icon(self, service, retry=False):
"""Contact the buddy to retrieve the buddy icon.""" """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 from sugar.p2p import Stream
buddy_stream = Stream.Stream.new_from_service(service, start_reader=False) buddy_stream = Stream.Stream.new_from_service(service, start_reader=False)
writer = buddy_stream.new_writer(service) writer = buddy_stream.new_writer(service)
success = writer.custom_request("get_buddy_icon", self._request_buddy_icon_cb, service) success = writer.custom_request("get_buddy_icon", self._request_buddy_icon_cb, service)
if not success: if not success:
del writer, buddy_stream 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 return False
def _get_service_key(self, service): def _get_service_key(self, service):
@ -210,8 +224,7 @@ class Buddy(object):
# A buddy isn't valid until its official presence # A buddy isn't valid until its official presence
# service has been found and resolved # service has been found and resolved
self._valid = True self._valid = True
logging.debug('Requesting buddy icon %s' % self._nick_name) self._get_buddy_icon(service)
self._request_buddy_icon(service)
self._color = service.get_one_property(_BUDDY_KEY_COLOR) self._color = service.get_one_property(_BUDDY_KEY_COLOR)
if self._color: if self._color:
self._dbus_helper.PropertyChanged([_BUDDY_KEY_COLOR]) self._dbus_helper.PropertyChanged([_BUDDY_KEY_COLOR])
@ -343,8 +356,8 @@ class Buddy(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): def __init__(self, ps, bus_name, object_id, icon_cache):
Buddy.__init__(self, bus_name, object_id, None) Buddy.__init__(self, bus_name, object_id, None, icon_cache)
self._nick_name = env.get_nick_name() self._nick_name = env.get_nick_name()
self._color = env.get_color() self._color = env.get_color()
self._ps = ps self._ps = ps

View File

@ -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

View File

@ -10,6 +10,7 @@ sugar_PYTHON = \
__init__.py \ __init__.py \
Activity.py \ Activity.py \
Buddy.py \ Buddy.py \
BuddyIconCache.py \
PresenceService.py \ PresenceService.py \
Service.py Service.py

View File

@ -5,6 +5,7 @@ import Activity
import random import random
import logging import logging
from sugar import util from sugar import util
import BuddyIconCache
_SA_UNRESOLVED = 0 _SA_UNRESOLVED = 0
@ -302,9 +303,11 @@ class PresenceService(object):
self._bus_name = dbus.service.BusName(_PRESENCE_SERVICE, bus=self._session_bus) self._bus_name = dbus.service.BusName(_PRESENCE_SERVICE, bus=self._session_bus)
self._dbus_helper = PresenceServiceDBusHelper(self, self._bus_name) self._dbus_helper = PresenceServiceDBusHelper(self, self._bus_name)
self._icon_cache = BuddyIconCache.BuddyIconCache()
# Our owner object # Our owner object
objid = self._get_next_object_id() 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._buddies[self._owner.get_name()] = self._owner
self._started = False self._started = False
@ -423,7 +426,7 @@ class PresenceService(object):
except KeyError: except KeyError:
source_addr = service.get_source_address() source_addr = service.get_source_address()
objid = self._get_next_object_id() 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._buddies[name] = buddy
self._dbus_helper.ServiceAppeared(service.object_path()) self._dbus_helper.ServiceAppeared(service.object_path())
if not buddy_was_valid and buddy.is_valid(): if not buddy_was_valid and buddy.is_valid():

View File

@ -8,6 +8,7 @@ from sugar import env
import logging import logging
from sugar.p2p import Stream from sugar.p2p import Stream
from sugar.presence import PresenceService from sugar.presence import PresenceService
from sugar import util
from model.Invites import Invites from model.Invites import Invites
import dbus import dbus
@ -24,11 +25,17 @@ class ShellOwner(object):
user_dir = profile.get_path() user_dir = profile.get_path()
self._icon = None self._icon = None
self._icon_hash = ""
for fname in os.listdir(user_dir): for fname in os.listdir(user_dir):
if not fname.startswith("buddy-icon."): if not fname.startswith("buddy-icon."):
continue continue
fd = open(os.path.join(user_dir, fname), "r") fd = open(os.path.join(user_dir, fname), "r")
self._icon = fd.read() 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() fd.close()
break break
@ -49,7 +56,7 @@ class ShellOwner(object):
def announce(self): def announce(self):
# Create and announce our presence # Create and announce our presence
color = conf.get_profile().get_color() 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, self._service = self._pservice.register_service(self._nick,
PRESENCE_SERVICE_TYPE, properties=props) PRESENCE_SERVICE_TYPE, properties=props)
logging.debug("Owner '%s' using port %d" % (self._nick, self._service.get_port())) logging.debug("Owner '%s' using port %d" % (self._nick, self._service.get_port()))