diff options
Diffstat (limited to 'mvc/ui')
-rw-r--r-- | mvc/ui/__init__.py | 0 | ||||
-rw-r--r-- | mvc/ui/console.py | 120 | ||||
-rw-r--r-- | mvc/ui/widgets.py | 1540 |
3 files changed, 0 insertions, 1660 deletions
diff --git a/mvc/ui/__init__.py b/mvc/ui/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/mvc/ui/__init__.py +++ /dev/null diff --git a/mvc/ui/console.py b/mvc/ui/console.py deleted file mode 100644 index a9751ac..0000000 --- a/mvc/ui/console.py +++ /dev/null @@ -1,120 +0,0 @@ -import json -import operator -import optparse -import time -import sys - -import mvc -from mvc.widgets import app -from mvc.widgets import initialize - -parser = optparse.OptionParser( - usage='%prog [-l] [--list-converters] [-c <converter> <filenames..>]', - version='%prog ' + mvc.VERSION, - prog='python -m mvc.ui.console') -parser.add_option('-j', '--json', action='store_true', - dest='json', - help='Output JSON documents, rather than text.') -parser.add_option('-l', '--list-converters', action='store_true', - dest='list_converters', - help="Print a list of supported converter types.") -parser.add_option('-c', '--converter', dest='converter', - help="Specify the type of conversion to make.") - -class Application(mvc.Application): - - def run(self): - (options, args) = parser.parse_args() - - if options.list_converters: - for c in sorted(self.converter_manager.list_converters(), - key=operator.attrgetter('name')): - if options.json: - print json.dumps({'name': c.name, - 'identifier': c.identifier}) - else: - print '%s (-c %s)' % ( - c.name, - c.identifier) - return - - try: - self.converter_manager.get_by_id(options.converter) - except KeyError: - message = '%r is not a valid converter type.' % ( - options.converter,) - if options.json: - print json.dumps({'error': message}) - else: - print 'ERROR:', message - print 'Use "%s -l" to get a list of valid converters.' % ( - parser.prog,) - print - parser.print_help() - sys.exit(1) - - any_failed = False - - def changed(c): - if c.status == 'failed': - any_failed = True - if options.json: - output = { - 'filename': c.video.filename, - 'output': c.output, - 'status': c.status, - 'duration': c.duration, - 'progress': c.progress, - 'percent': (c.progress_percent * 100 if c.progress_percent - else 0), - } - if c.error is not None: - output['error'] = c.error - print json.dumps(output) - else: - if c.status == 'initialized': - line = 'starting (output: %s)' % (c.output,) - elif c.status == 'converting': - if c.progress_percent is not None: - line = 'converting (%i%% complete, %is remaining)' % ( - c.progress_percent * 100, c.eta) - else: - line = 'converting (0% complete, unknown remaining)' - elif c.status == 'staging': - line = 'staging' - elif c.status == 'failed': - line = 'failed (error: %r)' % (c.error,) - elif c.status == 'finished': - line = 'finished (output: %s)' % (c.output,) - else: - line = c.status - print '%s: %s' % (c.video.filename, line) - - for filename in args: - try: - c = app.start_conversion(filename, options.converter) - except ValueError: - message = 'could not parse %r' % filename - if options.json: - any_failed = True - print json.dumps({'status': 'failed', 'error': message, - 'filename': filename}) - else: - print 'ERROR:', message - continue - changed(c) - c.listen(changed) - - # XXX real mainloop - while self.conversion_manager.running: - self.conversion_manager.check_notifications() - time.sleep(1) - self.conversion_manager.check_notifications() # one last time - - sys.exit(0 if not any_failed else 1) - -if __name__ == "__main__": - initialize(None) - app.widgetapp = Application() - app.widgetapp.startup() - app.widgetapp.run() diff --git a/mvc/ui/widgets.py b/mvc/ui/widgets.py deleted file mode 100644 index 28dabff..0000000 --- a/mvc/ui/widgets.py +++ /dev/null @@ -1,1540 +0,0 @@ -import logging -logging.basicConfig(level=logging.INFO) -logger = logging.getLogger(__name__) - -import os -import sys - -try: - import mvc -except ImportError: - mvc_path = os.path.join(os.path.dirname(__file__), '..', '..') - sys.path.append(mvc_path) - import mvc - -import copy -import tempfile -import urllib -import urlparse - -from mvc.widgets import (initialize, idle_add, mainloop_start, mainloop_stop, - attach_menubar, reveal_file, get_conversion_directory) -from mvc.widgets import menus -from mvc.widgets import widgetset -from mvc.widgets import cellpack -from mvc.widgets import widgetconst -from mvc.widgets import widgetutil -from mvc.widgets import app - -from mvc.converter import ConverterInfo -from mvc.video import VideoFile -from mvc.resources import image_path -from mvc.utils import size_string, round_even, convert_path_for_subprocess -from mvc import openfiles - -BUTTON_FONT = widgetutil.font_scale_from_osx_points(15.0) -LARGE_FONT = widgetutil.font_scale_from_osx_points(13.0) -SMALL_FONT = widgetutil.font_scale_from_osx_points(10.0) - -DEFAULT_FONT="Helvetica" - -CONVERT_TO_FONT = "Gill Sans Light" -CONVERT_TO_FONTSIZE = widgetutil.font_scale_from_osx_points(14.0) - -SETTINGS_FONT = "Gill Sans Light" -SETTINGS_FONTSIZE = widgetutil.font_scale_from_osx_points(13.0) - -CONVERT_NOW_FONT = "Gill Sans Light" -CONVERT_NOW_FONTSIZE = widgetutil.font_scale_from_osx_points(18.0) - -DND_FONT = "Gill Sans Light" -DND_LARGE_FONTSIZE = widgetutil.font_scale_from_osx_points(13.0) -DND_SMALL_FONTSIZE = widgetutil.font_scale_from_osx_points(12.0) - -ITEM_TITLE_FONT = "Futura Medium" -ITEM_TITLE_FONTSIZE = widgetutil.font_scale_from_osx_points(13.0) - -ITEM_ICONS_FONT= "Century Gothic" -ITEM_ICONS_FONTSIZE= widgetutil.font_scale_from_osx_points(10.0) - -GRADIENT_TOP = widgetutil.css_to_color('#585f63') -GRADIENT_BOTTOM = widgetutil.css_to_color('#383d40') - -DRAG_AREA = widgetutil.css_to_color('#2b2e31') - -TEXT_DISABLED = widgetutil.css_to_color('#333333') -TEXT_ACTIVE = widgetutil.css_to_color('#ffffff') -TEXT_CLICKED = widgetutil.css_to_color('#cccccc') -TEXT_INFO = widgetutil.css_to_color('#808080') -TEXT_COLOR = widgetutil.css_to_color('#ffffff') -TEXT_SHADOW = widgetutil.css_to_color('#000000') - -TABLE_WIDTH, TABLE_HEIGHT = 470, 87 - -class CustomLabel(widgetset.Background): - def __init__(self, text=''): - widgetset.Background.__init__(self) - self.text = text - self.font = DEFAULT_FONT - self.font_scale = LARGE_FONT - self.color = TEXT_COLOR - - def set_text(self, text): - self.text = text - self.invalidate_size_request() - - def set_color(self, color): - self.color = color - self.queue_redraw() - - def set_font(self, font, font_scale): - self.font = font - self.font_scale = font_scale - self.invalidate_size_request() - - def textbox(self, layout_manager): - layout_manager.set_text_color(self.color) - layout_manager.set_font(self.font_scale, family=self.font) - font = layout_manager.set_font(self.font_scale, family=self.font) - return layout_manager.textbox(self.text) - - def draw(self, context, layout_manager): - layout_manager.set_text_color(self.color) - layout_manager.set_font(LARGE_FONT, family=self.font) - textbox = self.textbox(layout_manager) - size = textbox.get_size() - textbox.draw(context, 0, (context.height - size[1]) // 2, - context.width, context.height) - - def size_request(self, layout_manager): - return self.textbox(layout_manager).get_size() - -class WebStyleButton(widgetset.CustomButton): - def __init__(self): - super(WebStyleButton, self).__init__() - self.set_cursor(widgetconst.CURSOR_POINTING_HAND) - self.text = '' - self.font = DEFAULT_FONT - self.font_scale = LARGE_FONT - - def set_text(self, text): - self.text = text - self.invalidate_size_request() - - def set_font(self, font, font_scale): - self.font = font - self.font_scale = font_scale - self.invalidate_size_request() - - def textbox(self, layout_manager): - return layout_manager.textbox(self.text, underline=True) - - def size_request(self, layout_manager): - textbox = self.textbox(layout_manager) - return textbox.get_size() - - def draw(self, context, layout_manager): - layout_manager.set_text_color(TEXT_COLOR) - layout_manager.set_font(self.font_scale, family=self.font) - textbox = self.textbox(layout_manager) - size = textbox.get_size() - textbox.draw(context, 0, (context.height - size[1]) // 2, - context.width, context.height) - -class FileDropTarget(widgetset.SolidBackground): - - dropoff_on = widgetset.ImageDisplay(widgetset.Image( - image_path("dropoff-icon-on.png"))) - dropoff_off = widgetset.ImageDisplay(widgetset.Image( - image_path("dropoff-icon-off.png"))) - dropoff_small_on = widgetset.ImageDisplay(widgetset.Image( - image_path("dropoff-icon-small-on.png"))) - dropoff_small_off = widgetset.ImageDisplay(widgetset.Image( - image_path("dropoff-icon-small-off.png"))) - - def __init__(self): - super(FileDropTarget, self).__init__() - self.set_background_color(DRAG_AREA) - self.alignment = widgetset.Alignment( - xscale=0.0, yscale=0.5, - xalign=0.5, yalign=0.5, - top_pad=10, right_pad=40, - bottom_pad=10, left_pad=40) - self.add(self.alignment) - - self.widgets = { - False: self.build_large_widgets(), - True: self.build_small_widgets() - } - - self.normal, self.drag = self.widgets[False] - self.alignment.add(self.normal) - - self.in_drag = False - self.small = False - - def build_large_widgets(self): - height = 40 # arbitrary, but the same for both - normal = widgetset.VBox(spacing=20) - normal.pack_start(widgetutil.align_center(self.dropoff_off, - top_pad=60)) - label = CustomLabel("Drag videos here or") - label.set_color(TEXT_COLOR) - label.set_font(DND_FONT, DND_LARGE_FONTSIZE) - hbox = widgetset.HBox(spacing=4) - hbox.pack_start(widgetutil.align_middle(label)) - - cfb = WebStyleButton() - cfb.set_font(DND_FONT, DND_LARGE_FONTSIZE) - cfb.set_text('Choose Files...') - - cfb.connect('clicked', self.choose_file) - hbox.pack_start(widgetutil.align_middle(cfb)) - hbox.set_size_request(-1, height) - normal.pack_start(hbox) - - drag = widgetset.VBox(spacing=20) - drag.pack_start(widgetutil.align_center(self.dropoff_on, - top_pad=60)) - hbox = widgetset.HBox(spacing=4) - hbox.pack_start(widgetutil.align_center( - widgetset.Label("Release button to drop off", - color=TEXT_COLOR))) - hbox.set_size_request(-1, height) - drag.pack_start(hbox) - return normal, drag - - def build_small_widgets(self): - height = 40 # arbitrary, but the same for both - normal = widgetset.HBox(spacing=4) - normal.pack_start(widgetutil.align_middle(self.dropoff_small_off, - right_pad=7)) - drag_label = CustomLabel('Drag more videos here or') - drag_label.set_font(DND_FONT, DND_SMALL_FONTSIZE) - drag_label.set_color(TEXT_COLOR) - normal.pack_start(widgetutil.align_middle(drag_label)) - cfb = WebStyleButton() - cfb.set_text('Choose Files...') - cfb.set_font(DND_FONT, DND_SMALL_FONTSIZE) - cfb.connect('clicked', self.choose_file) - normal.pack_start(cfb) - normal.set_size_request(-1, height) - - drop_label = CustomLabel('Release button to drop off') - drop_label.set_font(DND_FONT, DND_SMALL_FONTSIZE) - drop_label.set_color(TEXT_COLOR) - drag = widgetset.HBox(spacing=10) - drag.pack_start(widgetutil.align_middle(self.dropoff_small_on)) - drag.pack_start(widgetutil.align_middle(drop_label)) - drag.set_size_request(-1, height) - - return normal, drag - - def set_small(self, small): - if small != self.small: - self.small = small - self.normal, self.drag = self.widgets[small] - self.set_in_drag(self.in_drag, force=True) - - def set_in_drag(self, in_drag, force=False): - if force or in_drag != self.in_drag: - self.in_drag = in_drag - if in_drag: - self.alignment.set_child(self.drag) - else: - self.alignment.set_child(self.normal) - self.queue_redraw() - - def choose_file(self, widget): - app.widgetapp.choose_file() - -BUTTON_BACKGROUND = widgetutil.ThreeImageSurface('settings-base') - -class SettingsButton(widgetset.CustomButton): - - arrow_on = widgetset.ImageSurface(widgetset.Image( - image_path('arrow-down-on.png'))) - arrow_off = widgetset.ImageSurface(widgetset.Image( - image_path('arrow-down-off.png'))) - - def __init__(self, name): - super(SettingsButton, self).__init__() - if name != 'settings': - self.name = name.title() - else: - self.name = None - self.selected = False - if name != 'format': - self.surface_on = widgetset.ImageSurface(widgetset.Image( - image_path('%s-icon-on.png' % name))) - self.surface_off = widgetset.ImageSurface(widgetset.Image( - image_path('%s-icon-off.png' % name))) - if self.surface_on.height != self.surface_off.height: - raise ValueError('invalid surface: height mismatch') - self.image_padding = self.calc_image_padding(name) - else: - self.surface_on = self.surface_off = None - - def calc_image_padding(self, name): - """Add some padding to the bottom of our image icon. This can be used - to fine tune where it gets placed. - - :returns: padding in as a (top, right, bottom, left) tuple - """ - - # NOTE: we vertically center the images, so in order to move it X - # pickels up, we need X*2 pixels of bottom padding - if name == 'android': - return (0, 0, 2, 0) - elif name in ('apple', 'other'): - return (0, 0, 4, 0) - else: - return (0, 0, 0, 0) - - def textbox(self, layout_manager): - layout_manager.set_font(SETTINGS_FONTSIZE, family=SETTINGS_FONT) - return layout_manager.textbox(self.name) - - def size_request(self, layout_manager): - hbox = self.build_hbox(layout_manager) - size = hbox.get_size() - height = max(BUTTON_BACKGROUND.height, size[1]) - return int(size[0]) + 2, int(height) + 2 # padding - - def build_hbox(self, layout_manager): - hbox = cellpack.HBox(spacing=5) - if self.selected: - image = self.surface_on - arrow = self.arrow_on - layout_manager.set_text_color(TEXT_ACTIVE) - else: - image = self.surface_off - arrow = self.arrow_off - layout_manager.set_text_color(TEXT_DISABLED) - if image: - padding = cellpack.Padding(image, *self.image_padding) - hbox.pack(cellpack.Alignment(padding, xscale=0, yscale=0, - yalign=0.5)) - if self.name: - vbox = cellpack.VBox() - textbox = self.textbox(layout_manager) - vbox.pack(textbox) - vbox.pack_space(1) - hbox.pack(cellpack.Alignment(vbox, yscale=0, yalign=0.5), - expand=True) - a = cellpack.Alignment(arrow, xscale=0, yscale=0, yalign=0.5) - hbox.pack(cellpack.Padding(a, left=5, right=12)) - alignment = cellpack.Padding(hbox, left=5) - return alignment - - def draw(self, context, layout_manager): - BUTTON_BACKGROUND.draw(context, 1, 1, context.width - 2) - alignment = self.build_hbox(layout_manager) - padding = cellpack.Padding(alignment, top=1, right=3, bottom=1, left=3) - padding.render_layout(context) - - def set_selected(self, selected): - self.selected = selected - self.queue_redraw() - - -class OptionMenuBackground(widgetset.Background): - def __init__(self): - widgetset.Background.__init__(self) - self.surface = widgetutil.ThreeImageSurface('settings-depth') - - def set_child(self, child): - widgetset.Background.set_child(self, child) - # re-create the image surface and scale it as it needs to cover - # the whole of the height of the child - _, h = child.get_size_request() - self.surface = widgetutil.ThreeImageSurface('settings-depth', height=h) - self.invalidate_size_request() - - def size_request(self, layout_manager): - return -1, self.surface.height - - def draw(self, context, layout_manager): - child_width = self.child.get_size_request()[0] - self.surface.draw(context, 0, 0, child_width) - - -class BottomBackground(widgetset.Background): - - def draw(self, context, layout_manager): - gradient = widgetset.Gradient(0, 0, 0, context.height) - gradient.set_start_color(GRADIENT_TOP) - gradient.set_end_color(GRADIENT_BOTTOM) - context.rectangle(0, 0, context.width, context.height) - context.gradient_fill(gradient) - - -class LabeledNumberEntry(widgetset.HBox): - - def __init__(self, label): - super(LabeledNumberEntry, self).__init__(spacing=5) - self.label = widgetset.Label(label, color=TEXT_COLOR) - self.label.set_size(widgetconst.SIZE_SMALL) - self.entry = widgetset.NumberEntry() - self.entry.set_size_request(50, 20) - self.pack_start(self.label) - self.pack_start(self.entry) - self.entry.connect('focus-out', lambda x: self.emit('focus-out')) - - def get_text(self): - return self.entry.get_text() - - def set_text(self, text): - self.entry.set_text(text) - - def get_value(self): - try: - return int(self.entry.get_text()) - except ValueError: - return None - - -class CustomOptions(widgetset.Background): - - background = widgetset.ImageSurface(widgetset.Image( - image_path('settings-dropdown-bottom-bg.png'))) - - def __init__(self): - super(CustomOptions, self).__init__() - self.create_signal('setting-changed') - self.reset() - - def reset(self): - self.options = { - 'destination': None, - 'custom-size': False, - 'width': None, - 'height': None, - 'custom-aspect': False, - 'aspect-ratio': 4.0/3.0, - 'dont-upsize': True - } - - self.top = self.create_top() - self.top.set_size_request(390, 50) - self.left = self.create_left() - self.left.set_size_request(212, 70) - self.right = self.create_right() - self.right.set_size_request(178, 70) - vbox = widgetset.VBox() - vbox.pack_start(self.top) - hbox = widgetset.HBox() - hbox.pack_start(self.left) - hbox.pack_start(self.right) - vbox.pack_start(hbox) - - self.box = widgetutil.align_left(vbox) - - if self.child: - self.set_child(self.box) - - def create_top(self): - hbox = widgetset.HBox(spacing=0) - path_label = WebStyleButton() - path_label.set_text('Show output folder') - path_label.set_font(DEFAULT_FONT, widgetconst.SIZE_SMALL) - path_label.connect('clicked', self.on_path_label_clicked) - create_thumbnails = widgetset.Checkbox('Create Thumbnails', - color=TEXT_COLOR) - create_thumbnails.set_size(widgetconst.SIZE_SMALL) - create_thumbnails.connect('toggled', - self.on_create_thumbnails_changed) - - hbox.pack_start(widgetutil.align(path_label, xalign=0.5), expand=True) - hbox.pack_start(widgetutil.align(create_thumbnails, xalign=0.5), - expand=True) - # XXX: disabled until we can figure out how to do this properly. - #button = widgetset.Button('...') - #button.connect('clicked', self.on_destination_clicked) - #reset = widgetset.Button('Reset') - #reset.connect('clicked', self.on_destination_reset) - #hbox.pack_start(button) - #hbox.pack_start(reset) - return widgetutil.align(hbox, xscale=1.0, yalign=0.5) - - def _get_save_to_path(self): - if self.options['destination'] is None: - return get_conversion_directory() - else: - return self.options['destination'] - - def on_path_label_clicked(self, label): - save_path = self._get_save_to_path() - save_path = convert_path_for_subprocess(save_path) - openfiles.reveal_folder(save_path) - - def create_left(self): - self.custom_size = widgetset.Checkbox('Custom Size', color=TEXT_COLOR) - self.custom_size.set_size(widgetconst.SIZE_SMALL) - self.custom_size.connect('toggled', self.on_custom_size_changed) - - dont_upsize = widgetset.Checkbox('Don\'t Upsize', color=TEXT_COLOR) - dont_upsize.set_checked(self.options['dont-upsize']) - dont_upsize.set_size(widgetconst.SIZE_SMALL) - dont_upsize.connect('toggled', self.on_dont_upsize_changed) - - bottom = widgetset.HBox(spacing=5) - self.width_widget = LabeledNumberEntry('Width') - self.width_widget.connect('focus-out', self.on_width_changed) - self.width_widget.entry.connect('activate', - self.on_width_changed) - self.width_widget.disable() - self.height_widget = LabeledNumberEntry('Height') - self.height_widget.connect('focus-out', self.on_height_changed) - self.height_widget.entry.connect('activate', - self.on_height_changed) - self.height_widget.disable() - bottom.pack_start(self.width_widget) - bottom.pack_start(self.height_widget) - - hbox = widgetset.HBox(spacing=5) - hbox.pack_start(self.custom_size) - hbox.pack_start(dont_upsize) - - vbox = widgetset.VBox(spacing=5) - vbox.pack_start(widgetutil.align_left(hbox, left_pad=10)) - vbox.pack_start(widgetutil.align_center(bottom)) - return widgetutil.align_middle(vbox) - - def create_right(self): - aspect = widgetset.Checkbox('Custom Aspect Ratio', color=TEXT_COLOR) - aspect.set_size(widgetconst.SIZE_SMALL) - aspect.connect('toggled', self.on_aspect_changed) - self.aspect_widget = aspect - self.button_group = widgetset.RadioButtonGroup() - b1 = widgetset.RadioButton('4:3', self.button_group, color=TEXT_COLOR) - b2 = widgetset.RadioButton('3:2', self.button_group, color=TEXT_COLOR) - b3 = widgetset.RadioButton('16:9', self.button_group, color=TEXT_COLOR) - b1.set_selected() - b1.set_size(widgetconst.SIZE_SMALL) - b2.set_size(widgetconst.SIZE_SMALL) - b3.set_size(widgetconst.SIZE_SMALL) - self.aspect_map = dict() - self.aspect_map[b1] = (4, 3) - self.aspect_map[b2] = (3, 2) - self.aspect_map[b3] = (16, 9) - hbox = widgetset.HBox(spacing=5) - # Because the custom size starts off as disabled, so should aspect - # ratio as aspect ratio is dependent on a custom size set. - self.aspect_widget.disable() - for button in self.button_group.get_buttons(): - button.disable() - button.set_size(widgetconst.SIZE_SMALL) - hbox.pack_start(button) - button.connect('clicked', self.on_aspect_size_changed) - - vbox = widgetset.VBox() - vbox.pack_start(widgetutil.align_center(aspect)) - vbox.pack_start(widgetutil.align_center(hbox)) - return widgetutil.align_middle(vbox) - - def draw(self, context, layout_manager): - self.background.draw(context, 0, 0, self.background.width, - self.background.height) - - def enable_custom_size(self): - self.custom_size.enable() - - def disable_custom_size(self): - self.custom_size.disable() - self.custom_size.set_checked(False) - - def update_setting(self, setting, value): - self.options[setting] = value - if setting in ('width', 'height'): - if value is not None: - widget_text = str(value) - else: - widget_text = '' - if setting == 'width': - self.width_widget.set_text(widget_text) - elif setting == 'height': - self.height_widget.set_text(widget_text) - - def do_setting_changed(self, setting, value): - logging.info('setting-changed: %s -> %s', setting, value) - - def _change_setting(self, setting, value): - """Handles setting changes in response to widget changes.""" - - self.options[setting] = value - self.emit('setting-changed', setting, value) - - def force_width_to_aspect_ratio(self): - aspect_ratio = self.options['aspect-ratio'] - width = self.width_widget.get_text() - height = self.height_widget.get_text() - if not height: - return - new_width = round_even(float(height) * aspect_ratio) - if new_width != width: - self.update_setting('width', new_width) - self.emit('setting-changed', 'width', new_width) - - def force_height_to_aspect_ratio(self): - aspect_ratio = self.options['aspect-ratio'] - width = self.width_widget.get_text() - height = self.height_widget.get_text() - if not width: - return - new_height = round_even(float(width) / aspect_ratio) - if new_height != height: - self.update_setting('height', new_height) - self.emit('setting-changed', 'height', new_height) - - def show(self): - self.set_child(self.box) - self.set_size_request(self.background.width, - self.background.height + 28) - self.queue_redraw() - - def hide(self): - self.remove() - self.set_size_request(0, 0) - self.queue_redraw() - - def toggle(self): - if self.child: - self.hide() - else: - self.show() - - # signal handlers - def on_destination_clicked(self, widget): - dialog = widgetset.DirectorySelectDialog('Destination Directory') - r = dialog.run() - if r == 0: # picked a directory - self._change_setting('destination', directory) - - def on_destination_reset(self, widget): - self._change_setting('destination', None) - - def on_dont_upsize_changed(self, widget): - self._change_setting('dont-upsize', widget.get_checked()) - - def on_custom_size_changed(self, widget): - self._change_setting('custom-size', widget.get_checked()) - if widget.get_checked(): - self.width_widget.enable() - self.height_widget.enable() - self.aspect_widget.enable() - self.on_aspect_changed(self.aspect_widget) - else: - self.width_widget.disable() - self.height_widget.disable() - self.aspect_widget.disable() - self.on_aspect_changed(self.aspect_widget) - for button in self.button_group.get_buttons(): - button.disable() - - def on_create_thumbnails_changed(self, widget): - self._change_setting('create-thumbnails', widget.get_checked()) - - def on_width_changed(self, widget): - self._change_setting('width', self.width_widget.get_value()) - if self.options['custom-aspect']: - self.force_height_to_aspect_ratio() - - def on_height_changed(self, widget): - self._change_setting('height', self.height_widget.get_value()) - if self.options['custom-aspect']: - self.force_width_to_aspect_ratio() - - def on_aspect_changed(self, widget): - self._change_setting('custom-aspect', widget.get_checked()) - if widget.get_checked(): - self.force_height_to_aspect_ratio() - for button in self.button_group.get_buttons(): - button.enable() - else: - for button in self.button_group.get_buttons(): - button.disable() - - def on_aspect_size_changed(self, widget): - if self.options['custom-aspect']: - width_ratio, height_ratio = [float(v) for v in - self.aspect_map[widget]] - ratio = width_ratio / height_ratio - self._change_setting('aspect-ratio', ratio) - self.force_height_to_aspect_ratio() - -EMPTY_CONVERTER = ConverterInfo("") - - -class ConversionModel(widgetset.TableModel): - def __init__(self): - super(ConversionModel, self).__init__( - 'text', # filename - 'numeric', # output_size - 'text', # converter - 'text', # status - 'numeric', # duration - 'numeric', # progress - 'numeric', # eta, - 'object', # image - 'object', # the actual conversion - ) - self.conversion_to_iter = {} - self.thumbnail_to_image = {None: widgetset.Image( - image_path('audio.png'))} - - def conversions(self): - return iter(self.conversion_to_iter) - - def all_conversions_done(self): - has_conversions = any(self.conversions()) - all_done = ((set(c.status for c in self.conversions()) - - set(['canceled', 'finished', 'failed'])) == set()) - return all_done and has_conversions - - def get_image(self, path): - if path not in self.thumbnail_to_image: - try: - image = widgetset.Image(path) - except ValueError: - image = self.thumbnail_to_image[None] - self.thumbnail_to_image[path] = image - return self.thumbnail_to_image[path] - - def update_conversion(self, conversion): - try: - output_size = os.stat(conversion.output).st_size - except OSError: - output_size = 0 - - def complete(): - # needs to do it on the update_conversion() from app object - # which calls model_changed() and redraws for us - app.widgetapp.update_conversion(conversion) - - values = (conversion.video.filename, - output_size, - conversion.converter.name, - conversion.status, - conversion.duration or 0, - conversion.progress or 0, - conversion.eta or 0, - self.get_image(conversion.video.get_thumbnail(complete, 90, 70)), - conversion - ) - iter_ = self.conversion_to_iter.get(conversion) - if iter_ is None: - self.conversion_to_iter[conversion] = self.append(*values) - else: - self.update(iter_, *values) - - def remove(self, iter_): - conversion = self[iter_][-1] - del self.conversion_to_iter[conversion] - - # XXX If we add/remove too quickly, we could still be processing - # thumbnails and this may return null, and the self.thumbnail_to_image - # dictionary may get out of sync - def complete(path): - logging.info('calling completion handler for get_thumbnail on ' - 'removal') - - thumbnail_path = conversion.video.get_thumbnail(complete, 90, 70) - if thumbnail_path: - del self.thumbnail_to_image[thumbnail_path] - return super(ConversionModel, self).remove(iter_) - - -class IconWithText(cellpack.HBox): - - def __init__(self, icon, textbox): - super(IconWithText, self).__init__(spacing=5) - self.pack(cellpack.Alignment(icon, yalign=0.5, xscale=0, yscale=0)) - self.pack(textbox) - - -class ConversionCellRenderer(widgetset.CustomCellRenderer): - - IGNORE_PADDING = True - - clear = widgetset.ImageSurface(widgetset.Image( - image_path("clear-icon.png"))) - converted_to = widgetset.ImageSurface(widgetset.Image( - image_path("converted_to-icon.png"))) - queued = widgetset.ImageSurface(widgetset.Image( - image_path("queued-icon.png"))) - showfile = widgetset.ImageSurface(widgetset.Image( - image_path("showfile-icon.png"))) - show_ffmpeg = widgetset.ImageSurface(widgetset.Image( - image_path("error-icon.png"))) - progressbar_base = widgetset.ImageSurface(widgetset.Image( - image_path("progressbar-base.png"))) - delete_on = widgetset.ImageSurface(widgetset.Image( - image_path("item-delete-button-on.png"))) - delete_off = widgetset.ImageSurface(widgetset.Image( - image_path("item-delete-button-off.png"))) - error = widgetset.ImageSurface(widgetset.Image( - image_path("item-error.png"))) - completed = widgetset.ImageSurface(widgetset.Image( - image_path("item-completed.png"))) - - def __init__(self): - super(ConversionCellRenderer, self).__init__() - self.alignment = None - - def get_size(self, style, layout_manager): - return TABLE_WIDTH, TABLE_HEIGHT - - def render(self, context, layout_manager, selected, hotspot, hover): - left_right = cellpack.HBox() - top_bottom = cellpack.VBox() - left_right.pack(self.layout_left(layout_manager)) - left_right.pack(top_bottom, expand=True) - layout_manager.set_text_color(TEXT_COLOR) - layout_manager.set_font(ITEM_TITLE_FONTSIZE, bold=True, - family=ITEM_TITLE_FONT) - title = layout_manager.textbox(os.path.basename(self.input)) - title.set_wrap_style('truncated-char') - alignment = cellpack.Padding(cellpack.TruncatedTextLine(title), - top=25) - top_bottom.pack(alignment) - layout_manager.set_font(ITEM_ICONS_FONTSIZE, family=ITEM_ICONS_FONT) - - bottom = self.layout_bottom(layout_manager, hotspot) - if bottom is not None: - top_bottom.pack(bottom) - left_right.pack(self.layout_right(layout_manager, hotspot)) - - alignment = cellpack.Alignment(left_right, yscale=0, yalign=0.5) - self.alignment = alignment - - background = cellpack.Background(alignment) - background.set_callback(self.draw_background) - background.render_layout(context) - - @staticmethod - def draw_background(context, x, y, width, height): - # draw main background - gradient = widgetset.Gradient(x, y, x, height) - gradient.set_start_color(GRADIENT_TOP) - gradient.set_end_color(GRADIENT_BOTTOM) - context.rectangle(x, y, width, height) - context.gradient_fill(gradient) - # draw bottom line - context.set_line_width(1) - context.set_color((0, 0, 0)) - context.move_to(0, height-0.5) - context.line_to(context.width, height-0.5) - context.stroke() - - def draw_progressbar(self, context, x, y, _, height, width): - # We're only drawing a certain amount of width, not however much we're - # allocated. So, we ignore the passed-in width and just use what we - # set in layout_bottom. - widgetutil.circular_rect(context, x, y, width-1, height-1) - context.set_color((1, 1, 1)) - context.fill() - - def layout_left(self, layout_manager): - surface = widgetset.ImageSurface(self.thumbnail) - return cellpack.Padding(surface, 10, 10, 10, 10) - - def layout_right(self, layout_manager, hotspot): - alignment_kwargs = dict( - xalign=0.5, - xscale=0, - yalign=0.5, - yscale=0, - min_width=80) - if self.status == 'finished': - return cellpack.Alignment(self.completed, **alignment_kwargs) - elif self.status in ('canceled', 'failed'): - return cellpack.Alignment(self.error, **alignment_kwargs) - else: - if hotspot == 'cancel': - image = self.delete_on - else: - image = self.delete_off - return cellpack.Alignment(cellpack.Hotspot('cancel', - image), - **alignment_kwargs) - - def layout_bottom(self, layout_manager, hotspot): - layout_manager.set_text_color(TEXT_COLOR) - if self.status in ('converting', 'staging'): - box = cellpack.HBox(spacing=5) - stack = cellpack.Stack() - stack.pack(cellpack.Alignment(self.progressbar_base, - yalign=0.5, - xscale=0, yscale=0)) - percent = self.progress / self.duration - width = max(int(percent * self.progressbar_base.width), - 5) - stack.pack(cellpack.DrawingArea( - width, self.progressbar_base.height, - self.draw_progressbar, width)) - box.pack(cellpack.Alignment(stack, - yalign=0.5, - xscale=0, yscale=0)) - textbox = layout_manager.textbox("%d%%" % ( - 100 * percent)) - box.pack(textbox) - return box - elif self.status == 'initialized': # queued - vbox = cellpack.VBox() - vbox.pack_space(2) - vbox.pack(IconWithText(self.queued, - layout_manager.textbox("Queued"))) - return vbox - elif self.status in ('finished', 'failed', 'canceled'): - vbox = cellpack.VBox(spacing=5) - vbox.pack_space(4) - top = cellpack.HBox(spacing=5) - if self.status == 'finished': - if hotspot == 'show-file': - layout_manager.set_text_color(TEXT_CLICKED) - top.pack(cellpack.Hotspot('show-file', IconWithText( - self.showfile, - layout_manager.textbox('Show File', - underline=True)))) - elif self.status in ('failed', 'canceled'): - color = TEXT_CLICKED if hotspot == 'show-log' else TEXT_COLOR - layout_manager.set_text_color(color) - # XXX Missing grey error icon - if self.status == 'failed': - text = 'Error - Show FFmpeg Output' - else: - text = 'Canceled - Show FFmpeg Output' - top.pack(cellpack.Hotspot('show-log', IconWithText( - self.show_ffmpeg, - layout_manager.textbox(text, underline=True)))) - color = TEXT_CLICKED if hotspot == 'clear' else TEXT_COLOR - layout_manager.set_text_color(color) - top.pack(cellpack.Hotspot('clear', IconWithText( - self.showfile, - layout_manager.textbox('Clear', underline=True)))) - vbox.pack(top) - if self.status == 'finished': - layout_manager.set_text_color(TEXT_INFO) - vbox.pack(IconWithText( - self.converted_to, - layout_manager.textbox("Converted to %s" % ( - size_string(self.output_size))))) - return vbox - - def hotspot_test(self, style, layout_manager, x, y, width, height): - if self.alignment is None: - return - hotspot_info = self.alignment.find_hotspot(x, y, width, height) - if hotspot_info: - return hotspot_info[0] - -class ConvertButton(widgetset.CustomButton): - off = widgetset.ImageSurface(widgetset.Image( - image_path("convert-button-off.png"))) - clear = widgetset.ImageSurface(widgetset.Image( - image_path("convert-button-off.png"))) - on = widgetset.ImageSurface(widgetset.Image( - image_path("convert-button-on.png"))) - stop = widgetset.ImageSurface(widgetset.Image( - image_path("convert-button-stop.png"))) - - def __init__(self): - super(ConvertButton, self).__init__() - self.hidden = False - self.set_off() - - def set_on(self): - self.label = 'Convert to %s' % app.widgetapp.current_converter.name - self.image = self.on - self.set_cursor(widgetconst.CURSOR_POINTING_HAND) - self.queue_redraw() - - def set_clear(self): - self.label = 'Clear and Start Over' - self.image = self.clear - self.set_cursor(widgetconst.CURSOR_POINTING_HAND) - self.queue_redraw() - - def set_off(self): - self.label = 'Convert Now' - self.image = self.off - self.set_cursor(widgetconst.CURSOR_NORMAL) - self.queue_redraw() - - def set_stop(self): - self.label = 'Stop All Conversions' - self.image = self.stop - self.set_cursor(widgetconst.CURSOR_POINTING_HAND) - self.queue_redraw() - - def hide(self): - self.hidden = True - self.invalidate_size_request() - self.queue_redraw() - - def show(self): - self.hidden = False - self.invalidate_size_request() - self.queue_redraw() - - def size_request(self, layout_manager): - if self.hidden: - return 0, 0 - return self.off.width, self.off.height - - def draw(self, context, layout_manager): - if self.hidden: - return - self.image.draw(context, 0, 0, self.image.width, self.image.height) - layout_manager.set_font(CONVERT_NOW_FONTSIZE, family=CONVERT_NOW_FONT) - if self.image == self.off: - layout_manager.set_text_shadow(widgetutil.Shadow(TEXT_SHADOW, - 0.5, (-1, -1), 0)) - layout_manager.set_text_color(TEXT_DISABLED) - else: - layout_manager.set_text_shadow(widgetutil.Shadow(TEXT_SHADOW, - 0.5, (1, 1), 0)) - layout_manager.set_text_color(TEXT_ACTIVE) - textbox = layout_manager.textbox(self.label) - alignment = cellpack.Alignment(textbox, xalign=0.5, xscale=0.0, - yalign=0.5, yscale=0) - alignment.render_layout(context) - -# XXX do we want to export this for general purpose use? -class TextDialog(widgetset.Dialog): - def __init__(self, title, description, window): - widgetset.Dialog.__init__(self, title, description) - self.set_transient_for(window) - self.add_button('OK') - self.textbox = widgetset.MultilineTextEntry() - self.textbox.set_editable(False) - scroller = widgetset.Scroller(False, True) - scroller.set_has_borders(True) - scroller.add(self.textbox) - scroller.set_size_request(400, 500) - self.set_extra_widget(scroller) - - def set_text(self, text): - self.textbox.set_text(text) - -class Application(mvc.Application): - def __init__(self, simultaneous=None): - mvc.Application.__init__(self, simultaneous) - self.create_signal('window-shown') - self.sent_window_shown = False - - def startup(self): - if self.started: - return - - self.current_converter = EMPTY_CONVERTER - - mvc.Application.startup(self) - - self.menu_manager = menus.MenuManager() - self.menu_manager.setup_menubar(self.menubar) - - self.window = widgetset.Window("Libre Video Converter") - self.window.connect('on-shown', self.on_window_shown) - self.window.connect('will-close', self.destroy) - - # # table on top - self.model = ConversionModel() - self.table = widgetset.TableView(self.model) - self.table.draws_selection = False - self.table.set_row_spacing(0) - self.table.enable_album_view_focus_hack() - self.table.set_fixed_height(True) - self.table.set_grid_lines(False, False) - self.table.set_show_headers(False) - - c = widgetset.TableColumn("Data", ConversionCellRenderer(), - **dict((n, v) for (v, n) in enumerate(( - 'input', 'output_size', 'converter', 'status', - 'duration', 'progress', 'eta', 'thumbnail', - 'conversion')))) - c.set_min_width(TABLE_WIDTH) - self.table.add_column(c) - self.table.connect('hotspot-clicked', self.hotspot_clicked) - - # bottom buttons - converter_types = ('apple', 'android', 'other', 'format') - converters = {} - for c in self.converter_manager.list_converters(): - media_type = c.media_type - if media_type not in converter_types: - media_type = 'others' - brand = self.converter_manager.converter_to_brand(c) - # None = top level. Otherwise tack on the brand name. - if brand is None: - converters.setdefault(media_type, set()).add(c) - else: - converters.setdefault(media_type, set()).add(brand) - - self.menus = [] - - self.button_bar = widgetset.HBox() - buttons = widgetset.HBox() - - for type_ in converter_types: - options = [] - more_devices = None - for c in converters[type_]: - if isinstance(c, str): - rconverters = self.converter_manager.brand_to_converters(c) - values = [] - for r in rconverters: - values.append((r.name, r.identifier)) - # yuck - if c == 'More Devices': - more_devices = (c, values) - else: - options.append((c, values)) - else: - options.append((c.name, c.identifier)) - # Don't sort if formats.. - self.sort_converter_menu(type_, options) - if more_devices: - options.append(more_devices) - menu = SettingsButton(type_) - menu.connect('clicked', self.show_options_menu, options) - self.menus.append(menu) - buttons.pack_start(menu) - omb = OptionMenuBackground() - omb.set_child(widgetutil.pad(buttons, top=2, bottom=2, - left=2, right=2)) - self.button_bar.pack_start(omb) - - self.settings_button = SettingsButton('settings') - omb = OptionMenuBackground() - omb.set_child(widgetutil.pad(self.settings_button, top=2, - bottom=2, left=2, right=2)) - self.button_bar.pack_end(omb) - - self.drop_target = FileDropTarget() - self.drop_target.set_size_request(-1, 70) - - # # finish up - vbox = widgetset.VBox() - self.vbox = vbox - - # add menubars, if we're not on windows - if sys.platform != 'win32': - attach_menubar() - - self.scroller = widgetset.Scroller(False, True) - self.scroller.set_size_request(0, 0) - self.scroller.set_background_color(DRAG_AREA) - self.scroller.add(self.table) - vbox.pack_start(self.scroller) - vbox.pack_start(self.drop_target, expand=True) - - bottom = BottomBackground() - bottom_box = widgetset.VBox() - self.convert_label = CustomLabel('Convert to') - self.convert_label.set_font(CONVERT_TO_FONT, CONVERT_TO_FONTSIZE) - self.convert_label.set_color(TEXT_COLOR) - bottom_box.pack_start(widgetutil.align_left(self.convert_label, - top_pad=10, - bottom_pad=10)) - bottom_box.pack_start(self.button_bar) - - self.options = CustomOptions() - self.options.connect('setting-changed', self.on_setting_changed) - self.settings_button.connect('clicked', self.on_settings_toggle) - bottom_box.pack_start(widgetutil.align_right(self.options, - right_pad=5)) - - self.convert_button = ConvertButton() - self.convert_button.connect('clicked', self.convert) - - bottom_box.pack_start(widgetutil.align(self.convert_button, - xalign=0.5, yalign=0.5, - top_pad=50, bottom_pad=50)) - bottom.set_child(widgetutil.pad(bottom_box, left=20, right=20)) - vbox.pack_start(bottom) - self.window.set_content_widget(vbox) - - idle_add(self.conversion_manager.check_notifications, 1) - - self.window.connect('file-drag-motion', self.drag_motion) - self.window.connect('file-drag-received', self.drag_data_received) - self.window.connect('file-drag-leave', self.drag_finished) - self.window.accept_file_drag(True) - - self.window.center() - self.window.show() - self.update_table_size() - - def sort_converter_menu(self, menu_type, options): - """Sort a list of converter options for the menus - - :param menu_type: type of the menu - :param options: list of (name, menu) tuples, where menu is either a - ConverterInfo or list of ConverterInfos. - """ - if menu_type == 'format': - order = ['Audio', 'Video', 'Ingest Formats', 'Same Format'] - options.sort(key=lambda (name, menu): order.index(name)) - else: - options.sort() - - def drag_finished(self, widget): - self.drop_target.set_in_drag(False) - - def drag_motion(self, widget): - self.drop_target.set_in_drag(True) - - def drag_data_received(self, widget, values): - for uri in values: - parsed = urlparse.urlparse(uri) - if parsed.scheme == 'file': - pathname = urllib.url2pathname(parsed.path) - self.file_activated(widget, pathname) - - def on_window_shown(self, window): - # only emit window-shown once, even if our window gets shown, hidden, - # and shown again - if not self.sent_window_shown: - self.emit("window-shown") - self.sent_window_shown = True - - def destroy(self, widget): - for conversion in self.conversion_manager.in_progress.copy(): - conversion.stop() - mainloop_stop() - - def run(self): - mainloop_start() - - def choose_file(self): - dialog = widgetset.FileOpenDialog('Choose Files...') - dialog.set_select_multiple(True) - if dialog.run() == 0: # success - for filename in dialog.get_filenames(): - self.file_activated(None, filename) - dialog.destroy() - - def about(self): - dialog = widgetset.AboutDialog() - dialog.set_transient_for(self.window) - try: - dialog.run() - finally: - dialog.destroy() - - def quit(self): - self.window.close() - - def _generate_suboptions_menu(self, widget, options): - submenu = [] - for option, id_ in options: - callback = lambda x, i: self.on_select_converter(widget, - options[i][1]) - value = (option, callback) - submenu.append(value) - return submenu - - def show_options_menu(self, widget, options): - optionlist = [] - identifiers = dict() - for option, submenu in options: - if isinstance(submenu, list): - callback = self._generate_suboptions_menu(widget, submenu) - else: - callback = lambda x, i: self.on_select_converter(widget, - options[i][1]) - value = (option, callback) - optionlist.append(value) - menu = widgetset.ContextMenu(optionlist) - menu.popup() - - def update_convert_button(self): - can_cancel = False - can_start = False - has_conversions = any(self.model.conversions()) - all_done = self.model.all_conversions_done() - for c in self.model.conversions(): - if c.status == 'converting': - can_cancel = True - break - elif c.status == 'initialized': - can_start = True - # if there are no conversions ... these can't be set - if not has_conversions: - for m in self.menus: - m.set_selected(False) - self.settings_button.set_selected(False) - self.convert_label.set_color(TEXT_DISABLED) - # Set the colors - all are enabled if all conversions complete, or - # if we have conversions conversions but the converter has not yet - # been set. - # the converter has not been set. - if ((self.current_converter is EMPTY_CONVERTER and has_conversions) or - all_done): - for m in self.menus: - m.set_selected(True) - self.settings_button.set_selected(True) - if self.current_converter is EMPTY_CONVERTER: - self.convert_label.set_text('Convert to') - elif can_cancel: - target = self.current_converter.name - self.convert_label.set_text('Converting to %s' % target) - elif can_start: - target = self.current_converter.name - self.convert_label.set_text('Will convert to %s' % target) - self.convert_label.set_color(TEXT_ACTIVE) - if all_done: - self.convert_button.set_clear() - elif (self.current_converter is EMPTY_CONVERTER or not - (can_cancel or can_start)): - self.convert_button.set_off() - elif (self.current_converter is not EMPTY_CONVERTER and - self.options.options['custom-size'] and - (not self.options.options['width'] or - not self.options.options['height'])): - self.convert_button.set_off() - else: - self.convert_button.set_on() - if can_cancel: - self.convert_button.set_stop() - self.button_bar.disable() - else: - if has_conversions: - self.button_bar.enable() - else: - self.button_bar.disable() - - def file_activated(self, widget, filename): - filename = os.path.realpath(filename) - for c in self.model.conversions(): - if c.video.filename == filename: - logger.info('ignoring duplicate: %r', filename) - return - # XXX disabled - don't want to allow individualized file outputs - # since the workflow isn't entirely clear for now. - #if self.options.options['destination'] is None: - # try: - # tempfile.TemporaryFile(dir=os.path.dirname(filename)) - # except EnvironmentError: - # # can't write to the destination directory; ask for a new one - # self.options.on_destination_clicked(None) - try: - vf = VideoFile(filename) - except ValueError: - logging.info('invalid file %r, cannot parse', filename, - exc_info=True) - return - c = self.conversion_manager.get_conversion( - vf, - self.current_converter, - output_dir=self.options.options['destination']) - c.listen(self.update_conversion) - if self.conversion_manager.running: - # start running automatically if a conversion is already in - # progress - self.conversion_manager.run_conversion(c) - self.update_conversion(c) - self.update_table_size() - - def on_select_converter(self, widget, identifier): - self.current_converter = self.converter_manager.get_by_id(identifier) - self.options.reset() - - self.converter_changed(widget) - - def converter_changed(self, widget): - if hasattr(self, '_doing_conversion_change'): - return - self._doing_conversion_change = True - - # If all conversions are done, then change the status of them back - # to 'initialized'. - # - # XXX TODO: what happens if the state is 'failed'? Should we reset? - all_done = self.model.all_conversions_done() - if all_done: - for c in self.model.conversions(): - c.status = 'initialized' - - if self.current_converter is not EMPTY_CONVERTER: - self.convert_label.set_text( - 'Will convert to %s' % self.current_converter.name) - else: - self.convert_label.set_text('Convert to') - - if not self.current_converter.audio_only: - self.options.enable_custom_size() - self.options.update_setting('width', - self.current_converter.width) - self.options.update_setting('height', - self.current_converter.height) - else: - self.options.disable_custom_size() - - for c in self.model.conversions(): - if c.status == 'initialized': - c.set_converter(self.current_converter) - self.model.update_conversion(c) - - # We likely either reset the status or we've changed the conversion - # output so let's just reload the table model. - self.table.model_changed() - - self.update_convert_button() - - widget.set_selected(True) - for menu in self.menus: - if menu is not widget: - menu.set_selected(False) - - del self._doing_conversion_change - - def convert(self, widget): - self.convert_button.disable() - if not self.conversion_manager.running: - if self.current_converter is not EMPTY_CONVERTER: - valid_resolution = True - if (self.options.options['custom-size'] and - not (self.options.options['width'] and - self.options.options['height'])): - valid_resolution = False - if valid_resolution: - for conversion in self.model.conversions(): - if conversion.status == 'initialized': - self.conversion_manager.run_conversion(conversion) - self.button_bar.disable() - # all done: no conversion job should be running at this point - all_done = self.model.all_conversions_done() - if all_done: - # take stuff off one by one from the list until we have none! - # might not be very efficient. - iter_ = self.model.first_iter() - while iter_ is not None: - conversion = self.model[iter_][-1] - if conversion.status in ('finished', - 'failed', - 'canceled', - 'initialized'): - try: - self.conversion_manager.remove(conversion) - except ValueError: - pass - iter_ = self.model.remove(iter_) - self.update_table_size() - else: - for conversion in self.model.conversions(): - conversion.stop() - self.update_conversion(conversion) - self.conversion_manager.running = False - self.update_convert_button() - self.convert_button.enable() - - def update_conversion(self, conversion): - self.model.update_conversion(conversion) - self.update_table_size() - - def update_table_size(self): - conversions = len(self.model) - total_height = 380 - if not conversions: - self.scroller.set_size_request(-1, 0) - self.drop_target.set_small(False) - self.drop_target.set_size_request(-1, total_height) - else: - height = min(TABLE_HEIGHT * conversions, 320) - self.scroller.set_size_request(-1, height) - self.drop_target.set_small(True) - self.drop_target.set_size_request(-1, total_height - height) - self.update_convert_button() - self.table.model_changed() - - def hotspot_clicked(self, widget, name, iter_): - conversion = self.model[iter_][-1] - if name == 'show-file': - reveal_file(conversion.output) - elif name == 'clear': - self.model.remove(iter_) - self.update_table_size() - elif name == 'show-log': - lines = ''.join(conversion.lines) - d = TextDialog('Log', '', self.window) - d.set_text(lines) - try: - d.run() - finally: - d.destroy() - elif name == 'cancel': - if conversion.status == 'initialized': - self.model.remove(iter_) - try: - self.conversion_manager.remove(conversion) - except ValueError: - pass - self.update_table_size() - else: - conversion.stop() - self.update_conversion(conversion) - - def on_settings_toggle(self, widget): - if not self.options.child: - # hidden, going to show - self.convert_button.hide() - self.options.toggle() - if not self.options.child: - # was shown, not hidden - self.convert_button.show() - - def on_setting_changed(self, widget, setting, value): - if setting == 'destination': - for c in self.model.conversions(): - if c.status == 'initialized': - if value is None: - c.output_dir = os.path.dirname(c.video.filename) - else: - c.output_dir = value - # update final path - c.set_converter(self.current_converter) - return - elif setting == 'dont-upsize': - setattr(self.current_converter, 'dont_upsize', value) - return - - if (self.current_converter.identifier != 'custom' and - setting != 'create-thumbnails'): - if hasattr(self.current_converter, 'simple'): - self.current_converter = self.current_converter.simple( - self.current_converter.name) - else: - if self.current_converter is EMPTY_CONVERTER: - self.current_converter = copy.copy(self.converter_manager.get_by_id('sameformat')) - else: - self.current_converter = copy.copy(self.current_converter) - # If the current converter name is resize only, then we don't - # want to call it a custom conversion. - if self.current_converter.identifier != 'sameformat': - self.current_converter.name = 'Custom' - self.current_converter.width = self.options.options['width'] - self.current_converter.height = self.options.options['height'] - self.converter_changed(self.menus[-1]) # formats menu - if setting in ('width', 'height'): - setattr(self.current_converter, setting, value) - elif setting == 'custom-size': - if not value: - self.current_converter.old_size = ( - self.current_converter.width, - self.current_converter.height) - self.current_converter.width = None - self.current_converter.height = None - elif hasattr(self.current_converter, 'old_size'): - old_size = self.current_converter.old_size - (self.current_converter.width, - self.current_converter.height) = old_size - elif setting == 'create-thumbnails': - self.conversion_manager.create_thumbnails = bool(value) - -if __name__ == "__main__": - sys.dont_write_bytecode = True - app.widgetapp = Application() - initialize(app.widgetapp) |