summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul W. Frields <stickster@gmail.com>2010-12-21 15:29:25 -0500
committerPaul W. Frields <stickster@gmail.com>2010-12-21 15:29:25 -0500
commite56837e1f657d327eec5f9d12ceb45da4c871dd0 (patch)
treeb070e56fa71acd4c35e7b9c129b7cc1324506bc7
parentaf5fafca22e10bfa61ea78242b47fc2c1fd4c0c9 (diff)
downloadpulsecaster-e56837e1f657d327eec5f9d12ceb45da4c871dd0.tar.gz
pulsecaster-e56837e1f657d327eec5f9d12ceb45da4c871dd0.tar.xz
pulsecaster-e56837e1f657d327eec5f9d12ceb45da4c871dd0.zip
More verbose tempfile debugging message
-rw-r--r--pulsecaster/ui.py3
-rw-r--r--pulsecaster/ui.py.orig327
2 files changed, 329 insertions, 1 deletions
diff --git a/pulsecaster/ui.py b/pulsecaster/ui.py
index 5cfe8e6..bc0f772 100644
--- a/pulsecaster/ui.py
+++ b/pulsecaster/ui.py
@@ -171,7 +171,7 @@ class PulseCasterUI:
# Create temporary file
(self.tempfd, self.temppath) = tempfile.mkstemp(prefix='%s-tmp.' % (NAME))
self.tempfile = os.fdopen(self.tempfd)
- _debugPrint('%s (%s)' % (self.temppath, self.tempfd))
+ _debugPrint('tempfile: %s (fd %s)' % (self.temppath, self.tempfd))
# Adjust UI
self.user_vox.set_sensitive(False)
self.subject_vox.set_sensitive(False)
@@ -322,6 +322,7 @@ class PulseCasterUI:
def _remove_tempfile(self, tempfile, temppath):
tempfile.close()
os.remove(temppath)
+
if __name__ == '__main__':
pulseCaster = PulseCasterUI()
gtk.main()
diff --git a/pulsecaster/ui.py.orig b/pulsecaster/ui.py.orig
new file mode 100644
index 0000000..5cfe8e6
--- /dev/null
+++ b/pulsecaster/ui.py.orig
@@ -0,0 +1,327 @@
+# Copyright (C) 2009, 2010 Paul W. Frields and others.
+# -*- coding: utf-8 -*-
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+#
+# Author: Paul W. Frields <stickster@gmail.com>
+# Jürgen Geuter <tante@the-gay-bar.com>
+
+
+from config import *
+import gconfig
+from pulseaudio.PulseObj import PulseObj
+from listener import *
+from eggtrayicon import *
+import gtk
+import os
+import sys
+import tempfile
+import gobject
+import pygst
+pygst.require('0.10')
+import gst
+from datetime import datetime
+
+import gettext
+_ = lambda x: gettext.ldgettext(NAME, x)
+
+try:
+ _debug = os.environ['PULSECASTER_DEBUG']
+except:
+ _debug = False
+
+def _debugPrint(text):
+ if _debug:
+ print (text)
+
+class PulseCasterUI:
+ def __init__(self):
+ self.builder = gtk.Builder()
+ try:
+ self.builder.add_from_file(os.path.join(os.getcwd(),'data','pulsecaster.glade')
+)
+ _debugPrint(_("loading glade file from current subdir"))
+ except:
+ try:
+ self.builder.add_from_file(os.path.join(sys.prefix,'share','pulsecaster','pulsecaster.glade'))
+ except Exception,e:
+ print(e)
+ raise SystemExit(_("Cannot load resources"))
+
+ self.icontheme = gtk.icon_theme_get_default()
+ # Convenience for developers
+ self.icontheme.append_search_path(os.path.join(os.getcwd(),'data','icons','scalable'))
+ self.logo = self.icontheme.load_icon('pulsecaster', -1,
+ gtk.ICON_LOOKUP_FORCE_SVG)
+ gtk.window_set_default_icon(self.logo)
+ self.gconfig = gconfig.PulseCasterGconf()
+
+ self.warning = self.builder.get_object('warning')
+ self.dismiss = self.builder.get_object('dismiss_warning')
+ self.swckbox = self.builder.get_object('skip_warn_checkbox')
+ self.swckbox.set_active(int(self.gconfig.skip_warn))
+ self.dismiss.connect('clicked', self.hideWarn)
+ self.warning.connect('destroy', self.on_close)
+ self.warning.set_title(NAME)
+
+ # Main dialog basics
+ self.main = self.builder.get_object('main_dialog')
+ self.main.set_title(NAME)
+ self.main_title = self.builder.get_object('main_title')
+ self.main_title.set_label('<big><big><big><b><i>' +
+ NAME + '</i></b></big></big></big>')
+ self.main.connect('delete_event', self.on_close)
+ self.about_button = self.builder.get_object('about_button')
+ self.about_button.connect('clicked', self.showAbout)
+ self.close = self.builder.get_object('close_button')
+ self.close.connect('clicked', self.on_close)
+ self.record = self.builder.get_object('record_button')
+ self.record_id = self.record.connect('clicked', self.on_record)
+ self.record.set_sensitive(True)
+ self.main_logo = self.builder.get_object('logo')
+ self.main_logo.set_from_icon_name('pulsecaster', gtk.ICON_SIZE_DIALOG)
+ self.main.set_icon_list(self.logo)
+ # About dialog basics
+ self.about = self.builder.get_object('about_dialog')
+ self.about.connect('delete_event', self.hideAbout)
+ self.about.connect('response', self.hideAbout)
+ self.about.set_name(NAME)
+ self.about.set_version(VERSION)
+ self.about.set_copyright(COPYRIGHT)
+ self.about.set_comments(DESCRIPTION)
+ self.about.set_license(LICENSE_TEXT)
+ self.about.set_website(URL)
+ self.about.set_website_label(URL)
+ self.authors = [AUTHOR + ' <' + AUTHOR_EMAIL + '>']
+ for contrib in CONTRIBUTORS:
+ self.authors.append(contrib)
+ self.about.set_authors(self.authors)
+ self.about.set_program_name(NAME)
+ self.about.set_logo(self.icontheme.load_icon('pulsecaster', 96, gtk.ICON_LOOKUP_FORCE_SVG))
+
+ self.file_chooser = self.builder.get_object('file_chooser')
+ self.file_chooser_cancel_button = self.builder.get_object('file_chooser_cancel_button')
+ self.file_chooser_cancel_button.connect('clicked', self.hideFileChooser)
+ self.file_chooser_save_button = self.builder.get_object('file_chooser_save_button')
+ self.file_chooser_save_button.connect('clicked', self.updateFileSinkPath)
+ self.file_chooser.set_do_overwrite_confirmation(True)
+ self.file_chooser.connect('confirm-overwrite', self._confirm_overwrite)
+
+ # Create PulseAudio backing
+ self.pa = PulseObj(clientName=NAME)
+
+ # Create and populate combo boxes
+ self.table = self.builder.get_object('table1')
+ self.user_vox = gtk.combo_box_new_text()
+ self.subject_vox = gtk.combo_box_new_text()
+ self.table.attach(self.user_vox, 1, 2, 0, 1,
+ xoptions=gtk.EXPAND|gtk.FILL)
+ self.table.attach(self.subject_vox, 1, 2, 1, 2,
+ xoptions=gtk.EXPAND|gtk.FILL)
+ self.user_vox.connect('button-press-event', self.repop_sources)
+ self.subject_vox.connect('button-press-event', self.repop_sources)
+
+ # Fill the combo boxes initially
+ self.repop_sources()
+ self.listener = PulseCasterListener(self)
+
+ self.filesinkpath = ''
+
+ self.trayicon = gtk.StatusIcon()
+ self.trayicon.set_visible(False)
+ self.trayicon.set_from_icon_name('pulsecaster')
+
+ def repop_sources(self, *args):
+ self.sources = self.pa.pulse_source_list()
+ l = self.user_vox.get_model()
+ l.clear()
+ l = self.subject_vox.get_model()
+ l.clear()
+ self.uservoxes = []
+ self.subjectvoxes = []
+ for source in self.sources:
+ if source.monitor_of_sink_name == None:
+ self.uservoxes.append((source.name, source.description))
+ self.user_vox.append_text(source.description)
+ else:
+ self.subjectvoxes.append((source.name, source.description))
+ self.subject_vox.append_text(source.description)
+ self.user_vox.set_active(0)
+ self.subject_vox.set_active(0)
+ self.table.show_all()
+
+ if self.gconfig.skip_warn is False:
+ self.warning.show()
+ else:
+ self.hideWarn()
+
+ def on_record(self, *args):
+ # Create temporary file
+ (self.tempfd, self.temppath) = tempfile.mkstemp(prefix='%s-tmp.' % (NAME))
+ self.tempfile = os.fdopen(self.tempfd)
+ _debugPrint('%s (%s)' % (self.temppath, self.tempfd))
+ # Adjust UI
+ self.user_vox.set_sensitive(False)
+ self.subject_vox.set_sensitive(False)
+ self.close.set_sensitive(False)
+
+ self.combiner = gst.Pipeline('PulseCasterCombinePipe')
+ self.lsource = gst.element_factory_make('pulsesrc', 'lsrc')
+ self.lsource.set_property('device',
+ self.uservoxes[self.user_vox.get_active()][0])
+ self.rsource = gst.element_factory_make('pulsesrc', 'rsrc')
+ self.rsource.set_property('device',
+ self.subjectvoxes[self.subject_vox.get_active()][0])
+
+ self.adder = gst.element_factory_make('adder', 'mix')
+ self.encoder = gst.element_factory_make(self.gconfig.codec + 'enc', 'enc')
+ if self.gconfig.codec == 'vorbis':
+ self.muxer = gst.element_factory_make('oggmux', 'mux')
+ self.filesink = gst.element_factory_make('filesink', 'fsink')
+ self.filesink.set_property('location', self.temppath)
+
+ self.combiner.add(self.lsource,
+ self.rsource,
+ self.adder,
+ self.encoder,
+ self.filesink)
+ if self.gconfig.codec == 'vorbis':
+ self.combiner.add(self.muxer)
+ gst.element_link_many(self.lsource,
+ self.adder,
+ self.encoder)
+ if self.gconfig.codec == 'vorbis':
+ self.encoder.link(self.muxer)
+ self.muxer.link(self.filesink)
+ else: # flac
+ self.encoder.link(self.filesink)
+ gst.element_link_many(self.rsource, self.adder)
+
+ # FIXME: Dim elements other than the 'record' widget
+ self.record.set_label(gtk.STOCK_MEDIA_STOP)
+ self.record.disconnect(self.record_id)
+ self.stop_id = self.record.connect('clicked', self.on_stop)
+ self.record.show()
+ self.combiner.set_state(gst.STATE_PLAYING)
+ # Start timer
+ self.starttime = datetime.now()
+ self._update_time()
+ self.timeout = 1000
+ gobject.timeout_add(self.timeout, self._update_time)
+ self.trayicon.set_visible(True)
+
+ def on_stop(self, *args):
+ self.combiner.set_state(gst.STATE_NULL)
+ self.showFileChooser()
+ self.record.set_label(gtk.STOCK_MEDIA_RECORD)
+ self.record.disconnect(self.stop_id)
+ self.record_id = self.record.connect('clicked', self.on_record)
+ self.user_vox.set_sensitive(True)
+ self.subject_vox.set_sensitive(True)
+ self.close.set_sensitive(True)
+ self.record.show()
+
+ def on_close(self, *args):
+ try:
+ self.pa.disconnect()
+ except:
+ pass
+ gtk.main_quit()
+
+ def hideWarn(self, *args):
+ self.gconfig.change_warn(self.swckbox.get_active())
+ self.warning.hide()
+ self.main.show()
+
+ def showAbout(self, *args):
+ self.about.show()
+
+ def hideAbout(self, *args):
+ self.about.hide()
+
+ def showFileChooser(self, *args):
+ self.file_chooser.show()
+
+ def hideFileChooser(self, *args):
+ if not self.filesinkpath:
+ confirm = gtk.MessageDialog(type=gtk.MESSAGE_WARNING,
+ buttons=gtk.BUTTONS_YES_NO,
+ message_format=_('Are you sure you want to cancel ' +
+ 'saving your work? If you choose "Yes" ' +
+ 'your audio recording will be erased ' +
+ 'permanently.'))
+ response = confirm.run()
+ confirm.destroy()
+ if response == gtk.RESPONSE_YES:
+ self._remove_tempfile(self.tempfile, self.temppath)
+ else:
+ return
+ self.file_chooser.hide()
+
+ def updateFileSinkPath(self, *args):
+ self.filesinkpath = self.file_chooser.get_filename()
+ if not self.filesinkpath:
+ return
+ self.hideFileChooser()
+ if os.path.lexists(self.filesinkpath):
+ if not self._confirm_overwrite():
+ self.showFileChooser()
+ return
+ # Copy the temporary file to its new home
+ self.permfile = open(self.filesinkpath, 'w')
+ self._copy_temp_to_perm(self.tempfile, self.permfile)
+ self.permfile.close()
+ self._remove_tempfile(self.tempfile, self.temppath)
+ self.record.set_sensitive(True)
+
+ def _update_time(self, *args):
+ if self.combiner.get_state()[1] == gst.STATE_NULL:
+ self.trayicon.set_tooltip(None)
+ self.trayicon.set_visible(False)
+ return False
+ delta = datetime.now() - self.starttime
+ deltamin = delta.seconds // 60
+ deltasec = delta.seconds - (deltamin * 60)
+ self.trayicon.set_tooltip('Recording: %d:%02d' %
+ (deltamin, deltasec))
+ return True
+
+ def _confirm_overwrite(self, *args):
+ confirm = gtk.MessageDialog(type=gtk.MESSAGE_QUESTION,
+ buttons=gtk.BUTTONS_YES_NO,
+ message_format=_('File exists. OK to overwrite?'))
+ response = confirm.run()
+ if response == gtk.RESPONSE_YES:
+ retval = True
+ else:
+ retval = False
+ confirm.destroy()
+ return retval
+
+ def _copy_temp_to_perm(self, src, dest):
+ src.seek(0)
+ while True:
+ buf = src.read(1024*1024)
+ if buf:
+ dest.write(buf)
+ else:
+ break
+
+ def _remove_tempfile(self, tempfile, temppath):
+ tempfile.close()
+ os.remove(temppath)
+if __name__ == '__main__':
+ pulseCaster = PulseCasterUI()
+ gtk.main()