Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSAMdroid <sam.parkinson3@gmail.com>2013-12-06 22:00:18 (GMT)
committer SAMdroid <sam.parkinson3@gmail.com>2013-12-06 22:00:18 (GMT)
commiteeddab830f8c011dcb7309e197c879941291efc1 (patch)
tree27cbf3ef66aa66f34f38a1de85587c5155e9694b
parent826af38e6ab3373c997c46a1f6a4099f134691c7 (diff)
Added the ability to save media directly to external devices
-rwxr-xr-x[-rw-r--r--]aplay.py0
-rwxr-xr-x[-rw-r--r--]button.py0
-rwxr-xr-x[-rw-r--r--]collab.py0
-rwxr-xr-x[-rw-r--r--]constants.py0
-rwxr-xr-x[-rw-r--r--]glive.py0
-rwxr-xr-x[-rw-r--r--]gplay.py0
-rwxr-xr-x[-rw-r--r--]hw.py0
-rwxr-xr-x[-rw-r--r--]iconcombobox.py0
-rwxr-xr-x[-rw-r--r--]instance.py11
-rwxr-xr-x[-rw-r--r--]mediaview.py0
-rwxr-xr-x[-rw-r--r--]model.py45
-rw-r--r--model.pycbin0 -> 14494 bytes
-rwxr-xr-x[-rw-r--r--]record.py90
-rwxr-xr-x[-rw-r--r--]recorded.py28
-rwxr-xr-x[-rw-r--r--]recordtube.py0
-rwxr-xr-x[-rw-r--r--]serialize.py47
-rwxr-xr-x[-rw-r--r--]tray.py0
-rwxr-xr-x[-rw-r--r--]utils.py4
18 files changed, 179 insertions, 46 deletions
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 )