From eeddab830f8c011dcb7309e197c879941291efc1 Mon Sep 17 00:00:00 2001 From: SAMdroid Date: Fri, 06 Dec 2013 22:00:18 +0000 Subject: Added the ability to save media directly to external devices --- diff --git a/aplay.py b/aplay.py index fd6aaee..fd6aaee 100644..100755 --- a/aplay.py +++ b/aplay.py diff --git a/button.py b/button.py index 66cf80b..66cf80b 100644..100755 --- a/button.py +++ b/button.py diff --git a/collab.py b/collab.py index 34a0c21..34a0c21 100644..100755 --- a/collab.py +++ b/collab.py diff --git a/constants.py b/constants.py index 2e0f54c..2e0f54c 100644..100755 --- a/constants.py +++ b/constants.py diff --git a/glive.py b/glive.py index 9f8c5fa..9f8c5fa 100644..100755 --- a/glive.py +++ b/glive.py diff --git a/gplay.py b/gplay.py index 168fb22..168fb22 100644..100755 --- a/gplay.py +++ b/gplay.py diff --git a/hw.py b/hw.py index d678e37..d678e37 100644..100755 --- a/hw.py +++ b/hw.py diff --git a/iconcombobox.py b/iconcombobox.py index d5eb807..d5eb807 100644..100755 --- a/iconcombobox.py +++ b/iconcombobox.py diff --git a/instance.py b/instance.py index bcee466..afe69c7 100644..100755 --- a/instance.py +++ b/instance.py @@ -6,15 +6,20 @@ from sugar import util class Instance: key = profile.get_pubkey() keyHash = util.sha_data(key) - + keyHashPrintable = util.printable_hash(keyHash) - + instancePath = None + savePath = None def __init__(self, ca): self.__class__.instancePath = os.path.join(ca.get_activity_root(), "instance") recreateTmp() - + + def get_path(self): + if self.savePath: + return self.savePath + return self.instancePath def recreateTmp(): if (not os.path.exists(Instance.instancePath)): diff --git a/mediaview.py b/mediaview.py index 01567ff..01567ff 100644..100755 --- a/mediaview.py +++ b/mediaview.py diff --git a/model.py b/model.py index 1af2487..2cae17d 100644..100755 --- a/model.py +++ b/model.py @@ -26,6 +26,8 @@ import uuid import os import time import json +import shutil +import datetime import gobject import gst @@ -87,7 +89,7 @@ class Model: logger.error('read_file: %s' % e) return - serialize.fillMediaHash(dom, self.mediaHashs) + serialize.fillMediaHash(dom, self.mediaHashs, self.activity) for i in dom.documentElement.getElementsByTagName('ui'): for ui_el in i.childNodes: self.activity.deserialize(json.loads(ui_el.data)) @@ -277,7 +279,7 @@ class Model: def save_photo(self, pixbuf): recd = self.createNewRecorded(constants.TYPE_PHOTO) - imgpath = os.path.join(Instance.instancePath, recd.mediaFilename) + imgpath = os.path.join(self.activity.Instance.get_path(), recd.mediaFilename) pixbuf.save(imgpath, "jpeg") pixbuf = utils.generate_thumbnail(pixbuf) @@ -292,7 +294,7 @@ class Model: # called from gstreamer thread def save_video(self, path, still): recd = self.createNewRecorded(constants.TYPE_VIDEO) - os.rename(path, os.path.join(Instance.instancePath, recd.mediaFilename)) + shutil.move(path, os.path.join(self.activity.Instance.get_path(), recd.mediaFilename)) still = utils.generate_thumbnail(still) still.save(recd.make_thumb_path(), "png") @@ -304,10 +306,10 @@ class Model: def save_audio(self, path, still): recd = self.createNewRecorded(constants.TYPE_AUDIO) - os.rename(path, os.path.join(Instance.instancePath, recd.mediaFilename)) + shutil.move(path, os.path.join(self.activity.Instance.get_path(), recd.mediaFilename)) if still: - image_path = os.path.join(Instance.instancePath, "audioPicture.png") + image_path = os.path.join(self.activity.Instance.get_path(), "audioPicture.png") image_path = utils.getUniqueFilepath(image_path, 0) still.save(image_path, "png") recd.audioImageFilename = os.path.basename(image_path) @@ -363,29 +365,36 @@ class Model: return None def createNewRecorded(self, type): - recd = Recorded() + recd = Recorded(self.activity) recd.recorderName = self.get_nickname() - recd.recorderHash = Instance.keyHashPrintable + recd.recorderHash = self.activity.Instance.keyHashPrintable + + if self.activity.Instance.savePath: + recd.saveInternal = False #to create a file, use the hardware_id+time *and* check if available or not + recd.type = type nowtime = int(time.time()) recd.time = nowtime - recd.type = type - - mediaThumbFilename = str(recd.recorderHash) + "_" + str(recd.time) - mediaFilename = mediaThumbFilename - mediaFilename = mediaFilename + "." + constants.MEDIA_INFO[type]['ext'] - mediaFilepath = os.path.join( Instance.instancePath, mediaFilename ) - mediaFilepath = utils.getUniqueFilepath( mediaFilepath, 0 ) - recd.mediaFilename = os.path.basename( mediaFilepath ) - + stringType = constants.MEDIA_INFO[type]['istr'] # Translators: photo by photographer, e.g. "Photo by Mary" recd.title = _('%(type)s by %(name)s') % {'type': stringType, 'name': recd.recorderName} + mediaThumbFilename = utils.getUniqueFilepath(os.path.join( + recd.activity.Instance.get_path(), + recd.title), 0) + mediaFilename = mediaThumbFilename + mediaFilename = mediaFilename + "." + constants.MEDIA_INFO[type]['ext'] + mediaFilepath = os.path.join( self.activity.Instance.get_path(), mediaFilename ) + mediaFilepath = utils.getUniqueFilepath( mediaFilepath, 0 ) + recd.mediaFilename = os.path.basename( mediaFilepath ) + recd.oldFilename = mediaThumbFilename + recd.oldFilepath = mediaFilepath + color = sugar.profile.get_color() recd.colorStroke = color.get_stroke_color() recd.colorFill = color.get_fill_color() @@ -398,13 +407,13 @@ class Model: #load the thumbfile if recd.thumbFilename: - thumbFile = os.path.join(Instance.instancePath, recd.thumbFilename) + thumbFile = os.path.join(self.activity.Instance.get_path(), recd.thumbFilename) recd.thumbBytes = os.stat(thumbFile)[6] recd.tags = "" #load the mediafile - mediaFile = os.path.join(Instance.instancePath, recd.mediaFilename) + mediaFile = os.path.join(self.activity.Instance.get_path(), recd.mediaFilename) mBytes = os.stat(mediaFile)[6] recd.mediaBytes = mBytes diff --git a/model.pyc b/model.pyc new file mode 100644 index 0000000..0209219 --- /dev/null +++ b/model.pyc Binary files differ diff --git a/record.py b/record.py index e25010e..3b2c1a5 100644..100755 --- a/record.py +++ b/record.py @@ -27,6 +27,7 @@ from gettext import ngettext import gtk from gtk import gdk +import gio import cairo import pango import pangocairo @@ -72,10 +73,11 @@ else: class Record(activity.Activity): + def __init__(self, handle): super(Record, self).__init__(handle) self.props.enable_fullscreen_mode = False - Instance(self) + self.Instance = Instance(self) self.add_events(gtk.gdk.VISIBILITY_NOTIFY_MASK) self.connect("visibility-notify-event", self._visibility_changed) @@ -172,7 +174,7 @@ class Record(activity.Activity): self._toolbar.insert(gtk.SeparatorToolItem(), -1) - self._toolbar_controls = RecordControl(self._toolbar) + self._toolbar_controls = RecordControl(self._toolbar, self, 8 if self._video_button else 6) separator = gtk.SeparatorToolItem() separator.props.draw = False @@ -854,29 +856,103 @@ class PlayButton(gtk.Button): class RecordControl(): - - def __init__(self, toolbar): + def __init__(self, toolbar, activity, output_pos): + self.activity = activity + self.toolbar = toolbar + self.output_pos = output_pos self._timer_value = TIMER_VALUES[0] self._timer_button = ToolButton('timer-0') self._timer_button.set_tooltip(_('Select timer')) self._timer_button.connect('clicked', self._timer_selection_cb) - toolbar.insert(self._timer_button, -1) + self.toolbar.insert(self._timer_button, -1) self._setup_timer_palette() self._duration_value = DURATION_VALUES[0] self._duration_button = ToolButton('duration-2') self._duration_button.set_tooltip(_('Select duration')) self._duration_button.connect('clicked', self._duration_selection_cb) - toolbar.insert(self._duration_button, -1) + self.toolbar.insert(self._duration_button, -1) self._setup_duration_palette() self._quality_value = 0 self._quality_button = ToolButton('low-quality') self._quality_button.set_tooltip(_('Select quality')) self._quality_button.connect('clicked', self._quality_selection_cb) - toolbar.insert(self._quality_button, -1) + self.toolbar.insert(self._quality_button, -1) self._setup_quality_palette() + + self._setup_output_palette(False) + return + + def _output_selection_cb(self, widget): + if self._output_palette: + if not self._output_palette.is_up(): + self._output_palette.popup(immediate=True, + state=self._timer_palette.SECONDARY) + else: + self._output_palette.popdown(immediate=True) + return + + def _setup_output_palette(self, notFirst): + if notFirst: + self.toolbar.remove(self._output_button) + self._output_button = ToolButton() + self._output_button.set_icon_name('user-documents') + self._output_button.set_tooltip(_('Select output device')) + self._output_button.connect('clicked', self._output_selection_cb) + self._output_button.show() + self.toolbar.insert(self._output_button, self.output_pos) + self._output_palette = self._output_button.get_palette() + + volume_monitor = gio.volume_monitor_get() + self._mount_added_hid = volume_monitor.connect('mount-added', + self.__mount_change_cb) + self._mount_removed_hid = volume_monitor.connect('mount-removed', + self.__mount_change_cb) + + button = MenuItem(icon_name='activity-journal', + text_label='Journal') + self._output_palette.menu.append(button) + button.connect('activate', self._output_selected_cb, None) + button.show() + self._output_palette.menu.append(button) + + for mount in volume_monitor.get_mounts(): + self._add_button(mount) + + def __mount_change_cb(self, volume_monitor, mount): + self._setup_output_palette(True) + + def _get_mount_icon_name(self, mount, size): + icon = mount.get_icon() + if isinstance(icon, gio.ThemedIcon): + icon_theme = gtk.icon_theme_get_default() + for icon_name in icon.props.names: + if icon_theme.lookup_icon(icon_name, size, 0) is not None: + return icon_name + logging.error('Cannot find icon name for %s, %s', icon, mount) + return 'drive' + + def _add_button(self, mount): + logging.debug('VolumeToolbar._add_button: %r', mount.get_name()) + + button = MenuItem(icon_name= + self._get_mount_icon_name(mount, gtk.ICON_SIZE_LARGE_TOOLBAR), + text_label=mount.get_name()) + self._output_palette.menu.append(button) + button.connect('activate', self._output_selected_cb, mount) + button.show() + self._output_palette.menu.append(button) + + def _output_selected_cb(self, button, mount): + if mount: + self.activity.Instance.savePath = mount.get_root().get_path() + self._output_button.set_icon_name(self._get_mount_icon_name(mount, + gtk.ICON_SIZE_LARGE_TOOLBAR)) + else: + self.activity.Instance.savePath = None + self._output_button.set_icon_name('activity-journal') def _timer_selection_cb(self, widget): if self._timer_palette: diff --git a/recorded.py b/recorded.py index 9296742..d4e6d64 100644..100755 --- a/recorded.py +++ b/recorded.py @@ -24,10 +24,13 @@ import gtk import constants from instance import Instance import utils +import datetime import serialize +import copy class Recorded: - def __init__( self ): + def __init__( self, activity ): + self.activity = activity self.type = -1 self.time = None self.recorderName = None @@ -40,6 +43,10 @@ class Recorded: self.mediaBytes = None self.thumbBytes = None self.tags = None + + #set location of the files (if it may change upstream) + self.loc = self.activity.Instance.get_path() + self.saveInternal = True #flag to alert need to re-datastore the title self.metaChange = False @@ -52,6 +59,8 @@ class Recorded: self.mediaFilename = None self.thumbFilename = None self.audioImageFilename = None + self.oldFilename = None + self.oldFilepath = None #for flagging when you are being saved to the datastore for the first time... #and just because you have a datastore id, doesn't mean you're saved @@ -115,11 +124,11 @@ class Recorded: def getThumbFilepath( self ): if not self.thumbFilename: return None - return os.path.join(Instance.instancePath, self.thumbFilename) + return os.path.join(self.activity.Instance.get_path(), self.thumbFilename) def make_thumb_path(self): thumbFilename = self.mediaFilename + "_thumb.jpg" - thumbFilepath = os.path.join(Instance.instancePath, thumbFilename) + thumbFilepath = os.path.join(self.activity.Instance.get_path(), thumbFilename) thumbFilepath = utils.getUniqueFilepath(thumbFilepath, 0) self.thumbFilename = os.path.basename(thumbFilepath) return self.getThumbFilepath() @@ -139,34 +148,33 @@ class Recorded: def getAudioImageFilepath( self ): if (self.audioImageFilename != None): - audioFilepath = os.path.join(Instance.instancePath, self.audioImageFilename) + audioFilepath = os.path.join(self.activity.Instance.get_path(), self.audioImageFilename) return os.path.abspath(audioFilepath) else: return self.getThumbFilepath() - def getMediaFilepath(self): if (self.datastoreId == None): if (not self.buddy): #just taken by you, so it is in the tempSessionDir - mediaFilepath = os.path.join(Instance.instancePath, self.mediaFilename) + mediaFilepath = os.path.join(self.activity.Instance.get_path(), self.mediaFilename) return os.path.abspath(mediaFilepath) else: if (self.downloadedFromBuddy): #the user has requested the high-res version, and it has downloaded - mediaFilepath = os.path.join(Instance.instancePath, self.mediaFilename) + mediaFilepath = os.path.join(self.activity.Instance.get_path(), self.mediaFilename) return os.path.abspath(mediaFilepath) else: if self.mediaFilename == None: #creating a new filepath, probably just got here from the mesh ext = constants.MEDIA_INFO[self.type]['ext'] - recdPath = os.path.join(Instance.instancePath, "recdFile_"+self.mediaMd5+"."+ext) + recdPath = os.path.join(self.activity.Instance.get_path(), "recdFile_"+self.mediaMd5+"."+ext) recdPath = utils.getUniqueFilepath(recdPath, 0) self.mediaFilename = os.path.basename(recdPath) - mediaFilepath = os.path.join(Instance.instancePath, self.mediaFilename) + mediaFilepath = os.path.join(self.activity.Instance.get_path(), self.mediaFilename) return os.path.abspath(mediaFilepath) else: - mediaFilepath = os.path.join(Instance.instancePath, self.mediaFilename) + mediaFilepath = os.path.join(self.activity.Instance.get_path(), self.mediaFilename) return os.path.abspath(mediaFilepath) else: #pulling from the datastore, regardless of who took it, cause we got it diff --git a/recordtube.py b/recordtube.py index c2a60bb..c2a60bb 100644..100755 --- a/recordtube.py +++ b/recordtube.py diff --git a/serialize.py b/serialize.py index 74e3a3c..d2a63e0 100644..100755 --- a/serialize.py +++ b/serialize.py @@ -4,8 +4,16 @@ import os import gtk import logging import dbus +import subprocess +from datetime import datetime +import time +import tempfile +import shutil +import copy from sugar.datastore import datastore +from sugar import mime +from sugar import env import constants from instance import Instance @@ -14,15 +22,15 @@ import utils logger = logging.getLogger('serialize') -def fillMediaHash(doc, mediaHashs): +def fillMediaHash(doc, mediaHashs, activity): for key, value in constants.MEDIA_INFO.items(): recdElements = doc.documentElement.getElementsByTagName(value['name']) for el in recdElements: - _loadMediaIntoHash( el, mediaHashs[key] ) + _loadMediaIntoHash( el, mediaHashs[key], activity ) -def _loadMediaIntoHash(el, hash): +def _loadMediaIntoHash(el, hash, activity): addToHash = True - recd = recorded.Recorded() + recd = recorded.Recorded(activity) recd = fillRecdFromNode(recd, el) if recd: if recd.datastoreId: @@ -253,7 +261,7 @@ def _saveMediaToDatastore(el, recd, activity): recd.savedMedia = True _saveXml(el, recd) - else: + elif recd.saveInternal: #this will remove the media from being accessed on the local disk since it puts it away into cold storage #therefore this is only called when write_file is called by the activity superclass mediaObject = datastore.create() @@ -279,8 +287,11 @@ def _saveMediaToDatastore(el, recd, activity): colors = str(recd.colorStroke) + "," + str(recd.colorFill) mediaObject.metadata['icon-color'] = colors - mtype = constants.MEDIA_INFO[recd.type] - mediaObject.metadata['mime_type'] = mtype['mime'] + try: + mtype = constants.MEDIA_INFO[recd.type] + mediaObject.metadata['mime_type'] = mtype['mime'] + except KeyError: + logger.warning("KeyError when getting the mime type!?") mediaObject.metadata['activity_id'] = activity._activity_id @@ -296,3 +307,25 @@ def _saveMediaToDatastore(el, recd, activity): _saveXml(el, recd) recd.mediaFilename = None + else: + if recd.metaChange: + oldMediaFilepath = copy.copy(recd.oldFilepath) + mediaThumbFilename = utils.getUniqueFilepath(os.path.join( + recd.activity.Instance.get_path(), + recd.title), 0) + mediaFilename = mediaThumbFilename + mediaFilename = mediaFilename + "." + constants.MEDIA_INFO[recd.type]['ext'] + + mediaFilepath = os.path.join( recd.activity.Instance.get_path(), mediaFilename ) + mediaFilepath = utils.getUniqueFilepath( mediaFilepath, 0 ) + recd.mediaFilename = os.path.basename( mediaFilepath ) + recd.oldFilepath = mediaFilepath + + shutil.move(oldMediaFilepath, mediaFilepath) + + if recd.thumbFilename: + oldThumbFilepath = os.path.join(recd.activity.Instance.get_path(), + copy.copy(recd.thumbFilename)) + thumbFilepath = '.'.join(mediaFilepath.split('.')[:-1])+'_thumb.jpg' + shutil.move(oldThumbFilepath, thumbFilepath) + recd.thumbFilename = mediaThumbFilename+'_thumb.jpg' diff --git a/tray.py b/tray.py index 4f7956d..4f7956d 100644..100755 --- a/tray.py +++ b/tray.py diff --git a/utils.py b/utils.py index 701e45f..128f974 100644..100755 --- a/utils.py +++ b/utils.py @@ -47,8 +47,10 @@ def load_colored_svg(filename, stroke, fill): return rsvg.Handle(data=data).get_pixbuf() def getUniqueFilepath( path, i ): + if not os.path.exists(path): + return path pathOb = os.path.abspath( path ) - newPath = os.path.join( os.path.dirname(pathOb), str( str(i) + os.path.basename(pathOb) ) ) + newPath = os.path.join( os.path.dirname(pathOb), str( os.path.basename(pathOb) + str(i) ) ) if (os.path.exists(newPath)): i = i + 1 return getUniqueFilepath( pathOb, i ) -- cgit v0.9.1