Binary files /tmp/tmptFOjUn/ToBgYdiUgI/2gif-0.1.1/data/icons/bitcoin.png and /tmp/tmptFOjUn/gHHakzhr5L/2gif-0.3.0/data/icons/bitcoin.png differ Binary files /tmp/tmptFOjUn/ToBgYdiUgI/2gif-0.1.1/data/icons/flattr.png and /tmp/tmptFOjUn/gHHakzhr5L/2gif-0.3.0/data/icons/flattr.png differ Binary files /tmp/tmptFOjUn/ToBgYdiUgI/2gif-0.1.1/data/icons/paypal.png and /tmp/tmptFOjUn/gHHakzhr5L/2gif-0.3.0/data/icons/paypal.png differ diff -Nru 2gif-0.1.1/debian/2gif.install 2gif-0.3.0/debian/2gif.install --- 2gif-0.1.1/debian/2gif.install 2016-03-27 08:59:11.000000000 +0000 +++ 2gif-0.3.0/debian/2gif.install 1970-01-01 00:00:00.000000000 +0000 @@ -1,6 +0,0 @@ -src/*.py /opt/extras.ubuntu.com/2gif/bin -data/icons/2gif.png /opt/extras.ubuntu.com/2gif/share/icons -data/icons/background.svg /opt/extras.ubuntu.com/2gif/share/pixmaps -data/icons/background_none.svg /opt/extras.ubuntu.com/2gif/share/pixmaps -data/extras-2gif.desktop /usr/share/applications -debian/changelog /opt/extras.ubuntu.com/2gif/share/2gif diff -Nru 2gif-0.1.1/debian/changelog 2gif-0.3.0/debian/changelog --- 2gif-0.1.1/debian/changelog 2016-03-28 18:17:46.000000000 +0000 +++ 2gif-0.3.0/debian/changelog 2016-06-13 21:12:36.000000000 +0000 @@ -1,3 +1,27 @@ +2gif (0.3.0-0extras16.04.2) xenial; urgency=medium + + * Fixed icons + * Fixed bug in preferences dialog + + -- Lorenzo Carbonell Mon, 13 Jun 2016 23:12:16 +0200 + +2gif (0.3.0-0extras16.04.0) xenial; urgency=medium + + * Create Application and ApplicationWindow + * Added donate dialog + * Changed Buttons for MenuButtons + + -- Lorenzo Carbonell Sun, 12 Jun 2016 09:29:47 +0200 + +2gif (0.2.0-0extras16.04.0) xenial; urgency=medium + + * Added backgroud process + * Added progress dialog, can stop de convertion + * Added configuration + * Added about dialog + + -- Lorenzo Carbonell Fri, 10 Jun 2016 20:13:46 +0200 + 2gif (0.1.1-0extras15.10.1) wily; urgency=medium * Added gst for capture snapshots diff -Nru 2gif-0.1.1/debian/install 2gif-0.3.0/debian/install --- 2gif-0.1.1/debian/install 1970-01-01 00:00:00.000000000 +0000 +++ 2gif-0.3.0/debian/install 2016-06-13 18:47:56.000000000 +0000 @@ -0,0 +1,6 @@ +src/*.py /opt/extras.ubuntu.com/2gif/bin +data/icons/*.png /opt/extras.ubuntu.com/2gif/share/icons +data/icons/background.svg /opt/extras.ubuntu.com/2gif/share/pixmaps +data/icons/background_none.svg /opt/extras.ubuntu.com/2gif/share/pixmaps +data/extras-2gif.desktop /usr/share/applications +debian/changelog /opt/extras.ubuntu.com/2gif/share/2gif diff -Nru 2gif-0.1.1/src/2gif.py 2gif-0.3.0/src/2gif.py --- 2gif-0.1.1/src/2gif.py 2016-03-28 18:16:56.000000000 +0000 +++ 2gif-0.3.0/src/2gif.py 2016-06-13 21:11:42.000000000 +0000 @@ -1,7 +1,7 @@ #! /usr/bin/env python3 # -*- coding: utf-8 -*- # -# 2gif.py +# This file is part of 2gif # # Copyright (C) 2015-2016 Lorenzo Carbonell # lorenzo.carbonell.cerezo@gmail.com @@ -18,391 +18,167 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . -# -# + +import gi +try: + gi.require_version('Gtk', '3.0') +except Exception as e: + print(e) + exit(1) from gi.repository import Gtk -from gi.repository import Gdk -from gi.repository import GObject +from gi.repository import Gio +from gi.repository import GLib from gi.repository import GdkPixbuf +import sys +import os import webbrowser -import tempfile -import re +from mainwindow import MainWindow +from preferences_dialog import PreferencesDialog +from supportdialog import SupportDialog import comun -import shlex -import subprocess -import mimetypes -import os -import urllib -import urllib.request -import shutil -import threading -from videosnapshooter import VideoSnapShooter -import ctypes - - -_ = str +from comun import _ SUPPORTED_MIMES = ['video/x-ms-asf', 'video/x-msvideo', 'video/x-flv', 'video/quicktime', 'video/mp4', 'video/mpeg', 'video/x-ms-wmv', 'video/ogg', 'video/x-matroska'] -GObject.threads_init() - - -def terminate_thread(thread): - """Terminates a python thread from another thread. - - :param thread: a threading.Thread instance - """ - if thread is None or not thread.isAlive(): - thread = None - return - - exc = ctypes.py_object(SystemExit) - res = ctypes.pythonapi.PyThreadState_SetAsyncExc( - ctypes.c_long(thread.ident), exc) - thread = None - if res == 0: - raise ValueError("nonexistent thread id") - elif res > 1: - # """if it returns a number greater than one, you're in trouble, - # and you should call it again with exc=NULL to revert the effect""" - ctypes.pythonapi.PyThreadState_SetAsyncExc(thread.ident, None) - raise SystemError("PyThreadState_SetAsyncExc failed") - - -def ejecuta(comando): - print(comando) - args = shlex.split(comando) - p = subprocess.Popen(args, bufsize=10000, stdout=subprocess.PIPE) - valor = p.communicate()[0] - return valor - - -def get_thumb_for_video(vss, start_position, output, - output_image): - t = threading.Thread(target=_get_thumbnail_for_video, args=(vss, - start_position, - output, - output_image)) - t.daemon = True - t.start() - return t - - -def _get_thumbnail_for_video(vss, start_position, output, update_image): - print('a') - vss.snapshoot(output, start_position) - print('b') - pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(output, 300, 300) - print('c') - update_image.set_from_pixbuf(pixbuf) - print('d') - - -def _get_thumbnail_for_video2(filename, start_position, output, update_image): - ejecuta('avconv -i "%s" -f image2 -ss %s -vf scale=-1:300 -vframes 1 %s -y' - % (filename, start_position, output)) - update_image.set_from_file(output) - - -def get_seconds(value): - values = value.split(':') - return float(values[0]) * 3600.0 + float(values[1]) * 60.0 +\ - float(values[2]) - - -def getVideoDetails(filepath): - tmpf = tempfile.NamedTemporaryFile() - os.system("avconv -i \"%s\" 2> %s" % (filepath, tmpf.name)) - lines = tmpf.readlines() - tmpf.close() - metadata = {} - metadata['video'] = {} - metadata['audio'] = {} - print(lines) - metadata['duration'] = 0 - metadata['duration_in_seconds'] = 0 - metadata['bitrate'] = 0 - metadata['video']['resolution'] = 0 - metadata['video']['width'], metadata['video']['height'] = 0, 0 - metadata['video']['resolution'] = 0 - metadata['video']['bitrate'] = 0 - metadata['video']['fps'] = 0 - metadata['fps'] = 0 - metadata['audio']['codec'] = 0 - metadata['audio']['frequency'] = 0 - metadata['audio']['bitrate'] = 0 - for l in lines: - l = l.decode() - print(l) - l = l.strip() - if l.startswith('Duration'): - metadata['duration'] = re.search('Duration: (.*?),', l).group(0).\ - split(':', 1)[1].strip(' ,') - metadata['duration_in_seconds'] = get_seconds(metadata['duration']) - metadata['bitrate'] = re.search("bitrate: (\d+ kb/s)", l).\ - group(0).split(':')[1].strip() - if l.startswith('Stream #') and l.split(': ') == 'Video': - metadata['video'] = {} - try: - metadata['video']['codec'], metadata['video']['profile'] = \ - [e.strip(' , ()') for e in re.search( - 'Video: (.*? \(.*?\)),? ', l).group(0).split(':')[1]. - split('(')] - metadata['video']['resolution'] = re.search('([1-9]\d+x\d+)', - l).group(1) - metadata['video']['width'], metadata['video']['height'] =\ - metadata['video']['resolution'].split('x') - metadata['video']['width'] = float(metadata['video']['width']) - metadata['video']['height'] = \ - float(metadata['video']['height']) - metadata['video']['bitrate'] = re.search('(\d+ kb/s)', - l).group(1) - metadata['video']['fps'] = re.search('(\d+ fps)', l).group(1) - metadata['fps'] = int(metadata['video']['fps'].split(' ')[0]) - except Exception: - pass - if l.startswith('Stream #') and l.split(': ') == 'Audio': - try: - metadata['audio'] = {} - metadata['audio']['codec'] = re.search('Audio: (.*?) ', - l).group(1) - metadata['audio']['frequency'] = re.search(', (.*? Hz),', - l).group(1) - metadata['audio']['bitrate'] = re.search(', (\d+ kb/s)', - l).group(1) - except Exception: - pass - return metadata - -class Convert2GifDialog(Gtk.Window): +class MainApplication(Gtk.Application): def __init__(self): - self.code = None - Gtk.Window.__init__(self) - self.set_position(Gtk.WindowPosition.CENTER_ALWAYS) - self.set_title(comun.APPNAME) - self.set_icon_from_file(comun.ICON) - self.set_default_size(300, 300) - self.connect('destroy', self.on_destroy) - # - self.vbox = Gtk.VBox(False, 2) - self.add(self.vbox) - table = Gtk.Table(4, 2, False) - self.vbox.pack_end(table, False, False, 0) - self.begin = Gtk.Image.new_from_file(comun.BACKGROUND) - self.begin.set_tooltip_text('Drag and drop video here') - table.attach(self.begin, 0, 1, 0, 1, xpadding=5, ypadding=5) - self.end = Gtk.Image.new_from_file(comun.BACKGROUND_NONE) - table.attach(self.end, 1, 2, 0, 1, xpadding=5, ypadding=5) - # set icon for drag operation - self.begin.connect('drag-begin', self.drag_begin) - self.begin.connect('drag-data-get', self.drag_data_get_data) - self.begin.connect('drag-data-received', self.drag_data_received) - # - dnd_list = [Gtk.TargetEntry.new('text/uri-list', 0, 100), - Gtk.TargetEntry.new('text/plain', 0, 80)] - self.begin.drag_source_set(Gdk.ModifierType.BUTTON1_MASK, - dnd_list, - Gdk.DragAction.COPY) - self.begin.drag_source_add_uri_targets() - dnd_list = Gtk.TargetEntry.new("text/uri-list", 0, 0) - self.begin.drag_dest_set(Gtk.DestDefaults.MOTION | - Gtk.DestDefaults.HIGHLIGHT | - Gtk.DestDefaults.DROP, - [dnd_list], - Gdk.DragAction.MOVE) - self.begin.drag_dest_add_uri_targets() - # - self.spinbutton_begin = Gtk.SpinButton() - self.spinbutton_begin.set_tooltip_text('First second') - self.spinbutton_begin.set_editable(False) - table.attach(self.spinbutton_begin, 0, 1, 1, 2, - xpadding=5, - ypadding=5, - xoptions=Gtk.AttachOptions.SHRINK) - self.spinbutton_end = Gtk.SpinButton() - self.spinbutton_end.set_tooltip_text('Last second') - self.spinbutton_end.set_editable(False) - table.attach(self.spinbutton_end, 1, 2, 1, 2, - xpadding=5, - ypadding=5, - xoptions=Gtk.AttachOptions.SHRINK) - # - self.button_output = Gtk.Button( - os.path.join(os.path.expanduser('~'), 'output.gif')) - self.button_output.connect('clicked', self.on_button_output_clicked) - table.attach(self.button_output, 0, 2, 2, 3, - xpadding=5, - ypadding=5, - xoptions=Gtk.AttachOptions.SHRINK) - # - self.button_ok = Gtk.Button('Ok') - self.button_ok.connect('clicked', self.on_button_ok_clicked) - table.attach(self.button_ok, 0, 1, 3, 4, - xpadding=5, - ypadding=5, - xoptions=Gtk.AttachOptions.SHRINK) - self.button_cancel = Gtk.Button('Cancel') - self.button_cancel.connect('clicked', self.on_button_cancel_clicked) - table.attach(self.button_cancel, 1, 2, 3, 4, - xpadding=5, - ypadding=5, - xoptions=Gtk.AttachOptions.SHRINK) - # - self.duration_in_seconds = 0 - self.fps = 0 - self.filename = None - self.first_image_thread = None - self.last_image_thread = None - self.tmp_folder = tempfile.mkdtemp() - if os.path.exists(self.tmp_folder): - shutil.rmtree(self.tmp_folder) - os.makedirs(self.tmp_folder) - # - self.init_menu() - # - self.spinbutton_begin.connect('value-changed', - self.on_spinbutton_begin_value_changed) - self.spinbutton_end.connect('value-changed', - self.on_spinbutton_end_value_changed) - self.show_all() + Gtk.Application.__init__( + self, + application_id='es.atareao.togif', + flags=Gio.ApplicationFlags.FLAGS_NONE + ) + self.license_type = Gtk.License.GPL_3_0 + print(3) + + def do_shutdown(self): + Gtk.Application.do_shutdown(self) + + def on_quit(self, widget, data): + self.quit() + + def do_startup(self): + Gtk.Application.do_startup(self) + print('do_startup') + + def create_action(name, + callback=self.action_clicked, + var_type=None, + value=None): + if var_type is None: + action = Gio.SimpleAction.new(name, None) + else: + action = Gio.SimpleAction.new_stateful( + name, + GLib.VariantType.new(var_type), + GLib.Variant(var_type, value) + ) + action.connect('activate', callback) + return action + + self.add_action(create_action("add")) + self.add_action(create_action("update")) + self.add_action(create_action("quit", callback=lambda *_: self.quit())) + self.add_action(create_action("save")) + self.add_action(create_action("set_interval", var_type='i', value=30)) + + self.set_accels_for_action('app.add', ['A']) + self.set_accels_for_action('app.quit', ['Q']) + + self.add_action(create_action( + 'open_file', + callback=self.on_open_file_clicked)) + self.add_action(create_action( + 'set_preferences', + callback=self.on_preferences_clicked)) + self.add_action(create_action( + 'goto_homepage', + callback=lambda x, y: webbrowser.open( + 'http://www.atareao.es/apps/\ +crear-un-gif-animado-de-un-video-en-ubuntu-en-un-solo-clic/'))) + self.add_action(create_action( + 'goto_bug', + callback=lambda x, y: webbrowser.open( + 'https://bugs.launchpad.net/2gif'))) + self.add_action(create_action( + 'goto_sugestion', + callback=lambda x, y: webbrowser.open( + 'https://blueprints.launchpad.net/2gif'))) + self.add_action(create_action( + 'goto_translation', + callback=lambda x, y: webbrowser.open( + 'https://translations.launchpad.net/2gif'))) + self.add_action(create_action( + 'goto_questions', + callback=lambda x, y: webbrowser.open( + 'https://answers.launchpad.net/2gif'))) + self.add_action(create_action( + 'goto_twitter', + callback=lambda x, y: webbrowser.open( + 'https://twitter.com/atareao'))) + self.add_action(create_action( + 'goto_google_plus', + callback=lambda x, y: webbrowser.open( + 'https://plus.google.com/\ +118214486317320563625/posts'))) + self.add_action(create_action( + 'goto_facebook', + callback=lambda x, y: webbrowser.open( + 'http://www.facebook.com/elatareao'))) + self.add_action(create_action( + 'goto_donate', + callback=self.on_support_clicked)) + self.add_action(create_action( + 'about', + callback=self.on_about_activate)) + + def do_activate(self): + print('activate') + self.win = MainWindow(self) + self.add_window(self.win) + self.win.show() + + def action_clicked(self, action, variant): + print(action, variant) + if variant: + action.set_state(variant) + + def on_support_clicked(self, widget, optional): + dialog = SupportDialog(self.win) + dialog.run() + dialog.destroy() - def load_file_dialog(self): - filename = None - dialog = Gtk.FileChooserDialog("Open file", - self, - Gtk.FileChooserAction.OPEN, - (Gtk.STOCK_CANCEL, - Gtk.ResponseType.CANCEL, - Gtk.STOCK_OPEN, - Gtk.ResponseType.OK)) - dialog.set_current_folder(os.path.expanduser("~")) - filter_video = Gtk.FileFilter() - filter_video.set_name(_('Video files')) - for amimetype in SUPPORTED_MIMES: - filter_video.add_mime_type(amimetype) - dialog.add_filter(filter_video) - response = dialog.run() + def on_open_file_clicked(self, widget, optional): + filename = self.load_file_dialog() + if filename is not None: + self.win.process_file(filename) - if response == Gtk.ResponseType.OK: + def on_button_output_clicked(self, widget): + dialog = Gtk.FileChooserDialog('Select output file', + self.win, Gtk.FileChooserAction.SAVE, + (Gtk.STOCK_CANCEL, + Gtk.ResponseType.REJECT, + Gtk.STOCK_OK, Gtk.ResponseType.ACCEPT)) + dialog.set_position(Gtk.WindowPosition.CENTER_ALWAYS) + dialog.set_filename(self.button_output.get_label()) + dialog.set_current_name(self.button_output.get_label().split('/')[-1]) + filter = Gtk.FileFilter() + filter.set_name('Gif file') + filter.add_mime_type("image/gif") + dialog.add_filter(filter) + if dialog.run() == Gtk.ResponseType.ACCEPT: + dialog.hide() filename = dialog.get_filename() - elif response == Gtk.ResponseType.CANCEL: - pass + if not filename.endswith('.gif'): + filename += '.gif' + self.win.button_output.set_label(filename) dialog.destroy() - return filename - - def init_menu(self): - menubar = Gtk.MenuBar() - self.vbox.pack_start(menubar, False, False, 0) - accel_group = Gtk.AccelGroup() - self.add_accel_group(accel_group) - self.filemenu = Gtk.Menu.new() - self.filem = Gtk.MenuItem.new_with_label(_('File')) - self.filem.set_submenu(self.filemenu) - # - self.menus = {} - # - self.menus['open-file'] = Gtk.ImageMenuItem.new_with_label(_( - 'Open file')) - self.menus['open-file'].connect('activate', - self.on_toolbar_clicked, - 'open-file') - self.menus['open-file'].add_accelerator('activate', - accel_group, - ord('O'), - Gdk.ModifierType.CONTROL_MASK, - Gtk.AccelFlags.VISIBLE) - self.filemenu.append(self.menus['open-file']) - # - self.filemenu.append(Gtk.SeparatorMenuItem()) - menubar.append(self.filem) - # - self.filehelp = Gtk.Menu.new() - self.fileh = Gtk.MenuItem.new_with_label(_('Help')) - self.fileh.set_submenu(self.get_help_menu()) - # - menubar.append(self.fileh) - - def get_help_menu(self): - help_menu = Gtk.Menu() - # - homepage_item = Gtk.MenuItem(label=_( - 'Homepage')) - homepage_item.connect('activate', - lambda x: webbrowser.open( - 'http://www.atareao.es/')) - homepage_item.show() - help_menu.append(homepage_item) - # - help_item = Gtk.MenuItem(label=_( - 'Get help online...')) - help_item.connect('activate', - lambda x: webbrowser.open( - 'https://answers.launchpad.net/utext')) - help_item.show() - help_menu.append(help_item) - # - translate_item = Gtk.MenuItem(label=_( - 'Translate this application...')) - translate_item.connect('activate', - lambda x: webbrowser.open( - 'https://translations.launchpad.net/utext')) - translate_item.show() - help_menu.append(translate_item) - # - bug_item = Gtk.MenuItem(label=_( - 'Report a bug...')) - bug_item.connect('activate', - lambda x: webbrowser.open( - 'https://bufs.launchpad.net/utext')) - bug_item.show() - help_menu.append(bug_item) - # - separator = Gtk.SeparatorMenuItem() - separator.show() - help_menu.append(separator) - # - twitter_item = Gtk.MenuItem(label=_( - 'Follow me in Twitter')) - twitter_item.connect('activate', - lambda x: webbrowser.open( - 'https://twitter.com/atareao')) - twitter_item.show() - help_menu.append(twitter_item) - # - googleplus_item = Gtk.MenuItem(label=_( - 'Follow me in Google+')) - googleplus_item.connect('activate', - lambda x: webbrowser.open( - 'https://plus.google.com/\ -118214486317320563625/posts')) - googleplus_item.show() - help_menu.append(googleplus_item) - # - facebook_item = Gtk.MenuItem(label=_( - 'Follow me in Facebook')) - facebook_item.connect('activate', - lambda x: webbrowser.open( - 'http://www.facebook.com/elatareao')) - facebook_item.show() - help_menu.append(facebook_item) - # - about_item = Gtk.MenuItem.new_with_label(_('About')) - about_item.connect('activate', self.on_about_activate) - about_item.show() - separator = Gtk.SeparatorMenuItem() - separator.show() - help_menu.append(separator) - help_menu.append(about_item) - # - help_menu.show() - return help_menu - def on_about_activate(self, widget): + def on_about_activate(self, widget, optional): """Create and populate the about dialog.""" - about_dialog = Gtk.AboutDialog(parent=self) + about_dialog = Gtk.AboutDialog(parent=self.win) about_dialog.set_name(comun.APPNAME) about_dialog.set_version(comun.VERSION) about_dialog.set_copyright( @@ -436,167 +212,40 @@ about_dialog.run() about_dialog.destroy() - def on_toolbar_clicked(self, widget, option): - if option == 'open-file': - filename = self.load_file_dialog() - if filename is not None: - self.process_file(filename) + def on_preferences_clicked(self, widget, optional): + cm = PreferencesDialog(self.win) + if cm.run() == Gtk.ResponseType.ACCEPT: + cm.close_ok() + cm.destroy() - def on_button_output_clicked(self, widget): - dialog = Gtk.FileChooserDialog('Select output file', - None, Gtk.FileChooserAction.SAVE, + def load_file_dialog(self): + filename = None + dialog = Gtk.FileChooserDialog(_('Open file'), + self.win, + Gtk.FileChooserAction.OPEN, (Gtk.STOCK_CANCEL, - Gtk.ResponseType.REJECT, - Gtk.STOCK_OK, Gtk.ResponseType.ACCEPT)) - dialog.set_position(Gtk.WindowPosition.CENTER_ALWAYS) - dialog.set_filename(self.button_output.get_label()) - dialog.set_current_name(self.button_output.get_label().split('/')[-1]) - filter = Gtk.FileFilter() - filter.set_name('Gif file') - filter.add_mime_type("image/gif") - dialog.add_filter(filter) - if dialog.run() == Gtk.ResponseType.ACCEPT: - dialog.hide() + Gtk.ResponseType.CANCEL, + Gtk.STOCK_OPEN, + Gtk.ResponseType.OK)) + dialog.set_current_folder(os.path.expanduser("~")) + filter_video = Gtk.FileFilter() + filter_video.set_name(_('Video files')) + for amimetype in SUPPORTED_MIMES: + filter_video.add_mime_type(amimetype) + dialog.add_filter(filter_video) + response = dialog.run() + + if response == Gtk.ResponseType.OK: filename = dialog.get_filename() - if not filename.endswith('.gif'): - filename += '.gif' - self.button_output.set_label(filename) + elif response == Gtk.ResponseType.CANCEL: + pass dialog.destroy() + return filename + + +def main(): + app = MainApplication() + app.run('') - def on_destroy(self, widget): - if os.path.exists(self.tmp_folder): - shutil.rmtree(self.tmp_folder) - if self.first_image_thread is not None: - self.first_image_thread.kill() - if self.last_image_thread is not None: - self.last_image_thread.kill() - - def on_spinbutton_begin_value_changed(self, widget): - terminate_thread(self.first_image_thread) - if self.first_image_thread is None: - value = self.spinbutton_begin.get_value() - temp_begin_image = os.path.join(self.tmp_folder, 'begin.png') - if os.path.exists(temp_begin_image): - os.remove(temp_begin_image) - self.first_image_thread = \ - _get_thumbnail_for_video(self.vss, - value, - temp_begin_image, - self.begin) - - def on_spinbutton_end_value_changed(self, widget): - terminate_thread(self.last_image_thread) - if self.last_image_thread is None: - value = self.spinbutton_end.get_value() - temp_end_image = os.path.join(self.tmp_folder, 'end.png') - if os.path.exists(temp_end_image): - os.remove(temp_end_image) - self.last_image_thread = \ - _get_thumbnail_for_video(self.vss, - value, - temp_end_image, - self.end) - - def on_button_ok_clicked(self, widget): - start_position = self.spinbutton_begin.get_value() - end_position = self.spinbutton_end.get_value() - duration = end_position - start_position - output_file = self.button_output.get_label() - if os.path.exists(output_file): - os.remove(output_file) - if os.path.exists(output_file + '.tmp'): - os.remove(output_file + '.tmp') - if self.filename is not None and\ - os.path.exists(self.filename) and duration > 0: - ''' - self.vss.snapshootrate(self.tmp_folder, - 'out', - start_position, - end_position, - 1) - ''' - ejecuta('avconv -t %s -ss %s -i "%s" -r 1/1 "%s"' % - (duration, start_position, self.filename, - os.path.join(self.tmp_folder, 'out%04d.png'))) - ejecuta('convert -delay 1x1 -loop 0 "%s" "%s"' % - (os.path.join(self.tmp_folder, 'out*.png'), - os.path.join(self.tmp_folder, 'temporal.gif'))) - ejecuta('convert -layers Optimize "%s" "%s"' % - (os.path.join(self.tmp_folder, 'temporal.gif'), - output_file)) - self.on_destroy() - exit() - - def on_button_cancel_clicked(self, widget): - self.on_destroy() - exit() - - def drag_begin(self, widget, context): - pass - - def drag_data_get_data(self, treeview, context, selection, target_id, - etime): - pass - - def drag_data_received(self, widget, drag_context, x, y, selection_data, - info, timestamp): - if len(selection_data.get_uris()) > 0: - filename = selection_data.get_uris()[0] - if len(filename) > 8: - filename = urllib.request.url2pathname(filename) - self.process_file(filename[7:]) - return True - - def process_file(self, filename): - self.filename = filename - self.vss = VideoSnapShooter(filename) - mime = mimetypes.guess_type(self.filename) - if os.path.exists(self.filename): - mime = mimetypes.guess_type(self.filename)[0] - if mime in SUPPORTED_MIMES: - if self.first_image_thread is not None: - self.first_image_thread.kill() - if self.last_image_thread is not None: - self.last_image_thread.kill() - self.duration_in_seconds = int(self.vss.get_duration()) - self.spinbutton_begin.set_adjustment( - Gtk.Adjustment(0, 0, self.duration_in_seconds, - 1, 10, 0)) - self.spinbutton_begin.set_value(0) - self.spinbutton_begin.set_editable(True) - self.spinbutton_end.set_adjustment( - Gtk.Adjustment(self.duration_in_seconds, 0, - self.duration_in_seconds, 1, - 10, 0)) - self.spinbutton_end.set_value(self.duration_in_seconds) - self.spinbutton_end.set_editable(True) - temp_begin_image = os.path.join(self.tmp_folder, 'begin.png') - temp_end_image = os.path.join(self.tmp_folder, 'end.png') - ''' - self.vss.snapshoot(temp_begin_image, 0) - set_image_from_file_at_size(self.end, temp_end_image, 300, 300) - #self.begin.set_from_file(temp_begin_image) - self.vss.snapshoot(temp_end_image, self.duration_in_seconds) - set_image_from_file_at_size(self.end, temp_end_image, 300, 300) - #self.end.set_from_file(temp_end_image) - ''' - ''' - print(1) - terminate_thread(self.first_image_thread) - self.first_image_thread = \ - get_thumb_for_video(self.vss, - 0, - temp_begin_image, - self.begin) - print(2) - terminate_thread(self.last_image_thread) - self.last_image_thread = \ - get_thumb_for_video(self.vss, - self.duration_in_seconds, - temp_end_image, - self.end) - print(3) - ''' if __name__ == '__main__': - ld = Convert2GifDialog() - Gtk.main() + main() diff -Nru 2gif-0.1.1/src/comun.py 2gif-0.3.0/src/comun.py --- 2gif-0.1.1/src/comun.py 2016-03-27 09:02:13.000000000 +0000 +++ 2gif-0.3.0/src/comun.py 2016-06-12 07:25:26.000000000 +0000 @@ -1,9 +1,9 @@ -#! /usr/bin/python3 -# -*- coding: iso-8859-1 -*- +#! /usr/bin/env python3 +# -*- coding: utf-8 -*- # -# com.py +# This file is part of 2gif # -# Copyright (C) 2012 Lorenzo Carbonell +# Copyright (C) 2015-2016 Lorenzo Carbonell # lorenzo.carbonell.cerezo@gmail.com # # This program is free software: you can redistribute it and/or modify @@ -18,9 +18,6 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . -# -# -# import os import locale @@ -34,7 +31,12 @@ PARAMS = {'first-time': True, 'version': '', - 'sample-time': 5 + 'frames': 8, + 'scale': False, + 'modify-width': True, + 'modify-height': True, + 'width': 1024, + 'height': 600, } APP = '2gif' @@ -42,6 +44,7 @@ APP_CONF = APP + '.conf' CONFIG_DIR = os.path.join(os.path.expanduser('~'), '.config') CONFIG_APP_DIR = os.path.join(CONFIG_DIR, APP) +CONFIG_FILE = os.path.join(CONFIG_APP_DIR, APP_CONF) # check if running from source if is_package(): ROOTDIR = '/opt/extras.ubuntu.com/2gif' @@ -62,7 +65,11 @@ DEBIANDIR = os.path.normpath(os.path.join(ROOTDIR, '../debian')) CHANGELOG = os.path.join(DEBIANDIR, 'changelog') # +HTML = os.path.join(APPDIR, 'main.html') ICON = os.path.join(ICONDIR, '2gif.png') +BITCOIN_LOGO = os.path.join(ICONDIR, 'bitcoin.png') +FLATTR_LOGO = os.path.join(ICONDIR, 'flattr.png') +PAYPAL_LOGO = os.path.join(ICONDIR, 'paypal.png') BACKGROUND = os.path.join(PIXMAPDIR, 'background.svg') BACKGROUND_NONE = os.path.join(PIXMAPDIR, 'background_none.svg') # diff -Nru 2gif-0.1.1/src/configurator.py 2gif-0.3.0/src/configurator.py --- 2gif-0.1.1/src/configurator.py 2016-03-27 09:00:47.000000000 +0000 +++ 2gif-0.3.0/src/configurator.py 2016-06-12 07:25:26.000000000 +0000 @@ -1,12 +1,10 @@ -#!/usr/bin/env python3 +#! /usr/bin/env python3 # -*- coding: utf-8 -*- # -# configurator.py -# # This file is part of 2gif # -# Copyright (C) 2016 -# Lorenzo Carbonell Cerezo +# Copyright (C) 2015-2016 Lorenzo Carbonell +# lorenzo.carbonell.cerezo@gmail.com # # 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 diff -Nru 2gif-0.1.1/src/convertvideo.py 2gif-0.3.0/src/convertvideo.py --- 2gif-0.1.1/src/convertvideo.py 1970-01-01 00:00:00.000000000 +0000 +++ 2gif-0.3.0/src/convertvideo.py 2016-06-11 09:01:49.000000000 +0000 @@ -0,0 +1,78 @@ +#! /usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# This file is part of 2gif +# +# Copyright (C) 2015-2016 Lorenzo Carbonell +# lorenzo.carbonell.cerezo@gmail.com +# +# 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 . + +from idleobject import IdleObject +from threading import Thread +from gi.repository import GObject +import subprocess +import re +import shlex + + +def timestr_to_seconds(time_str): + hours, minutes, seconds = time_str.split(':') + return float(hours) * 3600.0 + float(minutes) * 60.0 + float(seconds) + + +class ConvertVideo(IdleObject, Thread): + __gsignals__ = { + 'processed': (GObject.SIGNAL_RUN_FIRST, GObject.TYPE_NONE, (int,)), + 'interrupted': (GObject.SIGNAL_RUN_FIRST, GObject.TYPE_NONE, ()), + 'finished': (GObject.SIGNAL_RUN_FIRST, GObject.TYPE_NONE, ()), + } + + def __init__(self, input_file, output_file, start_position, duration, + frames, scale_t): + IdleObject.__init__(self) + Thread.__init__(self) + self.input_file = input_file + self.output_file = output_file + self.start_position = start_position + self.duration = duration + self.frames = frames + self.scale_t = scale_t + self.daemon = True + self.stop = False + + def stop_it(self, anobject=None): + self.stop = True + + def run(self): + comando = 'avconv -t %s -ss %s -i "%s" -r %s %s "%s"' % ( + self.duration, self.start_position, self.input_file, self.frames, + self.scale_t, self.output_file) + args = shlex.split(comando) + p = subprocess.Popen(args, stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, close_fds=True) + contador = 0 + for line in p.stdout: + line = line.decode().rstrip() + m = re.search('time=([0-9,\.\:]+)\ bitrate=', line) + if m: + contador += 1 + found = m.group(1) + self.emit('processed', + int(timestr_to_seconds(found)/float(98)*100.0)) + if self.stop: + p.kill() + self.emit('interrupted') + self.emit('finished') diff -Nru 2gif-0.1.1/src/getthumbnailforvideo.py 2gif-0.3.0/src/getthumbnailforvideo.py --- 2gif-0.1.1/src/getthumbnailforvideo.py 1970-01-01 00:00:00.000000000 +0000 +++ 2gif-0.3.0/src/getthumbnailforvideo.py 2016-06-12 07:25:26.000000000 +0000 @@ -0,0 +1,65 @@ +#! /usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# This file is part of 2gif +# +# Copyright (C) 2015-2016 Lorenzo Carbonell +# lorenzo.carbonell.cerezo@gmail.com +# +# 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 . + +import gi +try: + gi.require_version('GdkPixbuf', '2.0') +except Exception as e: + print(e) + exit(1) +from idleobject import IdleObject +from threading import Thread +from gi.repository import GObject +from gi.repository import GdkPixbuf +from videosnapshooter import VideoSnapShooter +import time + +class GetThumbnailForVideo(IdleObject, Thread): + __gsignals__ = { + 'finished': (GObject.SIGNAL_RUN_FIRST, + GObject.TYPE_NONE, + (GdkPixbuf.Pixbuf,)) + } + + def __init__(self, input_file, output_file, start_position): + IdleObject.__init__(self) + Thread.__init__(self) + self.input_file = input_file + self.output_file = output_file + self.start_position = start_position + self.daemon = True + + def run(self): + emited = False + contador = 0 + while ((not emited) and (contador < 3)): + try: + contador += 1 + vss = VideoSnapShooter(self.input_file) + vss.snapshoot(self.output_file, self.start_position) + time.sleep(0.5) + pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size( + self.output_file, 300, 300) + self.emit('finished', pixbuf) + emited = True + except Exception as e: + time.sleep(0.5) + print(e) diff -Nru 2gif-0.1.1/src/gstvideo.py 2gif-0.3.0/src/gstvideo.py --- 2gif-0.1.1/src/gstvideo.py 2016-03-27 16:14:26.000000000 +0000 +++ 2gif-0.3.0/src/gstvideo.py 2016-06-12 07:25:26.000000000 +0000 @@ -1,12 +1,10 @@ -#!/usr/bin/python3 +#! /usr/bin/env python3 # -*- coding: utf-8 -*- # -# webcam.py +# This file is part of 2gif # -# This file is part of Backlight Indicator -# -# Copyright (C) 2016 -# Lorenzo Carbonell Cerezo +# Copyright (C) 2015-2016 Lorenzo Carbonell +# lorenzo.carbonell.cerezo@gmail.com # # 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 diff -Nru 2gif-0.1.1/src/idleobject.py 2gif-0.3.0/src/idleobject.py --- 2gif-0.1.1/src/idleobject.py 1970-01-01 00:00:00.000000000 +0000 +++ 2gif-0.3.0/src/idleobject.py 2016-06-12 07:25:26.000000000 +0000 @@ -0,0 +1,35 @@ +#! /usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# This file is part of 2gif +# +# Copyright (C) 2015-2016 Lorenzo Carbonell +# lorenzo.carbonell.cerezo@gmail.com +# +# 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 . + +from gi.repository import GObject +from gi.repository import GLib + + +class IdleObject(GObject.GObject): + """ + Override GObject.GObject to always emit signals in the main thread + by emmitting on an idle handler + """ + def __init__(self): + GObject.GObject.__init__(self) + + def emit(self, *args): + GLib.idle_add(GObject.GObject.emit, self, *args) diff -Nru 2gif-0.1.1/src/mainwindow.py 2gif-0.3.0/src/mainwindow.py --- 2gif-0.1.1/src/mainwindow.py 1970-01-01 00:00:00.000000000 +0000 +++ 2gif-0.3.0/src/mainwindow.py 2016-06-12 07:25:26.000000000 +0000 @@ -0,0 +1,398 @@ +#! /usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# This file is part of 2gif +# +# Copyright (C) 2015-2016 Lorenzo Carbonell +# lorenzo.carbonell.cerezo@gmail.com +# +# 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 . + +import gi +try: + gi.require_version('Gtk', '3.0') + gi.require_version('Gst', '1.0') +except Exception as e: + print(e) + exit(1) +from gi.repository import Gtk +from gi.repository import Gdk +from gi.repository import Gio +from gi.repository import GObject +import os +import tempfile +import shutil +import urllib +import urllib.request +import mimetypes +import comun +from comun import _ +from videosnapshooter import VideoSnapShooter +from getthumbnailforvideo import GetThumbnailForVideo +from configurator import Configuration +from progreso import Progreso +from convertvideo import ConvertVideo + +SUPPORTED_MIMES = ['video/x-ms-asf', 'video/x-msvideo', 'video/x-flv', + 'video/quicktime', 'video/mp4', 'video/mpeg', + 'video/x-ms-wmv', 'video/ogg', 'video/x-matroska'] + + +class MainWindow(Gtk.ApplicationWindow): + def __init__(self, app): + Gtk.ApplicationWindow.__init__(self, application=app) + self.set_position(Gtk.WindowPosition.CENTER_ALWAYS) + self.set_icon_from_file(comun.ICON) + self.connect('destroy', self.on_destroy) + # + hb = Gtk.HeaderBar() + hb.set_show_close_button(True) + hb.props.title = comun.APPNAME + self.set_titlebar(hb) + + menu_button_open = Gio.Menu() + menu_button_open.append_item( + Gio.MenuItem.new(_('Open video file'), 'app.open_file')) + + button_open = Gtk.MenuButton() + button_open.add(Gtk.Image.new_from_gicon( + Gio.ThemedIcon.new_with_default_fallbacks( + 'document-open-symbolic'), + Gtk.IconSize.BUTTON)) + button_open.set_menu_model(menu_button_open) + hb.pack_start(button_open) + # + menu_button_about = Gio.Menu() + menu_button_about.append_item( + Gio.MenuItem.new(_('Homepage'), 'app.goto_homepage')) + menu_button_about.append_item( + Gio.MenuItem.new(_('Report a bug'), 'app.goto_bug')) + menu_button_about.append_item( + Gio.MenuItem.new(_('Make a suggestion'), + 'app.goto_sugestion')) + menu_button_about.append_item( + Gio.MenuItem.new(_('Translate this application'), + 'app.goto_translation')) + menu_button_about.append_item( + Gio.MenuItem.new(_('Get help online'), + 'app.goto_questions')) + menu_button_about.append_item( + Gio.MenuItem.new('', None)) + menu_button_about.append_item( + Gio.MenuItem.new(_('Follow me on Twitter'), + 'app.goto_twitter')) + menu_button_about.append_item( + Gio.MenuItem.new(_('Follow me on Facebook'), + 'app.goto_facebook')) + menu_button_about.append_item( + Gio.MenuItem.new(_('Follow me on Google+'), + 'app.goto_google_plus')) + menu_button_about.append_item( + Gio.MenuItem.new('', None)) + menu_button_about.append_item( + Gio.MenuItem.new(_('Donate'), + 'app.goto_donate')) + menu_button_about.append_item( + Gio.MenuItem.new('', None)) + menu_button_about.append_item( + Gio.MenuItem.new(_('About...'), + 'app.about')) + + button_about = Gtk.MenuButton() + button_about.add( + Gtk.Image.new_from_gicon( + Gio.ThemedIcon.new_with_default_fallbacks( + 'dialog-information-symbolic'), + Gtk.IconSize.BUTTON)) + button_about.set_menu_model(menu_button_about) + hb.pack_end(button_about) + + menu_button_preferences = Gio.Menu() + menu_button_preferences.append_item( + Gio.MenuItem.new(_('Configuration'), 'app.set_preferences')) + + button_preferences = Gtk.MenuButton() + button_preferences.add( + Gtk.Image.new_from_gicon( + Gio.ThemedIcon.new_with_default_fallbacks( + 'preferences-system-symbolic'), + Gtk.IconSize.BUTTON)) + button_preferences.set_menu_model(menu_button_preferences) + hb.pack_end(button_preferences) + # + hbox = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, 5) + self.add(hbox) + self.vbox = Gtk.Box.new(Gtk.Orientation.VERTICAL, 5) + hbox.pack_start(self.vbox, False, False, 5) + frame = Gtk.Frame() + self.vbox.pack_start(frame, True, True, 5) + table = Gtk.Table(4, 2, False) + frame.add(table) + self.begin = Gtk.Image.new_from_file(comun.BACKGROUND) + self.begin.set_tooltip_text(_('Drag and drop video here')) + table.attach(self.begin, 0, 1, 0, 1, + xpadding=5, + ypadding=5, + xoptions=Gtk.AttachOptions.SHRINK, + yoptions=Gtk.AttachOptions.EXPAND) + self.end = Gtk.Image.new_from_file(comun.BACKGROUND_NONE) + table.attach(self.end, 1, 2, 0, 1, + xpadding=5, + ypadding=5, + xoptions=Gtk.AttachOptions.SHRINK, + yoptions=Gtk.AttachOptions.EXPAND) + + # set icon for drag operation + self.begin.connect('drag-begin', self.drag_begin) + self.begin.connect('drag-data-get', self.drag_data_get_data) + self.begin.connect('drag-data-received', self.drag_data_received) + # + dnd_list = [Gtk.TargetEntry.new('text/uri-list', 0, 100), + Gtk.TargetEntry.new('text/plain', 0, 80)] + self.begin.drag_source_set(Gdk.ModifierType.BUTTON1_MASK, + dnd_list, + Gdk.DragAction.COPY) + self.begin.drag_source_add_uri_targets() + dnd_list = Gtk.TargetEntry.new("text/uri-list", 0, 0) + self.begin.drag_dest_set(Gtk.DestDefaults.MOTION | + Gtk.DestDefaults.HIGHLIGHT | + Gtk.DestDefaults.DROP, + [dnd_list], + Gdk.DragAction.MOVE) + self.begin.drag_dest_add_uri_targets() + # + self.spinbutton_begin = Gtk.SpinButton() + self.spinbutton_begin.set_tooltip_text(_('First second')) + self.spinbutton_begin.set_editable(False) + table.attach(self.spinbutton_begin, 0, 1, 1, 2, + xpadding=5, + ypadding=5, + xoptions=Gtk.AttachOptions.SHRINK, + yoptions=Gtk.AttachOptions.SHRINK) + self.spinbutton_end = Gtk.SpinButton() + self.spinbutton_end.set_tooltip_text(_('Last second')) + self.spinbutton_end.set_editable(False) + table.attach(self.spinbutton_end, 1, 2, 1, 2, + xpadding=5, + ypadding=5, + xoptions=Gtk.AttachOptions.SHRINK, + yoptions=Gtk.AttachOptions.SHRINK) + # + self.button_output = Gtk.Button( + os.path.join(os.path.expanduser('~'), 'output.gif')) + self.button_output.connect('clicked', self.on_button_output_clicked) + table.attach(self.button_output, 0, 2, 2, 3, + xpadding=5, + ypadding=5, + xoptions=Gtk.AttachOptions.SHRINK, + yoptions=Gtk.AttachOptions.SHRINK) + # + hbox = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, 5) + self.vbox.pack_start(hbox, False, False, 5) + + self.button_ok = Gtk.Button.new_from_stock(Gtk.STOCK_EXECUTE) + self.button_ok.connect('clicked', self.on_button_ok_clicked) + hbox.pack_start(self.button_ok, False, False, 5) + self.button_cancel = Gtk.Button.new_from_stock(Gtk.STOCK_QUIT) + self.button_cancel.connect('clicked', self.on_button_cancel_clicked) + hbox.pack_end(self.button_cancel, False, False, 5) + # + self.duration_in_seconds = 0 + self.fps = 0 + self.filename = None + self.tmp_folder = tempfile.mkdtemp() + if os.path.exists(self.tmp_folder): + shutil.rmtree(self.tmp_folder) + os.makedirs(self.tmp_folder) + # + self.init_menu() + # + self.spinbutton_begin.connect('value-changed', + self.on_spinbutton_begin_value_changed) + self.spinbutton_end.connect('value-changed', + self.on_spinbutton_end_value_changed) + self.show_all() + + def init_menu(self): + menubar = Gtk.MenuBar() + self.vbox.pack_start(menubar, False, False, 0) + accel_group = Gtk.AccelGroup() + self.add_accel_group(accel_group) + self.filemenu = Gtk.Menu.new() + self.filem = Gtk.MenuItem.new_with_label(_('File')) + self.filem.set_submenu(self.filemenu) + # + self.menus = {} + # + self.menus['open-file'] = Gtk.ImageMenuItem.new_with_label(_( + 'Open file')) + self.menus['open-file'].add_accelerator('activate', + accel_group, + ord('O'), + Gdk.ModifierType.CONTROL_MASK, + Gtk.AccelFlags.VISIBLE) + self.filemenu.append(self.menus['open-file']) + # + self.filemenu.append(Gtk.SeparatorMenuItem()) + menubar.append(self.filem) + # + self.filehelp = Gtk.Menu.new() + self.fileh = Gtk.MenuItem.new_with_label(_('Help')) + # + menubar.append(self.fileh) + + def on_button_output_clicked(self, widget): + dialog = Gtk.FileChooserDialog('Select output file', + None, Gtk.FileChooserAction.SAVE, + (Gtk.STOCK_CANCEL, + Gtk.ResponseType.REJECT, + Gtk.STOCK_OK, Gtk.ResponseType.ACCEPT)) + dialog.set_position(Gtk.WindowPosition.CENTER_ALWAYS) + dialog.set_filename(self.button_output.get_label()) + dialog.set_current_name(self.button_output.get_label().split('/')[-1]) + filter = Gtk.FileFilter() + filter.set_name('Gif file') + filter.add_mime_type("image/gif") + dialog.add_filter(filter) + if dialog.run() == Gtk.ResponseType.ACCEPT: + dialog.hide() + filename = dialog.get_filename() + if not filename.endswith('.gif'): + filename += '.gif' + self.button_output.set_label(filename) + dialog.destroy() + + def on_destroy(self, widget): + if os.path.exists(self.tmp_folder): + shutil.rmtree(self.tmp_folder) + + def on_spinbutton_begin_value_changed(self, widget): + value = self.spinbutton_begin.get_value() + temp_begin_image = os.path.join(self.tmp_folder, 'begin.png') + if os.path.exists(temp_begin_image): + os.remove(temp_begin_image) + gtfv = GetThumbnailForVideo(self.filename, temp_begin_image, value) + gtfv.connect('finished', self.on_get_thumbnailforvideo_begin) + gtfv.start() + + def on_get_thumbnailforvideo_begin(self, anobject, pixbuf): + self.begin.set_from_pixbuf(pixbuf) + + def on_spinbutton_end_value_changed(self, widget): + value = self.spinbutton_end.get_value() + temp_end_image = os.path.join(self.tmp_folder, 'end.png') + if os.path.exists(temp_end_image): + os.remove(temp_end_image) + gtfv = GetThumbnailForVideo(self.filename, temp_end_image, value) + gtfv.connect('finished', self.on_get_thumbnailforvideo_end) + gtfv.start() + + def on_get_thumbnailforvideo_end(self, anobject, pixbuf): + self.end.set_from_pixbuf(pixbuf) + + def on_button_ok_clicked(self, widget): + start_position = self.spinbutton_begin.get_value() + end_position = self.spinbutton_end.get_value() + duration = end_position - start_position + output_file = self.button_output.get_label() + if os.path.exists(output_file): + os.remove(output_file) + if os.path.exists(output_file + '.tmp'): + os.remove(output_file + '.tmp') + if self.filename is not None and\ + os.path.exists(self.filename) and duration > 0: + configuration = Configuration() + frames = configuration.get('frames') + scale = configuration.get('scale') + modify_width = configuration.get('modify-width') + width = configuration.get('width') + modify_height = configuration.get('modify-height') + height = configuration.get('height') + if scale is True: + if modify_width is True: + t_width = str(width) + else: + t_width = '-1' + if modify_height is True: + t_height = str(height) + else: + t_height = '-1' + if t_width != '-1' or t_height != '-1': + scale_t = ' -vf scale=%s:%s ' % (t_width, t_height) + else: + scale_t = '' + else: + scale_t = '' + # input_file, output_file, start_position, duration, + # frames, scale_t): + progreso = Progreso(_('Converting video'), self, 100) + convertVideo = ConvertVideo(self.filename, output_file, + start_position, duration, frames, + scale_t) + progreso.connect('i-want-stop', convertVideo.stop_it) + convertVideo.connect('processed', progreso.set_value) + convertVideo.connect('interrupted', progreso.close) + convertVideo.start() + progreso.run() + + def on_button_cancel_clicked(self, widget): + self.on_destroy(None) + exit() + + def drag_begin(self, widget, context): + pass + + def drag_data_get_data(self, treeview, context, selection, target_id, + etime): + pass + + def drag_data_received(self, widget, drag_context, x, y, selection_data, + info, timestamp): + if len(selection_data.get_uris()) > 0: + filename = selection_data.get_uris()[0] + if len(filename) > 8: + filename = urllib.request.url2pathname(filename) + self.process_file(filename[7:]) + return True + + def process_file(self, filename): + self.filename = filename + vss = VideoSnapShooter(filename) + mime = mimetypes.guess_type(self.filename) + if os.path.exists(self.filename): + mime = mimetypes.guess_type(self.filename)[0] + if mime in SUPPORTED_MIMES: + self.duration_in_seconds = int(vss.get_duration()) + self.spinbutton_begin.set_value(0) + self.spinbutton_begin.set_editable(True) + self.spinbutton_begin.set_adjustment( + Gtk.Adjustment(0, 0, self.duration_in_seconds, + 1, 10, 0)) + self.spinbutton_end.set_value(self.duration_in_seconds) + self.spinbutton_end.set_editable(True) + self.spinbutton_end.set_adjustment( + Gtk.Adjustment(self.duration_in_seconds, 0, + self.duration_in_seconds, 1, + 10, 0)) + + +def main(): + GObject.threads_init() + mw = MainWindow(None) + mw.show_all() + Gtk.main() + exit(1) + +if __name__ == '__main__': + main() diff -Nru 2gif-0.1.1/src/preferences_dialog.py 2gif-0.3.0/src/preferences_dialog.py --- 2gif-0.1.1/src/preferences_dialog.py 2016-03-27 08:55:23.000000000 +0000 +++ 2gif-0.3.0/src/preferences_dialog.py 2016-06-13 21:11:38.000000000 +0000 @@ -1,12 +1,10 @@ -#!/usr/bin/env python3 +#! /usr/bin/env python3 # -*- coding: utf-8 -*- # -# preferences_dialog.py -# # This file is part of 2gif # -# Copyright (C) 2016 -# Lorenzo Carbonell Cerezo +# Copyright (C) 2015-2016 Lorenzo Carbonell +# lorenzo.carbonell.cerezo@gmail.com # # 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 @@ -22,32 +20,30 @@ # along with this program. If not, see . import gi +try: + gi.require_version('Gtk', '3.0') +except Exception as e: + print(e) + exit(1) from gi.repository import Gtk import comun import os import shutil from comun import _ from configurator import Configuration -from backlight import BacklightManager -try: - gi.require_version('Gtk', '3.0') -except Exception as e: - print(e) - exit(1) class PreferencesDialog(Gtk.Dialog): - def __init__(self): + def __init__(self, parent): # Gtk.Dialog.__init__(self, - 'Backlight Indicator | '+_('Preferences'), - None, + '2Gif | '+_('Preferences'), + parent, Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT, (Gtk.STOCK_CANCEL, Gtk.ResponseType.REJECT, Gtk.STOCK_OK, Gtk.ResponseType.ACCEPT)) self.set_position(Gtk.WindowPosition.CENTER_ALWAYS) - self.connect('close', self.close_ok) self.set_icon_from_file(comun.ICON) # vbox0 = Gtk.VBox(spacing=5) @@ -62,127 +58,102 @@ notebook.append_page(vbox11, Gtk.Label.new(_('General'))) frame11 = Gtk.Frame() vbox11.pack_start(frame11, False, True, 1) - table11 = Gtk.Table(2, 2, False) + table11 = Gtk.Table(2, 3, False) frame11.add(table11) # *************************************************************** - label11 = Gtk.Label(_('Autostart')+':') + label11 = Gtk.Label(_('Frames per second')+':') label11.set_alignment(0, 0.5) table11.attach(label11, 0, 1, 0, 1, xpadding=5, ypadding=5) - self.switch1 = Gtk.Switch() - table11.attach(self.switch1, 1, 2, 0, 1, + adjustment4 = Gtk.Adjustment(8, 1, 100, 1, 5, 1) + self.sample_time = Gtk.SpinButton() + self.sample_time.set_adjustment(adjustment4) + table11.attach(self.sample_time, 1, 2, 0, 1, xpadding=5, ypadding=5, xoptions=Gtk.AttachOptions.SHRINK) - label12 = Gtk.Label(_('Icon light')+':') + label12 = Gtk.Label(_('Scale')+':') label12.set_alignment(0, 0.5) table11.attach(label12, 0, 1, 1, 2, xpadding=5, ypadding=5) - self.switch2 = Gtk.Switch() - table11.attach(self.switch2, 1, 2, 1, 2, + self.scale = Gtk.Switch() + self.scale.connect('state-set', self.on_scale) + table11.attach(self.scale, 1, 2, 1, 2, + xpadding=5, + ypadding=5, + xoptions=Gtk.AttachOptions.SHRINK) + label14 = Gtk.Label(_('Width')+':') + label14.set_alignment(0, 0.5) + table11.attach(label14, 0, 1, 3, 4, xpadding=5, ypadding=5) + self.modify_width = Gtk.Switch() + self.modify_width.set_sensitive(False) + self.modify_width.connect('state-set', self.on_modify_width) + table11.attach(self.modify_width, 1, 2, 3, 4, + xpadding=5, + ypadding=5, + xoptions=Gtk.AttachOptions.SHRINK) + adjustment_width = Gtk.Adjustment(1, 1, 10025, 1, 5, 1) + self.width = Gtk.SpinButton() + self.width.set_sensitive(False) + self.width.set_adjustment(adjustment_width) + self.width.set_sensitive(False) + table11.attach(self.width, 2, 3, 3, 4, + xpadding=5, + ypadding=5, + xoptions=Gtk.AttachOptions.SHRINK) + label15 = Gtk.Label(_('Height')+':') + label15.set_alignment(0, 0.5) + table11.attach(label15, 0, 1, 4, 5, xpadding=5, ypadding=5) + self.modify_height = Gtk.Switch() + self.modify_height.set_sensitive(False) + self.modify_height.connect('state-set', self.on_modify_height) + table11.attach(self.modify_height, 1, 2, 4, 5, + xpadding=5, + ypadding=5, + xoptions=Gtk.AttachOptions.SHRINK) + adjustment_height = Gtk.Adjustment(1, 1, 10025, 1, 5, 1) + self.height = Gtk.SpinButton() + self.height.set_sensitive(False) + self.height.set_adjustment(adjustment_height) + table11.attach(self.height, 2, 3, 4, 5, xpadding=5, ypadding=5, xoptions=Gtk.AttachOptions.SHRINK) - # *************************************************************** - hbox2 = Gtk.HBox(spacing=5) - hbox2.set_border_width(5) - notebook.append_page(hbox2, Gtk.Label.new(_('Backlight'))) - frame2 = Gtk.Frame() - hbox2.pack_start(frame2, False, True, 1) - table2 = Gtk.Table(4, 2, False) - frame2.add(table2) - # *************************************************************** - label21 = Gtk.Label(_('Minimum backlight')+':') - label21.set_alignment(0, 0.5) - table2.attach(label21, 0, 1, 0, 1, - xpadding=5, ypadding=5) - adjustment1 = Gtk.Adjustment(0, 0, 101, 5, 10, 1) - self.minimum_backlight = Gtk.Scale() - self.minimum_backlight.set_digits(0) - self.minimum_backlight.set_size_request(200, 10) - self.minimum_backlight.set_adjustment(adjustment1) - self.minimum_backlight.connect('value-changed', - self.on_minimum_backlight_changed) - table2.attach(self.minimum_backlight, 1, 2, 0, 1, - xpadding=5, ypadding=5) - label22 = Gtk.Label(_('Maximum backlight')+':') - label22.set_alignment(0, 0.5) - table2.attach(label22, 0, 1, 1, 2, - xpadding=5, ypadding=5) - adjustment2 = Gtk.Adjustment(100, 0, 101, 5, 10, 1) - self.maximum_backlight = Gtk.Scale() - self.maximum_backlight.set_digits(0) - self.maximum_backlight.set_size_request(200, 10) - self.maximum_backlight.set_adjustment(adjustment2) - self.maximum_backlight.connect('value-changed', - self.on_maximum_backlight_changed) - table2.attach(self.maximum_backlight, 1, 2, 1, 2, - xpadding=5, ypadding=5) - label23 = Gtk.Label(_('Backlight')+':') - label23.set_alignment(0, 0.5) - table2.attach(label23, 0, 1, 2, 3, - xpadding=5, ypadding=5) - adjustment3 = Gtk.Adjustment(50, 0, 101, 5, 10, 1) - self.backlight = Gtk.Scale() - self.backlight.set_digits(0) - self.backlight.set_size_request(200, 10) - self.backlight.set_adjustment(adjustment3) - self.backlight.connect('value-changed', self.on_backlight_changed) - table2.attach(self.backlight, 1, 2, 2, 3, - xpadding=5, ypadding=5) - label24 = Gtk.Label(_('Sample time')+' ('+_('min')+'):') - label24.set_alignment(0, 0.5) - table2.attach(label24, 0, 1, 3, 4, - xpadding=5, ypadding=5) - adjustment4 = Gtk.Adjustment(5, 0, 121, 1, 5, 1) - self.sample_time = Gtk.SpinButton() - self.sample_time.set_size_request(200, 10) - self.sample_time.set_adjustment(adjustment4) - table2.attach(self.sample_time, 1, 2, 3, 4, - xpadding=5, ypadding=5, xoptions=Gtk.AttachOptions.FILL) # self.load_preferences() - # self.show_all() - def close_ok(self): - self.save_preferences() + def on_scale(self, widget, data): + self.modify_width.set_sensitive(data) + self.width.set_sensitive(data & self.modify_width.get_active()) + self.modify_height.set_sensitive(data) + self.height.set_sensitive(data & self.modify_height.get_active()) + + def on_modify_width(self, widget, data): + self.width.set_sensitive(data & self.scale.get_active()) + + def on_modify_height(self, widget, data): + self.height.set_sensitive(data & self.scale.get_active()) def load_preferences(self): configuration = Configuration() - first_time = configuration.get('first-time') - version = configuration.get('version') - if first_time or version != comun.VERSION: - configuration.set_defaults() - configuration.read() - self.switch1.set_active(os.path.exists(comun.AUTOSTARTD)) - self.switch2.set_active(configuration.get('theme') == 'light') - self.minimum_backlight.set_value( - configuration.get('minimum-backlight')) - self.maximum_backlight.set_value( - configuration.get('maximum-backlight')) - self.backlight.set_value(configuration.get('backlight')) - self.sample_time.set_value(configuration.get('sample-time')) + self.sample_time.set_value(configuration.get('frames')) + self.scale.set_active(configuration.get('scale')) + self.modify_width.set_active(configuration.get('modify-width')) + self.width.set_value(configuration.get('width')) + self.modify_height.set_active(configuration.get('modify-height')) + self.height.set_value(configuration.get('height')) def save_preferences(self): configuration = Configuration() - configuration.set('first-time', False) - configuration.set('version', comun.VERSION) - create_or_remove_autostart(self.switch1.get_active()) - if self.switch2.get_active() is True: - configuration.set('theme', 'light') - else: - configuration.set('theme', 'dark') - configuration.set('minimum-backlight', - self.minimum_backlight.get_value()) - configuration.set('maximum-backlight', - self.maximum_backlight.get_value()) - configuration.set('backlight', - self.backlight.get_value()) - configuration.set('sample-time', - self.sample_time.get_value()) + configuration.set('frame', self.sample_time.get_value()) + configuration.set('scale', self.scale.get_active()) + configuration.set('modify-width', self.modify_width.get_active()) + configuration.set('width', self.width.get_value()) + configuration.set('modify-height', self.modify_height.get_active()) + configuration.set('height', self.height.get_value()) configuration.save() if __name__ == "__main__": - cm = PreferencesDialog() + cm = PreferencesDialog(None) if cm.run() == Gtk.ResponseType.ACCEPT: print(1) cm.close_ok() diff -Nru 2gif-0.1.1/src/progreso.py 2gif-0.3.0/src/progreso.py --- 2gif-0.1.1/src/progreso.py 1970-01-01 00:00:00.000000000 +0000 +++ 2gif-0.3.0/src/progreso.py 2016-06-12 07:25:26.000000000 +0000 @@ -0,0 +1,121 @@ +#! /usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# This file is part of 2gif +# +# Copyright (C) 2015-2016 Lorenzo Carbonell +# lorenzo.carbonell.cerezo@gmail.com +# +# 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 . + +import gi +try: + gi.require_version('Gtk', '3.0') +except Exception as e: + print(e) + exit(1) +from gi.repository import Gtk +from gi.repository import GObject +import threading + + +class Progreso(Gtk.Dialog, threading.Thread): + __gsignals__ = { + 'i-want-stop': (GObject.SIGNAL_RUN_FIRST, GObject.TYPE_NONE, ()), + } + + def __init__(self, title, parent, max_value): + Gtk.Dialog.__init__(self, title, parent) + self.set_position(Gtk.WindowPosition.CENTER_ALWAYS) + self.set_size_request(330, 30) + self.set_resizable(False) + self.connect('destroy', self.close) + # self.set_modal(True) + vbox = Gtk.VBox(spacing=5) + vbox.set_border_width(5) + self.get_content_area().add(vbox) + # + frame1 = Gtk.Frame() + vbox.pack_start(frame1, True, True, 0) + table = Gtk.Table(2, 2, False) + frame1.add(table) + # + self.label = Gtk.Label() + table.attach(self.label, 0, 2, 0, 1, + xpadding=5, + ypadding=5, + xoptions=Gtk.AttachOptions.SHRINK, + yoptions=Gtk.AttachOptions.EXPAND) + # + self.progressbar = Gtk.ProgressBar() + self.progressbar.set_size_request(300, 0) + table.attach(self.progressbar, 0, 1, 1, 2, + xpadding=5, + ypadding=5, + xoptions=Gtk.AttachOptions.SHRINK, + yoptions=Gtk.AttachOptions.EXPAND) + button_stop = Gtk.Button() + button_stop.set_size_request(40, 40) + button_stop.set_image( + Gtk.Image.new_from_stock(Gtk.STOCK_STOP, Gtk.IconSize.BUTTON)) + button_stop.connect('clicked', self.on_button_stop_clicked) + table.attach(button_stop, 1, 2, 1, 2, + xpadding=5, + ypadding=5, + xoptions=Gtk.AttachOptions.SHRINK) + self.stop = False + self.show_all() + self.max_value = max_value + self.value = 0.0 + + def get_stop(self): + return self.stop + + def on_button_stop_clicked(self, widget): + self.stop = True + self.emit('i-want-stop') + + def set_scanning_file(self, afile): + if len(afile) > 35: + text = '...'+afile[-32:] + else: + text = afile + self.label.set_label(text) + + def set_value(self, anobject=None, value=1): + if value >= 0 and value <= self.max_value: + self.value = value + fraction = self.value/self.max_value + self.progressbar.set_fraction(fraction) + if self.value == self.max_value: + self.hide() + + def close(self, widget=None): + self.destroy() + + def increase(self): + self.value += 1.0 + fraction = self.value/self.max_value + self.progressbar.set_fraction(fraction) + if self.value == self.max_value: + self.hide() + + def decrease(self): + self.value -= 1.0 + fraction = self.value/self.max_value + self.progressbar.set_fraction(fraction) + +if __name__ == '__main__': + p = Progreso('Prueba', None, 100) + p.run() diff -Nru 2gif-0.1.1/src/supportdialog.py 2gif-0.3.0/src/supportdialog.py --- 2gif-0.1.1/src/supportdialog.py 1970-01-01 00:00:00.000000000 +0000 +++ 2gif-0.3.0/src/supportdialog.py 2016-06-12 07:25:26.000000000 +0000 @@ -0,0 +1,103 @@ +#! /usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# This file is part of 2gif +# +# Copyright (C) 2015-2016 Lorenzo Carbonell +# lorenzo.carbonell.cerezo@gmail.com +# +# 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 . + +import gi +try: + gi.require_version('Gtk', '3.0') +except Exception as e: + print(e) + exit(1) +from gi.repository import Gtk +from gi.repository import GdkPixbuf +import webbrowser +import comun +from comun import _ + + +class SupportDialog(Gtk.Dialog): + def __init__(self, parent): + Gtk.Dialog.__init__(self, + '2Gif | '+_('Donate'), + parent, + Gtk.DialogFlags.MODAL | + Gtk.DialogFlags.DESTROY_WITH_PARENT, + (Gtk.STOCK_OK, Gtk.ResponseType.ACCEPT)) + self.set_position(Gtk.WindowPosition.CENTER_ALWAYS) + self.set_size_request(300, 300) + self.connect('close', self.close_ok) + self.set_icon_from_file(comun.ICON) + # + hbox = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, 5) + hbox.set_border_width(5) + self.get_content_area().add(hbox) + vbox = Gtk.Box.new(Gtk.Orientation.VERTICAL, 5) + vbox.set_border_width(5) + hbox.pack_start(vbox, True, True, 5) + frame = Gtk.Frame() + vbox.pack_start(frame, True, True, 5) + internal_box = Gtk.Box.new(Gtk.Orientation.VERTICAL, 5) + frame.add(internal_box) + label = Gtk.Label() + label.set_markup('%s' % ('Help to support 2Gif')) + internal_box.pack_start(label, False, False, 5) + internal_box.pack_start( + Gtk.Image.new_from_pixbuf( + GdkPixbuf.Pixbuf.new_from_file_at_size(comun.ICON, 150, -1)), + False, False, 5) + internal_box.pack_start(Gtk.Label(_('Via')+':'), False, False, 5) + logo1 = Gtk.EventBox() + logo1.add(Gtk.Image.new_from_pixbuf( + GdkPixbuf.Pixbuf.new_from_file_at_size( + comun.PAYPAL_LOGO, 150, -1,))) + logo1.connect('button_press_event', + self.on_support_clicked) + internal_box.pack_start(logo1, False, False, 5) + logo2 = Gtk.EventBox() + logo2.add(Gtk.Image.new_from_pixbuf( + GdkPixbuf.Pixbuf.new_from_file_at_size( + comun.BITCOIN_LOGO, 150, -1,))) + logo2.connect('button_press_event', + self.on_support_clicked) + internal_box.pack_start(logo2, False, False, 5) + logo3 = Gtk.EventBox() + logo3.add(Gtk.Image.new_from_pixbuf( + GdkPixbuf.Pixbuf.new_from_file_at_size( + comun.FLATTR_LOGO, 150, -1,))) + logo3.connect('button_press_event', + self.on_support_clicked) + internal_box.pack_start(logo3, False, False, 5) + self.show_all() + + def close_ok(self, widget): + pass + + def on_support_clicked(self, widget, optional): + webbrowser.open('http://www.atareao.es/donar/') + self.destroy() + +if __name__ == "__main__": + dialog = SupportDialog(None) + if dialog.run() == Gtk.ResponseType.ACCEPT: + print(1) + dialog.close_ok() + dialog.hide() + dialog.destroy() + exit(0) diff -Nru 2gif-0.1.1/src/tools.py 2gif-0.3.0/src/tools.py --- 2gif-0.1.1/src/tools.py 1970-01-01 00:00:00.000000000 +0000 +++ 2gif-0.3.0/src/tools.py 2016-06-12 07:25:26.000000000 +0000 @@ -0,0 +1,95 @@ +#! /usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# This file is part of 2gif +# +# Copyright (C) 2015-2016 Lorenzo Carbonell +# lorenzo.carbonell.cerezo@gmail.com +# +# 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 . + +import tempfile +import os +import re + + +def get_seconds(value): + values = value.split(':') + return float(values[0]) * 3600.0 + float(values[1]) * 60.0 +\ + float(values[2]) + + +def getVideoDetails(filepath): + tmpf = tempfile.NamedTemporaryFile() + os.system("avconv -i \"%s\" 2> %s" % (filepath, tmpf.name)) + lines = tmpf.readlines() + tmpf.close() + metadata = {} + metadata['video'] = {} + metadata['audio'] = {} + print(lines) + metadata['duration'] = 0 + metadata['duration_in_seconds'] = 0 + metadata['bitrate'] = 0 + metadata['video']['resolution'] = 0 + metadata['video']['width'], metadata['video']['height'] = 0, 0 + metadata['video']['resolution'] = 0 + metadata['video']['bitrate'] = 0 + metadata['video']['fps'] = 0 + metadata['fps'] = 0 + metadata['audio']['codec'] = 0 + metadata['audio']['frequency'] = 0 + metadata['audio']['bitrate'] = 0 + for l in lines: + l = l.decode() + print(l) + l = l.strip() + if l.startswith('Duration'): + metadata['duration'] = re.search('Duration: (.*?),', l).group(0).\ + split(':', 1)[1].strip(' ,') + metadata['duration_in_seconds'] = get_seconds(metadata['duration']) + metadata['bitrate'] = re.search("bitrate: (\d+ kb/s)", l).\ + group(0).split(':')[1].strip() + if l.startswith('Stream #') and l.split(': ') == 'Video': + metadata['video'] = {} + try: + metadata['video']['codec'], metadata['video']['profile'] = \ + [e.strip(' , ()') for e in re.search( + 'Video: (.*? \(.*?\)),? ', l).group(0).split(':')[1]. + split('(')] + metadata['video']['resolution'] = re.search('([1-9]\d+x\d+)', + l).group(1) + metadata['video']['width'], metadata['video']['height'] =\ + metadata['video']['resolution'].split('x') + metadata['video']['width'] = float(metadata['video']['width']) + metadata['video']['height'] = \ + float(metadata['video']['height']) + metadata['video']['bitrate'] = re.search('(\d+ kb/s)', + l).group(1) + metadata['video']['fps'] = re.search('(\d+ fps)', l).group(1) + metadata['fps'] = int(metadata['video']['fps'].split(' ')[0]) + except Exception: + pass + if l.startswith('Stream #') and l.split(': ') == 'Audio': + try: + metadata['audio'] = {} + metadata['audio']['codec'] = re.search('Audio: (.*?) ', + l).group(1) + metadata['audio']['frequency'] = re.search(', (.*? Hz),', + l).group(1) + metadata['audio']['bitrate'] = re.search(', (\d+ kb/s)', + l).group(1) + except Exception: + pass + return metadata diff -Nru 2gif-0.1.1/src/videosnapshooter.py 2gif-0.3.0/src/videosnapshooter.py --- 2gif-0.1.1/src/videosnapshooter.py 2016-03-28 07:06:41.000000000 +0000 +++ 2gif-0.3.0/src/videosnapshooter.py 2016-06-12 07:25:21.000000000 +0000 @@ -1,5 +1,31 @@ -#!/usr/bin/env python3 - +#! /usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# This file is part of 2gif +# +# Copyright (C) 2015-2016 Lorenzo Carbonell +# lorenzo.carbonell.cerezo@gmail.com +# +# 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 . + +import gi +try: + gi.require_version('Gtk', '3.0') + gi.require_version('Gst', '1.0') +except Exception as e: + print(e) + exit(1) from gi.repository import Gtk from gi.repository import Gst import sys @@ -51,7 +77,8 @@ elif ret == Gst.StateChangeReturn.NO_PREROLL: print('live sources not supported yet\n') pixbuf = self.sink.props.last_pixbuf - pixbuf.savev(screenshotfilename, "png", [], []) + if pixbuf is not None: + pixbuf.savev(screenshotfilename, "png", [], []) def ft(self, value, maxvalue): return (len(str(maxvalue))-len(str(value)))*'0'+str(value)