diff options
Diffstat (limited to 'record.py')
-rw-r--r-- | record.py | 906 |
1 files changed, 462 insertions, 444 deletions
@@ -27,17 +27,21 @@ import telepathy.client import logging import xml.dom.minidom import time +from xml.dom.minidom import parse + +import logging +logger = logging.getLogger('record:record.py') from sugar.activity import activity from sugar.presence import presenceservice from sugar.presence.tubeconn import TubeConnection from sugar import util +import port.json from model import Model from ui import UI from recordtube import RecordTube from glive import Glive -from glivex import GliveX from gplay import Gplay from greplay import Greplay from recorded import Recorded @@ -49,472 +53,486 @@ import utils class Record(activity.Activity): - log = logging.getLogger('record-activity') + log = logging.getLogger('record-activity') + + def __init__(self, handle): + activity.Activity.__init__(self, handle) + #flags for controlling the writing to the datastore + self.I_AM_CLOSING = False + self.I_AM_SAVED = False + + self.props.enable_fullscreen_mode = False + Instance(self) + Constants(self) + self.modify_bg( gtk.STATE_NORMAL, Constants.colorBlack.gColor ) + + #wait a moment so that our debug console capture mistakes + gobject.idle_add( self._initme, None ) + + + def _initme( self, userdata=None ): + #totally tubular + self.meshTimeoutTime = 10000 + self.recTube = None + self.connect( "shared", self._sharedCb ) + + #the main classes + self.m = Model(self) + self.glive = Glive(self) + self.gplay = Gplay(self) + self.ui = UI(self) + + #CSCL + if self._shared_activity: + #have you joined or shared this activity yourself? + if self.get_shared(): + self._meshJoinedCb( self ) + else: + self.connect("joined", self._meshJoinedCb) + + return False + + + def read_file(self, file): + try: + dom = parse(file) + except Exception, e: + logger.error('read_file: %s' % e) + return + + serialize.fillMediaHash(dom, self.m.mediaHashs) + + for i in dom.documentElement.getElementsByTagName('ui'): + for ui_el in i.childNodes: + self.ui.deserialize(port.json.loads(ui_el.data)) + + + def write_file(self, file): + self.I_AM_SAVED = False + + self.m.mediaHashs['ui'] = self.ui.serialize() - def __init__(self, handle): - activity.Activity.__init__(self, handle) - #flags for controlling the writing to the datastore - self.I_AM_CLOSING = False - self.I_AM_SAVED = False + dom = serialize.saveMediaHash(self.m.mediaHashs) - self.props.enable_fullscreen_mode = False - Instance(self) - Constants(self) - self.modify_bg( gtk.STATE_NORMAL, Constants.colorBlack.gColor ) + ui_data = port.json.dumps(self.ui.serialize()) + ui_el = dom.createElement('ui') + ui_el.appendChild(dom.createTextNode(ui_data)) + dom.documentElement.appendChild(ui_el) - #wait a moment so that our debug console capture mistakes - gobject.idle_add( self._initme, None ) + xmlFile = open( file, "w" ) + dom.writexml(xmlFile) + xmlFile.close() + allDone = True + for h in range (0, len(self.m.mediaHashs)-1): + mhash = self.m.mediaHashs[h] + for i in range (0, len(mhash)): + recd = mhash[i] - def _initme( self, userdata=None ): - #totally tubular - self.meshTimeoutTime = 10000 - self.recTube = None - self.connect( "shared", self._sharedCb ) + if ( (not recd.savedMedia) or (not recd.savedXml) ): + allDone = False - #the main classes - self.m = Model(self) - self.glive = Glive(self) - self.glivex = GliveX(self) - self.gplay = Gplay() - self.ui = UI(self) + if (self.I_AM_CLOSING): + mediaObject = recd.datastoreOb + if (mediaObject != None): + recd.datastoreOb = None + mediaObject.destroy() + del mediaObject - #CSCL - if self._shared_activity: - #have you joined or shared this activity yourself? - if self.get_shared(): - self._meshJoinedCb( self ) - else: - self.connect("joined", self._meshJoinedCb) + self.I_AM_SAVED = True + if (self.I_AM_SAVED and self.I_AM_CLOSING): + self.destroy() - return False + def stopPipes(self): + self.ui.doMouseListener( False ) + self.m.setUpdating( False ) - def read_file(self, file): - serialize.fillMediaHash(file, self.m.mediaHashs) + if (self.ui.COUNTINGDOWN): + self.m.abandonRecording() + elif (self.m.RECORDING): + self.m.doShutter() + else: + self.glive.stop() - def write_file(self, file): - self.I_AM_SAVED = False + def restartPipes(self): + if (not self.ui.TRANSCODING): + self.ui.updateModeChange( ) + self.ui.doMouseListener( True ) - dom = serialize.saveMediaHash(self.m.mediaHashs) - xmlFile = open( file, "w" ) - dom.writexml(xmlFile) - xmlFile.close() - allDone = True - for h in range (0, len(self.m.mediaHashs)): - mhash = self.m.mediaHashs[h] - for i in range (0, len(mhash)): - recd = mhash[i] + def close( self ): + self.I_AM_CLOSING = True - if ( (not recd.savedMedia) or (not recd.savedXml) ): - allDone = False + self.m.UPDATING = False + if (self.ui != None): + self.ui.updateButtonSensitivities( ) + self.ui.doMouseListener( False ) + self.ui.hideAllWindows() + if (self.gplay != None): + self.gplay.stop( ) + if (self.glive != None): + self.glive.stop( ) - if (self.I_AM_CLOSING): - mediaObject = recd.datastoreOb - if (mediaObject != None): - recd.datastoreOb = None - mediaObject.destroy() - del mediaObject + #this calls write_file + activity.Activity.close( self ) - self.I_AM_SAVED = True - if (self.I_AM_SAVED and self.I_AM_CLOSING): - self.destroy() + def destroy( self ): + if self.I_AM_SAVED: + activity.Activity.destroy( self ) - def stopPipes(self): - self.ui.doMouseListener( False ) - self.m.setUpdating( False ) - if (self.ui.COUNTINGDOWN): - self.m.abandonRecording() - elif (self.m.RECORDING): - self.m.doShutter() - else: - self.glive.stop() - self.glivex.stop() + def _sharedCb( self, activity ): + self._setup() + id = self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].OfferDBusTube( Constants.SERVICE, {}) - def restartPipes(self): - if (not self.ui.TRANSCODING): - self.ui.updateModeChange( ) - self.ui.doMouseListener( True ) + def _meshJoinedCb( self, activity ): + if not self._shared_activity: + return + + self._setup() - def close( self ): - self.I_AM_CLOSING = True + self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].ListTubes( reply_handler=self._list_tubes_reply_cb, error_handler=self._list_tubes_error_cb) - self.m.UPDATING = False - if (self.ui != None): - self.ui.updateButtonSensitivities( ) - self.ui.doMouseListener( False ) - self.ui.hideAllWindows() - if (self.gplay != None): - self.gplay.stop( ) - if (self.glive != None): - self.glive.stop( ) - if (self.glivex != None): - self.glivex.stop( ) - #this calls write_file - activity.Activity.close( self ) - - - def destroy( self ): - if self.I_AM_SAVED: - activity.Activity.destroy( self ) - - - def _sharedCb( self, activity ): - self._setup() - - id = self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].OfferDBusTube( Constants.SERVICE, {}) - - - def _meshJoinedCb( self, activity ): - if not self._shared_activity: - return - - self._setup() - - self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].ListTubes( reply_handler=self._list_tubes_reply_cb, error_handler=self._list_tubes_error_cb) - - - def _list_tubes_reply_cb(self, tubes): - for tube_info in tubes: - self._newTubeCb(*tube_info) - - - def _list_tubes_error_cb(self, e): - self.__class__.log.error('ListTubes() failed: %s', e) - - - def _setup(self): - #sets up the tubes... - if self._shared_activity is None: - self.__class__.log.error('_setup: Failed to share or join activity') - return - - pservice = presenceservice.get_instance() - try: - name, path = pservice.get_preferred_connection() - self.conn = telepathy.client.Connection(name, path) - except: - self.__class__.log.error('_setup: Failed to get_preferred_connection') - - # Work out what our room is called and whether we have Tubes already - bus_name, conn_path, channel_paths = self._shared_activity.get_channels() - room = None - tubes_chan = None - text_chan = None - for channel_path in channel_paths: - channel = telepathy.client.Channel(bus_name, channel_path) - htype, handle = channel.GetHandle() - if htype == telepathy.HANDLE_TYPE_ROOM: - self.__class__.log.debug('Found our room: it has handle#%d "%s"', handle, self.conn.InspectHandles(htype, [handle])[0]) - room = handle - ctype = channel.GetChannelType() - if ctype == telepathy.CHANNEL_TYPE_TUBES: - self.__class__.log.debug('Found our Tubes channel at %s', channel_path) - tubes_chan = channel - elif ctype == telepathy.CHANNEL_TYPE_TEXT: - self.__class__.log.debug('Found our Text channel at %s', channel_path) - text_chan = channel - - if room is None: - self.__class__.log.error("Presence service didn't create a room") - return - if text_chan is None: - self.__class__.log.error("Presence service didn't create a text channel") - return - - # Make sure we have a Tubes channel - PS doesn't yet provide one - if tubes_chan is None: - self.__class__.log.debug("Didn't find our Tubes channel, requesting one...") - tubes_chan = self.conn.request_channel(telepathy.CHANNEL_TYPE_TUBES, telepathy.HANDLE_TYPE_ROOM, room, True) - - self.tubes_chan = tubes_chan - self.text_chan = text_chan - - tubes_chan[telepathy.CHANNEL_TYPE_TUBES].connect_to_signal('NewTube', self._newTubeCb) - - - def _newTubeCb(self, id, initiator, type, service, params, state): - self.__class__.log.debug('New tube: ID=%d initator=%d type=%d service=%s params=%r state=%d', id, initiator, type, service, params, state) - if (type == telepathy.TUBE_TYPE_DBUS and service == Constants.SERVICE): - if state == telepathy.TUBE_STATE_LOCAL_PENDING: - self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].AcceptDBusTube(id) - tube_conn = TubeConnection(self.conn, self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES], id, group_iface=self.text_chan[telepathy.CHANNEL_INTERFACE_GROUP]) - self.recTube = RecordTube(tube_conn) - self.recTube.connect("new-recd", self._newRecdCb) - self.recTube.connect("recd-request", self._recdRequestCb) - self.recTube.connect("recd-bits-arrived", self._recdBitsArrivedCb) - self.recTube.connect("recd-unavailable", self._recdUnavailableCb) - - - def _newRecdCb( self, objectThatSentTheSignal, recorder, xmlString ): - self.__class__.log.debug('_newRecdCb') - dom = None - try: - dom = xml.dom.minidom.parseString(xmlString) - except: - self.__class__.log.error('Unable to parse mesh xml') - if (dom == None): - return - - recd = Recorded() - recd = serialize.fillRecdFromNode(recd, dom.documentElement) - if (recd != None): - self.__class__.log.debug('_newRecdCb: adding new recd thumb') - recd.buddy = True - recd.downloadedFromBuddy = False - self.m.addMeshRecd( recd ) - else: - self.__class__.log.debug('_newRecdCb: recd is None. Unable to parse XML') - - - def requestMeshDownload( self, recd ): - if (recd.meshDownloading): - return True - - self.m.updateXoFullStatus() - if (self.m.FULL): - return True - - #this call will get the bits or request the bits if they're not available - if (recd.buddy and (not recd.downloadedFromBuddy)): - self.meshInitRoundRobin(recd) - return True - - else: - return False - - - def meshInitRoundRobin( self, recd ): - if (recd.meshDownloading): - self.__class__.log.debug("meshInitRoundRobin: we are in midst of downloading this file...") - return - - if (self.recTube == None): - gobject.idle_add(self.ui.updateMeshProgress, False, recd) - return - - #start with who took the photo - recd.triedMeshBuddies = [] - recd.triedMeshBuddies.append(Instance.keyHashPrintable) - self.meshReqRecFromBuddy( recd, recd.recorderHash, recd.recorderName ) - - - def meshNextRoundRobinBuddy( self, recd ): - self.__class__.log.debug('meshNextRoundRobinBuddy') - if (recd.meshReqCallbackId != 0): - gobject.source_remove(recd.meshReqCallbackId) - recd.meshReqCallbackId = 0 - - #delete any stub of a partially downloaded file - filepath = recd.getMediaFilepath() - if (filepath != None): - if (os.path.exists(filepath)): - os.remove( filepath ) - - goodBudObj = None - buds = self._shared_activity.get_joined_buddies() - for i in range (0, len(buds)): - nextBudObj = buds[i] - nextBud = util._sha_data(nextBudObj.props.key) - nextBud = util.printable_hash(nextBud) - if (recd.triedMeshBuddies.count(nextBud) > 0): - self.__class__.log.debug('mnrrb: weve already tried bud ' + str(nextBudObj.props.nick)) - else: - self.__class__.log.debug('mnrrb: ask next buddy: ' + str(nextBudObj.props.nick)) - goodBudObj = nextBudObj - break - - if (goodBudObj != None): - goodNick = goodBudObj.props.nick - goodBud = util._sha_data(goodBudObj.props.key) - goodBud = util.printable_hash(goodBud) - self.meshReqRecFromBuddy(recd, goodBud, goodNick) - else: - self.__class__.log.debug('weve tried all buddies here, and no one has this recd') - recd.meshDownloading = False - recd.triedMeshBuddies = [] - recd.triedMeshBuddies.append(Instance.keyHashPrintable) - self.ui.updateMeshProgress(False, recd) - - - def meshReqRecFromBuddy( self, recd, fromWho, fromWhosNick ): - recd.triedMeshBuddies.append( fromWho ) - recd.meshDownloadingFrom = fromWho - recd.meshDownloadingFromNick = fromWhosNick - recd.meshDownloadingProgress = False - recd.meshDownloading = True - recd.meshDownlodingPercent = 0.0 - self.ui.updateMeshProgress(True, recd) - recd.meshReqCallbackId = gobject.timeout_add(self.meshTimeoutTime, self._meshCheckOnRecdRequest, recd) - self.recTube.requestRecdBits( Instance.keyHashPrintable, fromWho, recd.mediaMd5 ) - - - def _meshCheckOnRecdRequest( self, recdRequesting ): - #todo: add category for "not active activity, so go ahead and delete" - - if (recdRequesting.downloadedFromBuddy): - self.__class__.log.debug('_meshCheckOnRecdRequest: recdRequesting.downloadedFromBuddy') - if (recdRequesting.meshReqCallbackId != 0): - gobject.source_remove(recdRequesting.meshReqCallbackId) - recdRequesting.meshReqCallbackId = 0 - return False - if (recdRequesting.deleted): - self.__class__.log.debug('_meshCheckOnRecdRequest: recdRequesting.deleted') - if (recdRequesting.meshReqCallbackId != 0): - gobject.source_remove(recdRequesting.meshReqCallbackId) - recdRequesting.meshReqCallbackId = 0 - return False - if (recdRequesting.meshDownloadingProgress): - self.__class__.log.debug('_meshCheckOnRecdRequest: recdRequesting.meshDownloadingProgress') - #we've received some bits since last we checked, so keep waiting... they'll all get here eventually! - recdRequesting.meshDownloadingProgress = False - return True - else: - self.__class__.log.debug('_meshCheckOnRecdRequest: ! recdRequesting.meshDownloadingProgress') - #that buddy we asked info from isn't responding; next buddy! - #self.meshNextRoundRobinBuddy( recdRequesting ) - gobject.idle_add(self.meshNextRoundRobinBuddy, recdRequesting) - return False - - - def _recdRequestCb( self, objectThatSentTheSignal, whoWantsIt, md5sumOfIt ): - #if we are here, it is because someone has been told we have what they want. - #we need to send them that thing, whatever that thing is - recd = self.m.getRecdByMd5( md5sumOfIt ) - if (recd == None): - self.__class__.log.debug('_recdRequestCb: we dont have the recd they asked for') - self.recTube.unavailableRecd(md5sumOfIt, Instance.keyHashPrintable, whoWantsIt) - return - if (recd.deleted): - self.__class__.log.debug('_recdRequestCb: we have the recd, but it has been deleted, so we wont share') - self.recTube.unavailableRecd(md5sumOfIt, Instance.keyHashPrintable, whoWantsIt) - return - if (recd.buddy and not recd.downloadedFromBuddy): - self.__class__.log.debug('_recdRequestCb: we have an incomplete recd, so we wont share') - self.recTube.unavailableRecd(md5sumOfIt, Instance.keyHashPrintable, whoWantsIt) - return - - recd.meshUploading = True - filepath = recd.getMediaFilepath() - - if (recd.type == Constants.TYPE_AUDIO): - audioImgFilepath = recd.getAudioImageFilepath() - - destPath = os.path.join(Instance.instancePath, "audioBundle") - destPath = utils.getUniqueFilepath(destPath, 0) - cmd = "cat " + str(filepath) + " " + str(audioImgFilepath) + " > " + str(destPath) - self.__class__.log.debug(cmd) - os.system(cmd) - filepath = destPath - - sent = self.recTube.broadcastRecd(recd.mediaMd5, filepath, whoWantsIt) - recd.meshUploading = False - #if you were deleted while uploading, now throw away those bits now - if (recd.deleted): - recd.doDeleteRecorded(recd) - - - def _recdBitsArrivedCb( self, objectThatSentTheSignal, md5sumOfIt, part, numparts, bytes, fromWho ): - #self.__class__.log.debug('_recdBitsArrivedCb: ' + str(part) + "/" + str(numparts)) - recd = self.m.getRecdByMd5( md5sumOfIt ) - if (recd == None): - self.__class__.log.debug('_recdBitsArrivedCb: thx 4 yr bits, but we dont even have that photo') - return - if (recd.deleted): - self.__class__.log.debug('_recdBitsArrivedCb: thx 4 yr bits, but we deleted that photo') - return - if (recd.downloadedFromBuddy): - self.__class__.log.debug('_recdBitsArrivedCb: weve already downloadedFromBuddy') - return - if (not recd.buddy): - self.__class__.log.debug('_recdBitsArrivedCb: uh, we took this photo, so dont need your bits') - return - if (recd.meshDownloadingFrom != fromWho): - self.__class__.log.debug('_recdBitsArrivedCb: wrong bits ' + str(fromWho) + ", exp:" + str(recd.meshDownloadingFrom)) - return - - #update that we've heard back about this, reset the timeout - gobject.source_remove(recd.meshReqCallbackId) - recd.meshReqCallbackId = gobject.timeout_add(self.meshTimeoutTime, self._meshCheckOnRecdRequest, recd) - - #update the progress bar - recd.meshDownlodingPercent = (part+0.0)/(numparts+0.0) - recd.meshDownloadingProgress = True - self.ui.updateMeshProgress(True, recd) - f = open(recd.getMediaFilepath(), 'a+') - f.write(bytes) - f.close() - - if part == numparts: - self.__class__.log.debug('Finished receiving %s' % recd.title) - gobject.source_remove( recd.meshReqCallbackId ) - recd.meshReqCallbackId = 0 - recd.meshDownloading = False - recd.meshDownlodingPercent = 1.0 - recd.downloadedFromBuddy = True - if (recd.type == Constants.TYPE_AUDIO): - filepath = recd.getMediaFilepath() - bundlePath = os.path.join(Instance.instancePath, "audioBundle") - bundlePath = utils.getUniqueFilepath(bundlePath, 0) - - cmd = "split -a 1 -b " + str(recd.mediaBytes) + " " + str(filepath) + " " + str(bundlePath) - self.__class__.log.debug( cmd ) - os.system( cmd ) - - bundleName = os.path.basename(bundlePath) - mediaFilename = str(bundleName) + "a" - mediaFilepath = os.path.join(Instance.instancePath, mediaFilename) - mediaFilepathExt = os.path.join(Instance.instancePath, mediaFilename+".ogg") - os.rename(mediaFilepath, mediaFilepathExt) - audioImageFilename = str(bundleName) + "b" - audioImageFilepath = os.path.join(Instance.instancePath, audioImageFilename) - audioImageFilepathExt = os.path.join(Instance.instancePath, audioImageFilename+".png") - os.rename(audioImageFilepath, audioImageFilepathExt) - - recd.mediaFilename = os.path.basename(mediaFilepathExt) - recd.audioImageFilename = os.path.basename(audioImageFilepathExt) - - self.ui.showMeshRecd( recd ) - elif part > numparts: - self.__class__.log.error('More parts than required have arrived') - - - def _getAlbumArtCb( self, objectThatSentTheSignal, pixbuf, recd ): - - if (pixbuf != None): - imagePath = os.path.join(Instance.instancePath, "audioPicture.png") - imagePath = utils.getUniqueFilepath( imagePath, 0 ) - pixbuf.save( imagePath, "png", {} ) - recd.audioImageFilename = os.path.basename(imagePath) - - self.ui.showMeshRecd( recd ) - return False - - - def _recdUnavailableCb( self, objectThatSentTheSignal, md5sumOfIt, whoDoesntHaveIt ): - self.__class__.log.debug('_recdUnavailableCb: sux, we want to see that photo') - recd = self.m.getRecdByMd5( md5sumOfIt ) - if (recd == None): - self.__class__.log.debug('_recdUnavailableCb: actually, we dont even know about that one..') - return - if (recd.deleted): - self.__class__.log.debug('_recdUnavailableCb: actually, since we asked, we deleted.') - return - if (not recd.buddy): - self.__class__.log.debug('_recdUnavailableCb: uh, odd, we took that photo and have it already.') - return - if (recd.downloadedFromBuddy): - self.__class__.log.debug('_recdUnavailableCb: we already downloaded it... you might have been slow responding.') - return - if (recd.meshDownloadingFrom != whoDoesntHaveIt): - self.__class__.log.debug('_recdUnavailableCb: we arent asking you for a copy now. slow response, pbly.') - return - - #self.meshNextRoundRobinBuddy( recd ) + def _list_tubes_reply_cb(self, tubes): + for tube_info in tubes: + self._newTubeCb(*tube_info) + + + def _list_tubes_error_cb(self, e): + self.__class__.log.error('ListTubes() failed: %s', e) + + + def _setup(self): + #sets up the tubes... + if self._shared_activity is None: + self.__class__.log.error('_setup: Failed to share or join activity') + return + + pservice = presenceservice.get_instance() + try: + name, path = pservice.get_preferred_connection() + self.conn = telepathy.client.Connection(name, path) + except: + self.__class__.log.error('_setup: Failed to get_preferred_connection') + + # Work out what our room is called and whether we have Tubes already + bus_name, conn_path, channel_paths = self._shared_activity.get_channels() + room = None + tubes_chan = None + text_chan = None + for channel_path in channel_paths: + channel = telepathy.client.Channel(bus_name, channel_path) + htype, handle = channel.GetHandle() + if htype == telepathy.HANDLE_TYPE_ROOM: + self.__class__.log.debug('Found our room: it has handle#%d "%s"', handle, self.conn.InspectHandles(htype, [handle])[0]) + room = handle + ctype = channel.GetChannelType() + if ctype == telepathy.CHANNEL_TYPE_TUBES: + self.__class__.log.debug('Found our Tubes channel at %s', channel_path) + tubes_chan = channel + elif ctype == telepathy.CHANNEL_TYPE_TEXT: + self.__class__.log.debug('Found our Text channel at %s', channel_path) + text_chan = channel + + if room is None: + self.__class__.log.error("Presence service didn't create a room") + return + if text_chan is None: + self.__class__.log.error("Presence service didn't create a text channel") + return + + # Make sure we have a Tubes channel - PS doesn't yet provide one + if tubes_chan is None: + self.__class__.log.debug("Didn't find our Tubes channel, requesting one...") + tubes_chan = self.conn.request_channel(telepathy.CHANNEL_TYPE_TUBES, telepathy.HANDLE_TYPE_ROOM, room, True) + + self.tubes_chan = tubes_chan + self.text_chan = text_chan + + tubes_chan[telepathy.CHANNEL_TYPE_TUBES].connect_to_signal('NewTube', self._newTubeCb) + + + def _newTubeCb(self, id, initiator, type, service, params, state): + self.__class__.log.debug('New tube: ID=%d initator=%d type=%d service=%s params=%r state=%d', id, initiator, type, service, params, state) + if (type == telepathy.TUBE_TYPE_DBUS and service == Constants.SERVICE): + if state == telepathy.TUBE_STATE_LOCAL_PENDING: + self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].AcceptDBusTube(id) + tube_conn = TubeConnection(self.conn, self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES], id, group_iface=self.text_chan[telepathy.CHANNEL_INTERFACE_GROUP]) + self.recTube = RecordTube(tube_conn) + self.recTube.connect("new-recd", self._newRecdCb) + self.recTube.connect("recd-request", self._recdRequestCb) + self.recTube.connect("recd-bits-arrived", self._recdBitsArrivedCb) + self.recTube.connect("recd-unavailable", self._recdUnavailableCb) + + + def _newRecdCb( self, objectThatSentTheSignal, recorder, xmlString ): + self.__class__.log.debug('_newRecdCb') + dom = None + try: + dom = xml.dom.minidom.parseString(xmlString) + except: + self.__class__.log.error('Unable to parse mesh xml') + if (dom == None): + return + + recd = Recorded() + recd = serialize.fillRecdFromNode(recd, dom.documentElement) + if (recd != None): + self.__class__.log.debug('_newRecdCb: adding new recd thumb') + recd.buddy = True + recd.downloadedFromBuddy = False + self.m.addMeshRecd( recd ) + else: + self.__class__.log.debug('_newRecdCb: recd is None. Unable to parse XML') + + + def requestMeshDownload( self, recd ): + if (recd.meshDownloading): + return True + + self.m.updateXoFullStatus() + if (self.m.FULL): + return True + + #this call will get the bits or request the bits if they're not available + if (recd.buddy and (not recd.downloadedFromBuddy)): + self.meshInitRoundRobin(recd) + return True + + else: + return False + + + def meshInitRoundRobin( self, recd ): + if (recd.meshDownloading): + self.__class__.log.debug("meshInitRoundRobin: we are in midst of downloading this file...") + return + + if (self.recTube == None): + gobject.idle_add(self.ui.updateMeshProgress, False, recd) + return + + #start with who took the photo + recd.triedMeshBuddies = [] + recd.triedMeshBuddies.append(Instance.keyHashPrintable) + self.meshReqRecFromBuddy( recd, recd.recorderHash, recd.recorderName ) + + + def meshNextRoundRobinBuddy( self, recd ): + self.__class__.log.debug('meshNextRoundRobinBuddy') + if (recd.meshReqCallbackId != 0): + gobject.source_remove(recd.meshReqCallbackId) + recd.meshReqCallbackId = 0 + + #delete any stub of a partially downloaded file + filepath = recd.getMediaFilepath() + if (filepath != None): + if (os.path.exists(filepath)): + os.remove( filepath ) + + goodBudObj = None + buds = self._shared_activity.get_joined_buddies() + for i in range (0, len(buds)): + nextBudObj = buds[i] + nextBud = util._sha_data(nextBudObj.props.key) + nextBud = util.printable_hash(nextBud) + if (recd.triedMeshBuddies.count(nextBud) > 0): + self.__class__.log.debug('mnrrb: weve already tried bud ' + str(nextBudObj.props.nick)) + else: + self.__class__.log.debug('mnrrb: ask next buddy: ' + str(nextBudObj.props.nick)) + goodBudObj = nextBudObj + break + + if (goodBudObj != None): + goodNick = goodBudObj.props.nick + goodBud = util._sha_data(goodBudObj.props.key) + goodBud = util.printable_hash(goodBud) + self.meshReqRecFromBuddy(recd, goodBud, goodNick) + else: + self.__class__.log.debug('weve tried all buddies here, and no one has this recd') + recd.meshDownloading = False + recd.triedMeshBuddies = [] + recd.triedMeshBuddies.append(Instance.keyHashPrintable) + self.ui.updateMeshProgress(False, recd) + + + def meshReqRecFromBuddy( self, recd, fromWho, fromWhosNick ): + recd.triedMeshBuddies.append( fromWho ) + recd.meshDownloadingFrom = fromWho + recd.meshDownloadingFromNick = fromWhosNick + recd.meshDownloadingProgress = False + recd.meshDownloading = True + recd.meshDownlodingPercent = 0.0 + self.ui.updateMeshProgress(True, recd) + recd.meshReqCallbackId = gobject.timeout_add(self.meshTimeoutTime, self._meshCheckOnRecdRequest, recd) + self.recTube.requestRecdBits( Instance.keyHashPrintable, fromWho, recd.mediaMd5 ) + + + def _meshCheckOnRecdRequest( self, recdRequesting ): + #todo: add category for "not active activity, so go ahead and delete" + + if (recdRequesting.downloadedFromBuddy): + self.__class__.log.debug('_meshCheckOnRecdRequest: recdRequesting.downloadedFromBuddy') + if (recdRequesting.meshReqCallbackId != 0): + gobject.source_remove(recdRequesting.meshReqCallbackId) + recdRequesting.meshReqCallbackId = 0 + return False + if (recdRequesting.deleted): + self.__class__.log.debug('_meshCheckOnRecdRequest: recdRequesting.deleted') + if (recdRequesting.meshReqCallbackId != 0): + gobject.source_remove(recdRequesting.meshReqCallbackId) + recdRequesting.meshReqCallbackId = 0 + return False + if (recdRequesting.meshDownloadingProgress): + self.__class__.log.debug('_meshCheckOnRecdRequest: recdRequesting.meshDownloadingProgress') + #we've received some bits since last we checked, so keep waiting... they'll all get here eventually! + recdRequesting.meshDownloadingProgress = False + return True + else: + self.__class__.log.debug('_meshCheckOnRecdRequest: ! recdRequesting.meshDownloadingProgress') + #that buddy we asked info from isn't responding; next buddy! + #self.meshNextRoundRobinBuddy( recdRequesting ) + gobject.idle_add(self.meshNextRoundRobinBuddy, recdRequesting) + return False + + + def _recdRequestCb( self, objectThatSentTheSignal, whoWantsIt, md5sumOfIt ): + #if we are here, it is because someone has been told we have what they want. + #we need to send them that thing, whatever that thing is + recd = self.m.getRecdByMd5( md5sumOfIt ) + if (recd == None): + self.__class__.log.debug('_recdRequestCb: we dont have the recd they asked for') + self.recTube.unavailableRecd(md5sumOfIt, Instance.keyHashPrintable, whoWantsIt) + return + if (recd.deleted): + self.__class__.log.debug('_recdRequestCb: we have the recd, but it has been deleted, so we wont share') + self.recTube.unavailableRecd(md5sumOfIt, Instance.keyHashPrintable, whoWantsIt) + return + if (recd.buddy and not recd.downloadedFromBuddy): + self.__class__.log.debug('_recdRequestCb: we have an incomplete recd, so we wont share') + self.recTube.unavailableRecd(md5sumOfIt, Instance.keyHashPrintable, whoWantsIt) + return + + recd.meshUploading = True + filepath = recd.getMediaFilepath() + + if (recd.type == Constants.TYPE_AUDIO): + audioImgFilepath = recd.getAudioImageFilepath() + + destPath = os.path.join(Instance.instancePath, "audioBundle") + destPath = utils.getUniqueFilepath(destPath, 0) + cmd = "cat " + str(filepath) + " " + str(audioImgFilepath) + " > " + str(destPath) + self.__class__.log.debug(cmd) + os.system(cmd) + filepath = destPath + + sent = self.recTube.broadcastRecd(recd.mediaMd5, filepath, whoWantsIt) + recd.meshUploading = False + #if you were deleted while uploading, now throw away those bits now + if (recd.deleted): + recd.doDeleteRecorded(recd) + + + def _recdBitsArrivedCb( self, objectThatSentTheSignal, md5sumOfIt, part, numparts, bytes, fromWho ): + #self.__class__.log.debug('_recdBitsArrivedCb: ' + str(part) + "/" + str(numparts)) + recd = self.m.getRecdByMd5( md5sumOfIt ) + if (recd == None): + self.__class__.log.debug('_recdBitsArrivedCb: thx 4 yr bits, but we dont even have that photo') + return + if (recd.deleted): + self.__class__.log.debug('_recdBitsArrivedCb: thx 4 yr bits, but we deleted that photo') + return + if (recd.downloadedFromBuddy): + self.__class__.log.debug('_recdBitsArrivedCb: weve already downloadedFromBuddy') + return + if (not recd.buddy): + self.__class__.log.debug('_recdBitsArrivedCb: uh, we took this photo, so dont need your bits') + return + if (recd.meshDownloadingFrom != fromWho): + self.__class__.log.debug('_recdBitsArrivedCb: wrong bits ' + str(fromWho) + ", exp:" + str(recd.meshDownloadingFrom)) + return + + #update that we've heard back about this, reset the timeout + gobject.source_remove(recd.meshReqCallbackId) + recd.meshReqCallbackId = gobject.timeout_add(self.meshTimeoutTime, self._meshCheckOnRecdRequest, recd) + + #update the progress bar + recd.meshDownlodingPercent = (part+0.0)/(numparts+0.0) + recd.meshDownloadingProgress = True + self.ui.updateMeshProgress(True, recd) + f = open(recd.getMediaFilepath(), 'a+') + f.write(bytes) + f.close() + + if part == numparts: + self.__class__.log.debug('Finished receiving %s' % recd.title) + gobject.source_remove( recd.meshReqCallbackId ) + recd.meshReqCallbackId = 0 + recd.meshDownloading = False + recd.meshDownlodingPercent = 1.0 + recd.downloadedFromBuddy = True + if (recd.type == Constants.TYPE_AUDIO): + filepath = recd.getMediaFilepath() + bundlePath = os.path.join(Instance.instancePath, "audioBundle") + bundlePath = utils.getUniqueFilepath(bundlePath, 0) + + cmd = "split -a 1 -b " + str(recd.mediaBytes) + " " + str(filepath) + " " + str(bundlePath) + self.__class__.log.debug( cmd ) + os.system( cmd ) + + bundleName = os.path.basename(bundlePath) + mediaFilename = str(bundleName) + "a" + mediaFilepath = os.path.join(Instance.instancePath, mediaFilename) + mediaFilepathExt = os.path.join(Instance.instancePath, mediaFilename+".ogg") + os.rename(mediaFilepath, mediaFilepathExt) + audioImageFilename = str(bundleName) + "b" + audioImageFilepath = os.path.join(Instance.instancePath, audioImageFilename) + audioImageFilepathExt = os.path.join(Instance.instancePath, audioImageFilename+".png") + os.rename(audioImageFilepath, audioImageFilepathExt) + + recd.mediaFilename = os.path.basename(mediaFilepathExt) + recd.audioImageFilename = os.path.basename(audioImageFilepathExt) + + self.ui.showMeshRecd( recd ) + elif part > numparts: + self.__class__.log.error('More parts than required have arrived') + + + def _getAlbumArtCb( self, objectThatSentTheSignal, pixbuf, recd ): + + if (pixbuf != None): + imagePath = os.path.join(Instance.instancePath, "audioPicture.png") + imagePath = utils.getUniqueFilepath( imagePath, 0 ) + pixbuf.save( imagePath, "png", {} ) + recd.audioImageFilename = os.path.basename(imagePath) + + self.ui.showMeshRecd( recd ) + return False + + + def _recdUnavailableCb( self, objectThatSentTheSignal, md5sumOfIt, whoDoesntHaveIt ): + self.__class__.log.debug('_recdUnavailableCb: sux, we want to see that photo') + recd = self.m.getRecdByMd5( md5sumOfIt ) + if (recd == None): + self.__class__.log.debug('_recdUnavailableCb: actually, we dont even know about that one..') + return + if (recd.deleted): + self.__class__.log.debug('_recdUnavailableCb: actually, since we asked, we deleted.') + return + if (not recd.buddy): + self.__class__.log.debug('_recdUnavailableCb: uh, odd, we took that photo and have it already.') + return + if (recd.downloadedFromBuddy): + self.__class__.log.debug('_recdUnavailableCb: we already downloaded it... you might have been slow responding.') + return + if (recd.meshDownloadingFrom != whoDoesntHaveIt): + self.__class__.log.debug('_recdUnavailableCb: we arent asking you for a copy now. slow response, pbly.') + return + + #self.meshNextRoundRobinBuddy( recd ) |