"Fossies" - the Fresh Open Source Software Archive

Member "ffmulticonverter-1.8.0/ffmulticonverter/ffmulticonverter.py" (25 Jun 2016, 18480 Bytes) of package /linux/privat/ffmulticonverter-1.8.0.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. For more information about "ffmulticonverter.py" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 1.7.2_vs_1.8.0.

    1 # Copyright (C) 2011-2016 Ilias Stamatis <stamatis.iliass@gmail.com>
    2 #
    3 # This program is free software: you can redistribute it and/or modify
    4 # it under the terms of the GNU General Public License as published by
    5 # the Free Software Foundation, either version 3 of the License, or
    6 # (at your option) any later version.
    7 #
    8 # This program is distributed in the hope that it will be useful,
    9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
   10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   11 # GNU General Public License for more details.
   12 #
   13 # You should have received a copy of the GNU General Public License
   14 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
   15 
   16 import os
   17 import sys
   18 import platform
   19 import textwrap
   20 import logging
   21 import webbrowser
   22 
   23 from PyQt5.QtGui import QIcon, QKeySequence
   24 from PyQt5.QtCore import (
   25         PYQT_VERSION_STR, QCoreApplication, QLocale, QSettings, Qt,
   26         QTranslator, QT_VERSION_STR
   27         )
   28 from PyQt5.QtWidgets import (
   29         QAbstractItemView, QApplication, QCheckBox, QFileDialog, QLabel,
   30         QLineEdit, QMainWindow, QMessageBox, QPushButton, QShortcut, QTabWidget,
   31         QToolButton, QWidget
   32         )
   33 
   34 import ffmulticonverter as ffmc
   35 from ffmulticonverter import utils
   36 from ffmulticonverter import config
   37 from ffmulticonverter import about_dlg
   38 from ffmulticonverter import preferences_dlg
   39 from ffmulticonverter import presets_dlgs
   40 from ffmulticonverter import progress
   41 from ffmulticonverter import qrc_resources
   42 from ffmulticonverter.audiovideotab import AudioVideoTab
   43 from ffmulticonverter.imagetab import ImageTab
   44 from ffmulticonverter.documenttab import DocumentTab
   45 
   46 
   47 class ValidationError(Exception):
   48     pass
   49 
   50 
   51 class MainWindow(QMainWindow):
   52     def __init__(self, parent=None):
   53         super(MainWindow, self).__init__(parent)
   54 
   55         self.fnames = []  # list of file names to be converted
   56         self.office_listener_started = False
   57 
   58         self.parse_cla()
   59 
   60         addQPB = QPushButton(self.tr('Add'))
   61         delQPB = QPushButton(self.tr('Delete'))
   62         clearQPB = QPushButton(self.tr('Clear'))
   63         vlayout1 = utils.add_to_layout('v', addQPB, delQPB, clearQPB, None)
   64 
   65         self.filesList = utils.FilesList()
   66         self.filesList.setSelectionMode(QAbstractItemView.ExtendedSelection)
   67         hlayout1 = utils.add_to_layout('h', self.filesList, vlayout1)
   68 
   69         outputQL = QLabel(self.tr('Output folder:'))
   70         self.toQLE = QLineEdit()
   71         self.toQLE.setReadOnly(True)
   72         self.toQTB = QToolButton()
   73         self.toQTB.setText('...')
   74         hlayout2 = utils.add_to_layout('h', outputQL, self.toQLE, self.toQTB)
   75 
   76         self.audiovideo_tab = AudioVideoTab(self)
   77         self.image_tab = ImageTab(self)
   78         self.document_tab = DocumentTab(self)
   79 
   80         self.tabs = [self.audiovideo_tab, self.image_tab, self.document_tab]
   81         tab_names = [self.tr('Audio/Video'), self.tr('Images'),
   82                      self.tr('Documents')]
   83 
   84         self.tabWidget = QTabWidget()
   85         for num, tab in enumerate(tab_names):
   86             self.tabWidget.addTab(self.tabs[num], tab)
   87         self.tabWidget.setCurrentIndex(0)
   88 
   89         self.origQCB = QCheckBox(
   90                 self.tr('Save each file in the same\nfolder as input file'))
   91         self.deleteQCB = QCheckBox(self.tr('Delete original'))
   92         convertQPB = QPushButton(self.tr('&Convert'))
   93 
   94         hlayout3 = utils.add_to_layout('h', self.origQCB, self.deleteQCB, None)
   95         hlayout4 = utils.add_to_layout('h', None, convertQPB)
   96         final_layout = utils.add_to_layout(
   97                 'v', hlayout1, self.tabWidget, hlayout2, hlayout3, hlayout4)
   98 
   99         self.dependenciesQL = QLabel()
  100         self.statusBar().addPermanentWidget(self.dependenciesQL, stretch=1)
  101 
  102         widget = QWidget()
  103         widget.setLayout(final_layout)
  104         self.setCentralWidget(widget)
  105 
  106         openAction = utils.create_action(
  107                 self, self.tr('Open'), QKeySequence.Open, None,
  108                 self.tr('Open a file'), self.filesList_add
  109                 )
  110         convertAction = utils.create_action(
  111                 self, self.tr('Convert'), 'Ctrl+C', None,
  112                 self.tr('Convert files'), self.start_conversion
  113                 )
  114         quitAction = utils.create_action(
  115                 self, self.tr('Quit'), 'Ctrl+Q', None,
  116                 self.tr('Quit'), self.close
  117                 )
  118         edit_presetsAction = utils.create_action(
  119                 self, self.tr('Edit Presets'), 'Ctrl+P', None,
  120                 self.tr('Edit Presets'), self.open_dialog_presets
  121                 )
  122         importAction = utils.create_action(
  123                 self, self.tr('Import'), None, None,
  124                 self.tr('Import presets'), self.import_presets
  125                 )
  126         exportAction = utils.create_action(
  127                 self, self.tr('Export'), None, None,
  128                 self.tr('Export presets'), self.export_presets
  129                 )
  130         resetAction = utils.create_action(
  131                 self, self.tr('Reset'), None, None,
  132                 self.tr('Reset presets'), self.reset_presets
  133                 )
  134         syncAction = utils.create_action(
  135                 self, self.tr('Synchronize'), None, None,
  136                 self.tr('Synchronize presets'), self.sync_presets
  137                 )
  138         removeoldAction = utils.create_action(
  139                 self, self.tr('Remove old'), None, None,
  140                 self.tr('Remove old presets'), self.removeold_presets
  141                 )
  142         clearallAction = utils.create_action(
  143                 self, self.tr('Clear All'), None, None,
  144                 self.tr('Clear form'), self.clear_all
  145                 )
  146         preferencesAction = utils.create_action(
  147                 self, self.tr('Preferences'), 'Alt+Ctrl+P',
  148                 None, self.tr('Preferences'), self.open_dialog_preferences
  149                 )
  150         trackerAction = utils.create_action(
  151                 self, 'Issue tracker', None, None, None,
  152                 lambda: webbrowser.open(
  153                     "https://github.com/Ilias95/FF-Multi-Converter/issues")
  154                 )
  155         wikiAction = utils.create_action(
  156                 self, 'Wiki', None, None, None,
  157                 lambda: webbrowser.open(
  158                     "https://github.com/Ilias95/FF-Multi-Converter/wiki")
  159                 )
  160         ffmpegdocAction = utils.create_action(
  161                 self, 'FFmpeg ' + self.tr('documentation'), None, None, None,
  162                 lambda: webbrowser.open(
  163                     "https://www.ffmpeg.org/documentation.html")
  164                 )
  165         imagemagickdocAction = utils.create_action(
  166                 self, 'ImageMagick ' + self.tr('documentation'), None, None,
  167                 None, lambda: webbrowser.open(
  168                     "http://www.imagemagick.org/script/convert.php")
  169                 )
  170         aboutAction = utils.create_action(
  171                 self, self.tr('About'), 'Ctrl+?', None,
  172                 self.tr('About'), self.open_dialog_about
  173                 )
  174 
  175         fileMenu = self.menuBar().addMenu(self.tr('File'))
  176         editMenu = self.menuBar().addMenu(self.tr('Edit'))
  177         presetsMenu = self.menuBar().addMenu(self.tr('Presets'))
  178         helpMenu = self.menuBar().addMenu(self.tr('Help'))
  179 
  180         utils.add_actions(
  181                 fileMenu, [openAction, convertAction, None, quitAction])
  182         utils.add_actions(
  183                 presetsMenu,
  184                 [edit_presetsAction, importAction, exportAction, resetAction,
  185                  None, syncAction, removeoldAction]
  186                 )
  187         utils.add_actions(editMenu, [clearallAction, None, preferencesAction])
  188         utils.add_actions(
  189                 helpMenu,
  190                 [trackerAction, wikiAction, None, ffmpegdocAction,
  191                 imagemagickdocAction, None, aboutAction]
  192                 )
  193 
  194         self.filesList.dropped.connect(self.filesList_add_dragged)
  195         addQPB.clicked.connect(self.filesList_add)
  196         delQPB.clicked.connect(self.filesList_delete)
  197         clearQPB.clicked.connect(self.filesList_clear)
  198         self.tabWidget.currentChanged.connect(
  199                 lambda: self.tabs[0].moreQPB.setChecked(False))
  200         self.origQCB.toggled.connect(
  201                 lambda: self.toQLE.setEnabled(not self.origQCB.isChecked()))
  202         self.toQTB.clicked.connect(self.get_output_folder)
  203         convertQPB.clicked.connect(convertAction.triggered)
  204 
  205         del_shortcut = QShortcut(self)
  206         del_shortcut.setKey(Qt.Key_Delete)
  207         del_shortcut.activated.connect(self.filesList_delete)
  208 
  209         self.setWindowTitle('FF Multi Converter')
  210 
  211         self.load_settings()
  212         self.check_for_dependencies()
  213 
  214         self.audiovideo_tab.set_default_command()
  215         self.image_tab.set_default_command()
  216         self.toQLE.setText(self.default_output)
  217 
  218         self.filesList_update()
  219 
  220 
  221     def parse_cla(self):
  222         """Parse command line arguments."""
  223         for i in QCoreApplication.arguments()[1:]:
  224             i = os.path.abspath(i)
  225             if os.path.isfile(i):
  226                 self.fnames.append(i)
  227             else:
  228                 print("ffmulticonverter: {0}: Not a file".format(i))
  229 
  230     def check_for_dependencies(self):
  231         """
  232         Check if each one of the program dependencies are installed and
  233         update self.dependenciesQL with the appropriate message.
  234         """
  235         if not utils.is_installed(self.ffmpeg_path):
  236             self.ffmpeg_path = utils.is_installed('ffmpeg')
  237             QSettings().setValue('ffmpeg_path', self.ffmpeg_path)
  238         self.unoconv = utils.is_installed('unoconv')
  239         self.imagemagick = utils.is_installed('convert')
  240 
  241         missing = []
  242         if not self.ffmpeg_path:
  243             missing.append('ffmpeg')
  244         if not self.unoconv:
  245             missing.append('unoconv')
  246         if not self.imagemagick:
  247             missing.append('imagemagick')
  248 
  249         if missing:
  250             missing = ', '.join(missing)
  251             status = self.tr('Missing dependencies:') + ' ' + missing
  252             self.dependenciesQL.setText(status)
  253 
  254     def load_settings(self):
  255         settings = QSettings()
  256 
  257         self.overwrite_existing = settings.value('overwrite_existing', type=bool)
  258         self.default_output = settings.value('default_output', type=str)
  259         self.prefix = settings.value('prefix', type=str)
  260         self.suffix = settings.value('suffix', type=str)
  261         self.ffmpeg_path = settings.value('ffmpeg_path', type=str)
  262         self.default_command = (settings.value('default_command', type=str) or
  263                 config.default_ffmpeg_cmd)
  264         # type=list won't work for some reason
  265         extraformats_video = (settings.value('extraformats_video') or [])
  266         videocodecs = (settings.value('videocodecs') or config.video_codecs)
  267         audiocodecs = (settings.value('audiocodecs') or config.audio_codecs)
  268         self.default_command_image = (settings.value('default_command_image',
  269                 type=str) or
  270                 config.default_imagemagick_cmd)
  271         extraformats_image = (settings.value('extraformats_image') or [])
  272         extraformats_document = (settings.value('extraformats_document') or [])
  273 
  274         self.audiovideo_tab.fill_video_comboboxes(videocodecs,
  275                 audiocodecs, extraformats_video)
  276         self.image_tab.fill_extension_combobox(extraformats_image)
  277         self.document_tab.fill_extension_combobox(extraformats_document)
  278 
  279     def get_current_tab(self):
  280         for i in self.tabs:
  281             if self.tabs.index(i) == self.tabWidget.currentIndex():
  282                 return i
  283 
  284     def filesList_update(self):
  285         self.filesList.clear()
  286         for i in self.fnames:
  287             self.filesList.addItem(i)
  288 
  289     def filesList_add(self):
  290         filters  = 'All Files (*);;'
  291         filters += 'Audio/Video Files (*.{});;'.format(
  292                 ' *.'.join(self.audiovideo_tab.formats))
  293         filters += 'Image Files (*.{});;'.format(
  294                 ' *.'.join(self.image_tab.formats + self.image_tab.extra_img))
  295         filters += 'Document Files (*.{})'.format(
  296                 ' *.'.join(self.document_tab.formats))
  297 
  298         fnames = QFileDialog.getOpenFileNames(self, 'FF Multi Converter - ' +
  299                 self.tr('Choose File'), config.home, filters,
  300                 options=QFileDialog.HideNameFilterDetails)[0]
  301 
  302         if fnames:
  303             for i in fnames:
  304                 if not i in self.fnames:
  305                     self.fnames.append(i)
  306             self.filesList_update()
  307 
  308     def filesList_add_dragged(self, links):
  309         for path in links:
  310             if os.path.isfile(path) and not path in self.fnames:
  311                 self.fnames.append(path)
  312         self.filesList_update()
  313 
  314     def filesList_delete(self):
  315         items = self.filesList.selectedItems()
  316         if items:
  317             for i in items:
  318                 self.fnames.remove(i.text())
  319             self.filesList_update()
  320 
  321     def filesList_clear(self):
  322         self.fnames = []
  323         self.filesList_update()
  324 
  325     def clear_all(self):
  326         """Clears or sets to default the values of all graphical widgets."""
  327         self.toQLE.clear()
  328         self.origQCB.setChecked(False)
  329         self.deleteQCB.setChecked(False)
  330         self.filesList_clear()
  331 
  332         self.audiovideo_tab.clear()
  333         self.image_tab.clear()
  334 
  335     def get_output_folder(self):
  336         if self.toQLE.isEnabled():
  337             output = QFileDialog.getExistingDirectory(
  338                     self, 'FF Multi Converter - ' +
  339                     self.tr('Choose output destination'),
  340                     config.home)
  341             if output:
  342                 self.toQLE.setText(output)
  343 
  344     def import_presets(self):
  345         presets_dlgs.ShowPresets().import_presets()
  346 
  347     def export_presets(self):
  348         presets_dlgs.ShowPresets().export_presets()
  349 
  350     def reset_presets(self):
  351         presets_dlgs.ShowPresets().reset()
  352 
  353     def sync_presets(self):
  354         presets_dlgs.ShowPresets().synchronize()
  355 
  356     def removeold_presets(self):
  357         presets_dlgs.ShowPresets().remove_old()
  358 
  359     def ok_to_continue(self):
  360         """
  361         Check if everything is ok to continue with conversion.
  362 
  363         Check if:
  364         - At least one file has given for conversion.
  365         - An output folder has given.
  366         - Output folder exists.
  367 
  368         Return False if an error arises, else True.
  369         """
  370         try:
  371             if not self.fnames:
  372                 raise ValidationError(
  373                         self.tr('You must add at least one file to convert!'))
  374             elif not self.origQCB.isChecked() and not self.toQLE.text():
  375                 raise ValidationError(
  376                         self.tr('You must choose an output folder!'))
  377             elif (not self.origQCB.isChecked() and
  378                   not os.path.exists(self.toQLE.text())):
  379                 raise ValidationError(self.tr('Output folder does not exists!'))
  380             if not self.get_current_tab().ok_to_continue():
  381                 return False
  382             return True
  383 
  384         except ValidationError as e:
  385             QMessageBox.warning(
  386                     self, 'FF Multi Converter - ' + self.tr('Error!'), str(e))
  387             return False
  388 
  389     def start_conversion(self):
  390         """
  391         Extract the appropriate information from GUI and call the
  392         Progress dialog with the suitable argumens.
  393         """
  394         if not self.ok_to_continue():
  395             return
  396 
  397         tab = self.get_current_tab()
  398         ext_to = '.' + tab.extQCB.currentText()
  399 
  400         if tab.name == 'Documents' and not self.office_listener_started:
  401             utils.start_office_listener()
  402             self.office_listener_started = True
  403 
  404         _list = utils.create_paths_list(
  405                 self.fnames, ext_to, self.prefix, self.suffix,
  406                 self.toQLE.text(), self.origQCB.isChecked(),
  407                 self.overwrite_existing
  408                 )
  409 
  410         dialog = progress.Progress(
  411                 _list, tab, self.deleteQCB.isChecked(), self)
  412         dialog.show()
  413 
  414     def open_dialog_preferences(self):
  415         """Open the preferences dialog."""
  416         dialog = preferences_dlg.Preferences(self)
  417         if dialog.exec_():
  418             self.load_settings()
  419 
  420     def open_dialog_presets(self):
  421         """Open the presets dialog."""
  422         dialog = presets_dlgs.ShowPresets(self)
  423         dialog.exec_()
  424 
  425     def open_dialog_about(self):
  426         """Call the about dialog with the appropriate values."""
  427         msg = self.tr('Convert among several file types to other formats')
  428         msg = textwrap.fill(msg, 54).replace('\n', '<br>')
  429         text = '''<b> FF Multi Converter {0} </b>
  430                  <p>{1}
  431                  <p><a href="{2}">FF Multi Converter - Home Page</a>
  432                  <p>Copyright &copy; 2011-2016 {3}
  433                  <br>License: {4}
  434                  <p>Python {5} - Qt {6} - PyQt {7} on {8}'''\
  435                  .format(ffmc.__version__, msg, ffmc.__url__, ffmc.__author__,
  436                          ffmc.__license__, platform.python_version()[:5],
  437                          QT_VERSION_STR, PYQT_VERSION_STR, platform.system())
  438         image = ':/ffmulticonverter.png'
  439         authors = '{0} <{1}>\n\n'.format(ffmc.__author__, ffmc.__author_email__)
  440         authors += 'Contributors:\nPanagiotis Mavrogiorgos'
  441         translators = []
  442         for i in config.translators:
  443             translators.append('{0}\n     {1}'.format(i[0], i[1]))
  444         translators = '\n\n'.join(translators)
  445 
  446         dialog = about_dlg.AboutDialog(text, image, authors, translators, self)
  447         dialog.exec_()
  448 
  449 
  450 def main():
  451     app = QApplication([i.encode('utf-8') for i in sys.argv])
  452     app.setOrganizationName(ffmc.__name__)
  453     app.setOrganizationDomain(ffmc.__url__)
  454     app.setApplicationName('FF Multi Converter')
  455     app.setWindowIcon(QIcon(':/ffmulticonverter.png'))
  456 
  457     locale = QLocale.system().name()
  458     qtTranslator = QTranslator()
  459     if qtTranslator.load('qt_' + locale, ':/'):
  460         app.installTranslator(qtTranslator)
  461     appTranslator = QTranslator()
  462     if appTranslator.load('ffmulticonverter_' + locale, ':/'):
  463         app.installTranslator(appTranslator)
  464 
  465     if not os.path.exists(config.log_dir):
  466         os.makedirs(config.log_dir)
  467 
  468     logging.basicConfig(
  469             filename=config.log_file,
  470             level=logging.DEBUG,
  471             format=config.log_format,
  472             datefmt=config.log_dateformat
  473             )
  474 
  475     converter = MainWindow()
  476     converter.show()
  477     app.exec_()