ffconverter 2.2.2__tar.gz → 2.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. {ffconverter-2.2.2 → ffconverter-2.3.0}/ChangeLog +8 -0
  2. {ffconverter-2.2.2 → ffconverter-2.3.0}/PKG-INFO +1 -1
  3. {ffconverter-2.2.2 → ffconverter-2.3.0}/README.md +1 -1
  4. {ffconverter-2.2.2 → ffconverter-2.3.0}/ffconverter/__init__.py +2 -2
  5. {ffconverter-2.2.2 → ffconverter-2.3.0}/ffconverter/dynamictab.py +6 -31
  6. {ffconverter-2.2.2 → ffconverter-2.3.0}/ffconverter/ffconverter.py +163 -74
  7. {ffconverter-2.2.2 → ffconverter-2.3.0}/ffconverter/preferences_dlg.py +31 -5
  8. {ffconverter-2.2.2 → ffconverter-2.3.0}/ffconverter/progress.py +1 -1
  9. {ffconverter-2.2.2 → ffconverter-2.3.0}/ffconverter/utils.py +44 -4
  10. {ffconverter-2.2.2 → ffconverter-2.3.0}/ffconverter.egg-info/PKG-INFO +1 -1
  11. {ffconverter-2.2.2 → ffconverter-2.3.0}/share/ffconverter.desktop +1 -1
  12. {ffconverter-2.2.2 → ffconverter-2.3.0}/.gitignore +0 -0
  13. {ffconverter-2.2.2 → ffconverter-2.3.0}/AUTHORS +0 -0
  14. {ffconverter-2.2.2 → ffconverter-2.3.0}/COPYING +0 -0
  15. {ffconverter-2.2.2 → ffconverter-2.3.0}/MANIFEST.in +0 -0
  16. {ffconverter-2.2.2 → ffconverter-2.3.0}/TRANSLATORS +0 -0
  17. {ffconverter-2.2.2 → ffconverter-2.3.0}/bin/ffconverter +0 -0
  18. {ffconverter-2.2.2 → ffconverter-2.3.0}/ffconverter/about_dlg.py +0 -0
  19. {ffconverter-2.2.2 → ffconverter-2.3.0}/ffconverter/audiovideotab.py +0 -0
  20. {ffconverter-2.2.2 → ffconverter-2.3.0}/ffconverter/config.py +0 -0
  21. {ffconverter-2.2.2 → ffconverter-2.3.0}/ffconverter/imagetab.py +0 -0
  22. {ffconverter-2.2.2 → ffconverter-2.3.0}/ffconverter/presets_dlgs.py +0 -0
  23. {ffconverter-2.2.2 → ffconverter-2.3.0}/ffconverter/qrc_resources.py +0 -0
  24. {ffconverter-2.2.2 → ffconverter-2.3.0}/ffconverter.egg-info/SOURCES.txt +0 -0
  25. {ffconverter-2.2.2 → ffconverter-2.3.0}/ffconverter.egg-info/dependency_links.txt +0 -0
  26. {ffconverter-2.2.2 → ffconverter-2.3.0}/ffconverter.egg-info/top_level.txt +0 -0
  27. {ffconverter-2.2.2 → ffconverter-2.3.0}/launcher +0 -0
  28. {ffconverter-2.2.2 → ffconverter-2.3.0}/locale/ffconverter.pro +0 -0
  29. {ffconverter-2.2.2 → ffconverter-2.3.0}/locale/ffconverter_bg.ts +0 -0
  30. {ffconverter-2.2.2 → ffconverter-2.3.0}/locale/ffconverter_ca.ts +0 -0
  31. {ffconverter-2.2.2 → ffconverter-2.3.0}/locale/ffconverter_cs.ts +0 -0
  32. {ffconverter-2.2.2 → ffconverter-2.3.0}/locale/ffconverter_de_DE.ts +0 -0
  33. {ffconverter-2.2.2 → ffconverter-2.3.0}/locale/ffconverter_el.ts +0 -0
  34. {ffconverter-2.2.2 → ffconverter-2.3.0}/locale/ffconverter_en.ts +0 -0
  35. {ffconverter-2.2.2 → ffconverter-2.3.0}/locale/ffconverter_es.ts +0 -0
  36. {ffconverter-2.2.2 → ffconverter-2.3.0}/locale/ffconverter_fr.ts +0 -0
  37. {ffconverter-2.2.2 → ffconverter-2.3.0}/locale/ffconverter_gl.ts +0 -0
  38. {ffconverter-2.2.2 → ffconverter-2.3.0}/locale/ffconverter_gl_ES.ts +0 -0
  39. {ffconverter-2.2.2 → ffconverter-2.3.0}/locale/ffconverter_hu.ts +0 -0
  40. {ffconverter-2.2.2 → ffconverter-2.3.0}/locale/ffconverter_it.ts +0 -0
  41. {ffconverter-2.2.2 → ffconverter-2.3.0}/locale/ffconverter_ms_MY.ts +0 -0
  42. {ffconverter-2.2.2 → ffconverter-2.3.0}/locale/ffconverter_pl_PL.ts +0 -0
  43. {ffconverter-2.2.2 → ffconverter-2.3.0}/locale/ffconverter_pt.ts +0 -0
  44. {ffconverter-2.2.2 → ffconverter-2.3.0}/locale/ffconverter_pt_BR.ts +0 -0
  45. {ffconverter-2.2.2 → ffconverter-2.3.0}/locale/ffconverter_ro_RO.ts +0 -0
  46. {ffconverter-2.2.2 → ffconverter-2.3.0}/locale/ffconverter_ru.ts +0 -0
  47. {ffconverter-2.2.2 → ffconverter-2.3.0}/locale/ffconverter_tr.ts +0 -0
  48. {ffconverter-2.2.2 → ffconverter-2.3.0}/locale/ffconverter_vi.ts +0 -0
  49. {ffconverter-2.2.2 → ffconverter-2.3.0}/locale/ffconverter_zh_CN.ts +0 -0
  50. {ffconverter-2.2.2 → ffconverter-2.3.0}/locale/ffconverter_zh_TW.ts +0 -0
  51. {ffconverter-2.2.2 → ffconverter-2.3.0}/man/ffconverter.1.gz +0 -0
  52. {ffconverter-2.2.2 → ffconverter-2.3.0}/resources.qrc +0 -0
  53. {ffconverter-2.2.2 → ffconverter-2.3.0}/setup.cfg +0 -0
  54. {ffconverter-2.2.2 → ffconverter-2.3.0}/setup.py +0 -0
  55. {ffconverter-2.2.2 → ffconverter-2.3.0}/share/ffconverter.png +0 -0
  56. {ffconverter-2.2.2 → ffconverter-2.3.0}/share/presets.xml +0 -0
  57. {ffconverter-2.2.2 → ffconverter-2.3.0}/test/test_dialogs.py +0 -0
@@ -1,3 +1,11 @@
1
+ Version 2.3.0
2
+ -------------
3
+
4
+ * Added toggle to use a mobile UI, which has less features
5
+ but fits on smaller screens (Linux/Windows Phones)
6
+
7
+ * Image conversion on Windows doesn't get stuck anymore
8
+
1
9
  Version 2.2.2
2
10
  -------------
3
11
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ffconverter
3
- Version: 2.2.2
3
+ Version: 2.3.0
4
4
  Summary: File Format Converter with Qt GUI
5
5
  Home-page: https://github.com/l-koehler/FF-converter
6
6
  Author: Ilias Stamatis
@@ -16,7 +16,7 @@ MacOS should work, but I don't have a Mac, so I can't test that.
16
16
  * python3
17
17
  * pyqt5
18
18
 
19
- On Linux, use your distributions package manager or pip to install these.
19
+ On Linux, use your distributions package manager or pip to install these.
20
20
  On Windows, use [python.org](https://python.org) to get python (the version
21
21
  in the Microsoft Store is [worse in some regards](https://docs.python.org/3/using/windows.html#known-issues)), then use
22
22
  `python -m pip install PyQt5` to get PyQt5.
@@ -4,8 +4,8 @@ GUI File Format Converter
4
4
 
5
5
  # version
6
6
  __major__ = 2
7
- __minor__ = 2
8
- __patch__ = 2
7
+ __minor__ = 3
8
+ __patch__ = 0
9
9
  __prerelease__ = "" # alpha, beta, rc etc.
10
10
 
11
11
  # package information
@@ -39,8 +39,7 @@ class DynamicTab(QWidget):
39
39
  hlayout2 = utils.add_to_layout('h', self.commonformatQChB)
40
40
  final_layout = utils.add_to_layout('v', hlayout1, hlayout2)
41
41
  self.setLayout(final_layout)
42
-
43
-
42
+
44
43
  # default to only common formats
45
44
  self.commonformatQChB.setChecked(True)
46
45
  self.commonformatQChB.stateChanged.connect(parent.filesList_update)
@@ -52,36 +51,12 @@ class DynamicTab(QWidget):
52
51
  """
53
52
  def fill_extension_combobox(self, list_of_files, all_supported_conversions):
54
53
  self.extQCB.clear()
55
- possible_outputs = []
56
- for input_file in list_of_files:
57
- file_outputs = []
58
- input_file_ext = utils.get_extension(input_file)
59
- for conv in all_supported_conversions:
60
- if input_file_ext in conv[0]:
61
- # append to possible_outputs only once per file
62
- file_outputs += conv[1]
63
- possible_outputs.append(file_outputs)
64
- # possible_outputs: list of lists of output formats, one per input file
65
- # valid_outputs: list of outputs possible for ALL files
66
- if len(list_of_files) > 1:
67
- valid_outputs = []
68
- for extension in sum(possible_outputs, []):
69
- available = True
70
- for i in possible_outputs:
71
- if extension not in i:
72
- available = False
73
- break
74
- if available and extension not in valid_outputs:
75
- valid_outputs.append(extension)
76
- else:
77
- first_output_list = possible_outputs[0] if possible_outputs else []
78
- valid_outputs = list(dict.fromkeys(first_output_list))
79
-
54
+ common = []
80
55
  if self.commonformatQChB.isChecked():
81
- all_common = (self.parent.settings.value('extraformats_common') or []) + config.common_formats
82
- # remove all uncommon formats from the list
83
- valid_outputs[:] = [ext for ext in valid_outputs if ext in all_common]
84
- self.extQCB.addItems(sorted(valid_outputs))
56
+ common = (self.parent.settings.value('extraformats_common') or []) + config.common_formats
57
+ outputs = utils.get_combobox_content(self, list_of_files, all_supported_conversions,
58
+ common=common)
59
+ self.extQCB.addItems(outputs)
85
60
 
86
61
  def ok_to_continue(self):
87
62
  return True
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2023 l-koehler
1
+ # Copyright (C) 2023-2024 l-koehler
2
2
  # Copyright (C) 2011-2016 Ilias Stamatis <stamatis.iliass@gmail.com>
3
3
  #
4
4
  # This program is free software: you can redistribute it and/or modify
@@ -20,6 +20,7 @@ import platform
20
20
  import textwrap
21
21
  import logging
22
22
  import webbrowser
23
+ from threading import Thread
23
24
 
24
25
  from PyQt5.QtGui import QIcon, QKeySequence
25
26
  from PyQt5.QtCore import (
@@ -29,7 +30,7 @@ from PyQt5.QtCore import (
29
30
  from PyQt5.QtWidgets import (
30
31
  QAbstractItemView, QApplication, QCheckBox, QFileDialog, QLabel,
31
32
  QLineEdit, QMainWindow, QMessageBox, QPushButton, QShortcut, QTabWidget,
32
- QToolButton, QWidget
33
+ QToolButton, QWidget, QComboBox
33
34
  )
34
35
 
35
36
  import ffconverter as ffmc
@@ -56,7 +57,90 @@ class MainWindow(QMainWindow):
56
57
  self.fnames = [] # list of file names to be converted
57
58
  self.office_listener_started = False
58
59
 
60
+ self.settings = QSettings()
61
+
62
+ mobile_ui = self.settings.value('mobile_ui', type=bool)
63
+
64
+ # needed by threaded_conversion_check, which we want to start
65
+ # as early as possible to save time on startup
66
+ if not mobile_ui:
67
+ self.dynamic_tab = DynamicTab(self)
68
+ self.audiovideo_tab = AudioVideoTab(self)
69
+ self.image_tab = ImageTab(self)
70
+ self.dependenciesQL = QLabel()
71
+ self.load_settings(self.settings)
72
+
73
+ def threaded_conversion_check(self):
74
+ self.check_for_dependencies()
75
+ return(utils.get_all_conversions(self.settings, missing=self.missing))
76
+ # Start a thread to get the conversions, join() it at the end of __init__
77
+ conversion_check_thread = utils.ThreadWithReturn(target=threaded_conversion_check, args=(self,))
78
+ conversion_check_thread.start()
79
+
59
80
  self.parse_cla()
81
+
82
+ self.setWindowTitle('FF-Converter')
83
+
84
+ openAction = utils.create_action(
85
+ self, self.tr('Open'), QKeySequence.Open, None,
86
+ self.tr('Open a file'), self.filesList_add
87
+ )
88
+ convertAction = utils.create_action(
89
+ self, self.tr('Convert'), 'Ctrl+C', None,
90
+ self.tr('Convert files'), self.start_conversion
91
+ )
92
+ quitAction = utils.create_action(
93
+ self, self.tr('Quit'), 'Ctrl+Q', None,
94
+ self.tr('Quit'), self.close
95
+ )
96
+ preferencesAction = utils.create_action(
97
+ self, self.tr('Preferences'), 'Alt+Ctrl+P',
98
+ None, self.tr('Preferences'), self.open_dialog_preferences
99
+ )
100
+
101
+ if mobile_ui:
102
+ # Mobile UI has less features, but is usable on devices
103
+ # with a smaller, vertical screen (Linux Phones)
104
+ self.name = "Mobile"
105
+ addQPB = QPushButton(self.tr('Add'))
106
+ clearQPB = QPushButton(self.tr('Clear'))
107
+ preferencesQPB = QPushButton(self.tr('Preferences'))
108
+ self.filesList = utils.FilesList()
109
+ self.filesList.setSelectionMode(QAbstractItemView.ExtendedSelection)
110
+ hlayout1 = utils.add_to_layout('h', addQPB, clearQPB, preferencesQPB, None)
111
+ vlayout1 = utils.add_to_layout('v', self.filesList, hlayout1)
112
+
113
+ outputQL = QLabel(self.tr('Output folder:'))
114
+ self.toQLE = QLineEdit()
115
+ self.toQLE.setReadOnly(True)
116
+ self.toQTB = QToolButton()
117
+ self.toQTB.setText('...')
118
+ hlayout2 = utils.add_to_layout('h', outputQL, self.toQLE, self.toQTB)
119
+
120
+ convertQL = QLabel(self.tr('Convert to:'))
121
+ self.extQCB = QComboBox()
122
+ hlayout3 = utils.add_to_layout('h', convertQL, self.extQCB)
123
+
124
+ convertQPB = QPushButton(self.tr('&Convert'))
125
+
126
+ self.filesList.dropped.connect(self.filesList_add_dragged)
127
+ addQPB.clicked.connect(self.filesList_add)
128
+ clearQPB.clicked.connect(self.filesList_clear)
129
+ self.toQTB.clicked.connect(self.get_output_folder)
130
+ convertQPB.clicked.connect(convertAction.triggered)
131
+ preferencesQPB.clicked.connect(preferencesAction.triggered)
132
+
133
+ final_layout = utils.add_to_layout('v', vlayout1, hlayout2,
134
+ hlayout3, convertQPB)
135
+
136
+ widget = QWidget()
137
+ widget.setLayout(final_layout)
138
+ self.setCentralWidget(widget)
139
+ # return early, full UI will not be created
140
+ conversion_check_thread.join()
141
+ self.all_supported_conversions = conversion_check_thread.result
142
+ return
143
+
60
144
  addQPB = QPushButton(self.tr('Add'))
61
145
  delQPB = QPushButton(self.tr('Delete'))
62
146
  clearQPB = QPushButton(self.tr('Clear'))
@@ -73,10 +157,6 @@ class MainWindow(QMainWindow):
73
157
  self.toQTB.setText('...')
74
158
  hlayout2 = utils.add_to_layout('h', outputQL, self.toQLE, self.toQTB)
75
159
 
76
- self.dynamic_tab = DynamicTab(self)
77
- self.audiovideo_tab = AudioVideoTab(self)
78
- self.image_tab = ImageTab(self)
79
-
80
160
  self.tabs = [self.dynamic_tab, self.audiovideo_tab,
81
161
  self.image_tab]
82
162
  tab_names = [self.tr('All Formats'), self.tr('Audio/Video Settings'),
@@ -97,25 +177,12 @@ class MainWindow(QMainWindow):
97
177
  final_layout = utils.add_to_layout(
98
178
  'v', hlayout1, self.tabWidget, hlayout2, hlayout3, hlayout4)
99
179
 
100
- self.dependenciesQL = QLabel()
101
180
  self.statusBar().addPermanentWidget(self.dependenciesQL, stretch=1)
102
181
 
103
182
  widget = QWidget()
104
183
  widget.setLayout(final_layout)
105
184
  self.setCentralWidget(widget)
106
185
 
107
- openAction = utils.create_action(
108
- self, self.tr('Open'), QKeySequence.Open, None,
109
- self.tr('Open a file'), self.filesList_add
110
- )
111
- convertAction = utils.create_action(
112
- self, self.tr('Convert'), 'Ctrl+C', None,
113
- self.tr('Convert files'), self.start_conversion
114
- )
115
- quitAction = utils.create_action(
116
- self, self.tr('Quit'), 'Ctrl+Q', None,
117
- self.tr('Quit'), self.close
118
- )
119
186
  edit_presetsAction = utils.create_action(
120
187
  self, self.tr('Edit Presets'), 'Ctrl+P', None,
121
188
  self.tr('Edit Presets'), self.open_dialog_presets
@@ -144,10 +211,6 @@ class MainWindow(QMainWindow):
144
211
  self, self.tr('Clear All'), None, None,
145
212
  self.tr('Clear form'), self.clear_all
146
213
  )
147
- preferencesAction = utils.create_action(
148
- self, self.tr('Preferences'), 'Alt+Ctrl+P',
149
- None, self.tr('Preferences'), self.open_dialog_preferences
150
- )
151
214
  trackerAction = utils.create_action(
152
215
  self, 'Issue tracker', None, None, None,
153
216
  lambda: webbrowser.open(
@@ -208,16 +271,14 @@ class MainWindow(QMainWindow):
208
271
  del_shortcut.setKey(Qt.Key_Delete)
209
272
  del_shortcut.activated.connect(self.filesList_delete)
210
273
 
211
- self.setWindowTitle('Multi-Converter')
212
-
213
- self.settings = self.load_settings()
214
- self.missing = self.check_for_dependencies()
215
-
216
274
  self.audiovideo_tab.set_default_command()
217
275
  self.image_tab.set_default_command()
218
276
  self.toQLE.setText(self.default_output)
219
277
 
220
- self.all_supported_conversions = utils.get_all_conversions(self.settings, missing=self.missing)
278
+ # get all supported conversions from the thread started before the UI creation
279
+ # most of the time is still spent waiting for the thread
280
+ conversion_check_thread.join()
281
+ self.all_supported_conversions = conversion_check_thread.result
221
282
 
222
283
  self.filesList_update()
223
284
 
@@ -236,6 +297,7 @@ class MainWindow(QMainWindow):
236
297
  update self.dependenciesQL with the appropriate message.
237
298
  """
238
299
  use_wsl = self.settings.value('use_wsl', type=bool)
300
+ mobile_ui = self.settings.value('mobile_ui', type=bool)
239
301
  if not utils.is_installed(self.ffmpeg_path, use_wsl):
240
302
  self.ffmpeg_path = utils.is_installed('ffmpeg', use_wsl)
241
303
  QSettings().setValue('ffmpeg_path', self.ffmpeg_path)
@@ -251,39 +313,41 @@ class MainWindow(QMainWindow):
251
313
  self.compress_gzip = utils.is_installed('gzip', use_wsl)
252
314
  self.compress_bzip2 = utils.is_installed('bzip2', use_wsl)
253
315
 
254
- missing = []
316
+ self.missing = []
255
317
  if not self.ffmpeg_path:
256
- missing.append('ffmpeg')
318
+ self.missing.append('ffmpeg')
257
319
  if not self.unoconv:
258
- missing.append('unoconv')
320
+ self.missing.append('unoconv')
259
321
  if not self.imagemagick:
260
- missing.append('imagemagick')
322
+ self.missing.append('imagemagick')
261
323
  if not self.pandoc:
262
- missing.append('pandoc')
324
+ self.missing.append('pandoc')
263
325
  if not self.compress_zip:
264
- missing.append('zip')
326
+ self.missing.append('zip')
265
327
  if not self.compress_unzip:
266
- missing.append('unzip')
328
+ self.missing.append('unzip')
267
329
  if not self.compress_tar:
268
- missing.append('tar')
330
+ self.missing.append('tar')
269
331
  if not self.compress_squash:
270
- missing.append('squashfs-tools')
332
+ self.missing.append('squashfs-tools')
271
333
  if not self.compress_ar:
272
- missing.append('binutils/ar')
334
+ self.missing.append('binutils/ar')
273
335
  if not self.compress_gzip:
274
- missing.append('gzip')
336
+ self.missing.append('gzip')
275
337
  if not self.compress_bzip2:
276
- missing.append('bzip2')
338
+ self.missing.append('bzip2')
277
339
 
278
- if missing:
279
- missing = ', '.join(missing)
280
- status = self.tr('Missing dependencies:') + ' ' + missing
281
- self.dependenciesQL.setText(status)
282
- return missing
340
+ if self.missing:
341
+ self.missing = ', '.join(self.missing)
342
+ status = self.tr('Missing dependencies:') + ' ' + self.missing
343
+ if mobile_ui:
344
+ print(status)
345
+ else:
346
+ self.dependenciesQL.setText(status)
283
347
 
284
- def load_settings(self):
285
- settings = QSettings()
348
+ def load_settings(self, settings):
286
349
 
350
+ self.mobile_ui = settings.value('mobile_ui', type=bool)
287
351
  self.overwrite_existing = settings.value('overwrite_existing', type=bool)
288
352
  self.default_output = settings.value('default_output', type=str)
289
353
  self.prefix = settings.value('prefix', type=str)
@@ -295,9 +359,6 @@ class MainWindow(QMainWindow):
295
359
  extraformats_video = (settings.value('extraformats_video') or [])
296
360
  videocodecs = (settings.value('videocodecs') or config.video_codecs)
297
361
  audiocodecs = (settings.value('audiocodecs') or config.audio_codecs)
298
- self.default_command_image = (settings.value('default_command_image',
299
- type=str) or
300
- config.default_imagemagick_cmd)
301
362
  extraformats_image = (settings.value('extraformats_image') or [])
302
363
  extraformats_document = (settings.value('extraformats_document') or [])
303
364
  extraformats_markdown = (settings.value('extraformats_markdown') or [])
@@ -306,12 +367,15 @@ class MainWindow(QMainWindow):
306
367
  extraformats_common = (settings.value('extraformats_common') or [])
307
368
  extraformats_double = (settings.value('extraformats_double') or [])
308
369
 
309
- self.audiovideo_tab.fill_video_comboboxes(videocodecs,
370
+ if not self.mobile_ui:
371
+ self.audiovideo_tab.fill_video_comboboxes(videocodecs,
310
372
  audiocodecs, extraformats_video)
311
- self.image_tab.fill_extension_combobox(extraformats_image)
312
-
373
+ self.image_tab.fill_extension_combobox(extraformats_image)
374
+ self.default_command_image = (settings.value('default_command_image',
375
+ type=str) or config.default_imagemagick_cmd)
376
+
313
377
  return settings
314
-
378
+
315
379
  def get_current_tab(self):
316
380
  for i in self.tabs:
317
381
  if self.tabs.index(i) == self.tabWidget.currentIndex():
@@ -321,16 +385,24 @@ class MainWindow(QMainWindow):
321
385
  self.filesList.clear()
322
386
  for i in self.fnames:
323
387
  self.filesList.addItem(i)
324
- # update dynamic tab
325
- # dynamic_tab takes not extra formats, but a list of all files added
326
- self.dynamic_tab.fill_extension_combobox(self.fnames, self.all_supported_conversions)
388
+ if self.mobile_ui:
389
+ # update mobile UI combobox
390
+ self.extQCB.clear()
391
+ outputs = utils.get_combobox_content(self, self.fnames,
392
+ self.all_supported_conversions, [])
393
+ self.extQCB.addItems(outputs)
394
+ else:
395
+ # update dynamic tab
396
+ # dynamic_tab takes not extra formats, but a list of all files added
397
+ self.dynamic_tab.fill_extension_combobox(self.fnames, self.all_supported_conversions)
327
398
 
328
399
  def filesList_add(self):
329
400
  filters = 'All Files (*);;'
330
- filters += 'Audio/Video Files (*.{});;'.format(
331
- ' *.'.join(self.audiovideo_tab.formats))
332
- filters += 'Image Files (*.{});;'.format(
333
- ' *.'.join(self.image_tab.formats + self.image_tab.extra_img))
401
+ if not self.mobile_ui:
402
+ filters += 'Audio/Video Files (*.{});;'.format(
403
+ ' *.'.join(self.audiovideo_tab.formats))
404
+ filters += 'Image Files (*.{});;'.format(
405
+ ' *.'.join(self.image_tab.formats + self.image_tab.extra_img))
334
406
 
335
407
  fnames = QFileDialog.getOpenFileNames(self, 'FF Multi Converter - ' +
336
408
  self.tr('Choose File'), config.home, filters,
@@ -413,17 +485,23 @@ class MainWindow(QMainWindow):
413
485
 
414
486
  Return False if an error arises, else True.
415
487
  """
488
+ if self.mobile_ui:
489
+ origQCB_status = False
490
+ tab_ok = True
491
+ else:
492
+ origQCB_status = self.origQCB.isChecked()
493
+ tab_ok = self.get_current_tab().ok_to_continue()
416
494
  try:
417
495
  if not self.fnames:
418
496
  raise ValidationError(
419
497
  self.tr('You must add at least one file to convert!'))
420
- elif not self.origQCB.isChecked() and not self.toQLE.text():
498
+ elif not origQCB_status and not self.toQLE.text():
421
499
  raise ValidationError(
422
500
  self.tr('You must choose an output folder!'))
423
- elif (not self.origQCB.isChecked() and
501
+ elif (not origQCB_status and
424
502
  not os.path.exists(self.toQLE.text())):
425
503
  raise ValidationError(self.tr('Output folder does not exists!'))
426
- if not self.get_current_tab().ok_to_continue():
504
+ if not tab_ok:
427
505
  return False
428
506
  return True
429
507
 
@@ -440,28 +518,39 @@ class MainWindow(QMainWindow):
440
518
  if not self.ok_to_continue():
441
519
  return
442
520
 
443
- tab = self.get_current_tab()
521
+ if self.mobile_ui:
522
+ tab = self
523
+ else:
524
+ tab = self.get_current_tab()
444
525
  ext_to = '.' + tab.extQCB.currentText()
445
526
 
446
527
  if tab.name == 'All Formats' and not self.office_listener_started:
447
528
  utils.start_office_listener()
448
529
  self.office_listener_started = True
449
530
 
450
- _list = utils.create_paths_list(
451
- self.fnames, ext_to, self.prefix, self.suffix,
452
- self.toQLE.text(), self.origQCB.isChecked(),
453
- self.overwrite_existing
454
- )
531
+ if self.mobile_ui:
532
+ _list = utils.create_paths_list(
533
+ self.fnames, ext_to, self.prefix, self.suffix,
534
+ self.toQLE.text(), False, self.overwrite_existing
535
+ )
536
+ dialog = progress.Progress(
537
+ _list, tab, False, self)
538
+ else:
539
+ _list = utils.create_paths_list(
540
+ self.fnames, ext_to, self.prefix, self.suffix,
541
+ self.toQLE.text(), self.origQCB.isChecked(),
542
+ self.overwrite_existing
543
+ )
544
+ dialog = progress.Progress(
545
+ _list, tab, self.deleteQCB.isChecked(), self)
455
546
 
456
- dialog = progress.Progress(
457
- _list, tab, self.deleteQCB.isChecked(), self)
458
547
  dialog.show()
459
548
 
460
549
  def open_dialog_preferences(self):
461
550
  """Open the preferences dialog."""
462
551
  dialog = preferences_dlg.Preferences(self)
463
552
  if dialog.exec_():
464
- self.load_settings()
553
+ self.load_settings(self.settings)
465
554
 
466
555
  def open_dialog_presets(self):
467
556
  """Open the presets dialog."""
@@ -20,7 +20,7 @@ from PyQt5.QtCore import QSettings, QTimer
20
20
  from PyQt5.QtWidgets import (
21
21
  QDialog, QDialogButtonBox, QFileDialog, QLabel, QLineEdit,
22
22
  QRadioButton, QSpacerItem, QTabWidget, QToolButton, QWidget,
23
- QPlainTextEdit, QPushButton, QCheckBox
23
+ QPlainTextEdit, QPushButton, QCheckBox, QMessageBox
24
24
  )
25
25
 
26
26
  from ffconverter import utils
@@ -67,11 +67,12 @@ class Preferences(QDialog):
67
67
  [self.commonformatsQPTE, self.doubleformatsQPTE])
68
68
  other_layout = utils.add_to_layout('h', other_grid, None)
69
69
 
70
+ self.mobile_uiQChB = QCheckBox(self.tr("Use (smaller) mobile UI"))
70
71
  if os.name == 'nt':
71
72
  self.use_wslQChB = QCheckBox(self.tr("Windows: Use WSL for conversions"))
72
- use_wslGrid = utils.add_to_grid([self.use_wslQChB])
73
+ other_optionsGrid = utils.add_to_grid([self.use_wslQChB],[self.mobile_uiQChB])
73
74
  else:
74
- use_wslGrid = None
75
+ other_optionsGrid = utils.add_to_grid([self.mobile_uiQChB])
75
76
 
76
77
  tabwidget1_layout = utils.add_to_layout(
77
78
  'v', saveQL,
@@ -81,7 +82,7 @@ class Preferences(QDialog):
81
82
  QSpacerItem(14, 13), prefix_layout,
82
83
  QSpacerItem(14, 13), otherQL,
83
84
  QSpacerItem(14, 13), other_layout,
84
- QSpacerItem(14, 13), use_wslGrid
85
+ QSpacerItem(14, 13), other_optionsGrid,
85
86
  )
86
87
 
87
88
  ffmpegQL = QLabel('<html><b>FFmpeg</b></html>')
@@ -195,6 +196,7 @@ class Preferences(QDialog):
195
196
  def load_settings(self):
196
197
  """Load settings and update graphical widgets with loaded values."""
197
198
  settings = QSettings()
199
+ mobile_ui = settings.value('mobile_ui', type=bool)
198
200
  overwrite_existing = settings.value('overwrite_existing', type=bool)
199
201
  default_output = settings.value('default_output', type=str)
200
202
  prefix = settings.value('prefix', type=str)
@@ -219,6 +221,10 @@ class Preferences(QDialog):
219
221
  self.exst_overwriteQRB.setChecked(True)
220
222
  else:
221
223
  self.exst_prefixQRB.setChecked(True)
224
+ if mobile_ui:
225
+ self.mobile_uiQChB.setChecked(True)
226
+ else:
227
+ self.mobile_uiQChB.setChecked
222
228
 
223
229
  self.defaultQLE.setText(default_output)
224
230
  self.prefixQLE.setText(prefix)
@@ -272,6 +278,9 @@ class Preferences(QDialog):
272
278
  def save_settings(self):
273
279
  """Set settings values by extracting the appropriate information from
274
280
  the graphical widgets."""
281
+ # set this to true if something that requires a restart was changed
282
+ ui_change = False
283
+
275
284
  videocodecs = self.plaintext_to_list(self.vidcodecsQPTE)
276
285
  audiocodecs = self.plaintext_to_list(self.audcodecsQPTE)
277
286
  extraformats_video = self.plaintext_to_list(self.extraformatsffmpegQPTE,
@@ -298,6 +307,11 @@ class Preferences(QDialog):
298
307
  if not utils.is_installed(ffmpeg_path, use_wsl):
299
308
  ffmpeg_path = utils.is_installed('ffmpeg', use_wsl)
300
309
 
310
+ # check if the UI was changed
311
+ if settings.value('mobile_ui', type=bool) != self.mobile_uiQChB.isChecked():
312
+ ui_change = True
313
+
314
+ settings.setValue('mobile_ui', self.mobile_uiQChB.isChecked())
301
315
  settings.setValue('overwrite_existing', self.exst_overwriteQRB.isChecked())
302
316
  settings.setValue('default_output', self.defaultQLE.text())
303
317
  settings.setValue('prefix', self.prefixQLE.text())
@@ -315,4 +329,16 @@ class Preferences(QDialog):
315
329
  settings.setValue('extraformats_double', sorted(extraformats_double))
316
330
  settings.setValue('use_wsl', use_wsl)
317
331
 
318
- self.accept()
332
+ if ui_change:
333
+ ret = QMessageBox.question(self,'',
334
+ self.tr("The changed settings require a program restart. Close Program?"),
335
+ QMessageBox.Yes | QMessageBox.No)
336
+ if ret == QMessageBox.Yes:
337
+ self.accept()
338
+ exit(0)
339
+ else:
340
+ # reset changes to mobile_ui and mobile_uiQChB
341
+ settings.setValue('mobile_ui', not self.mobile_uiQChB.isChecked())
342
+ self.mobile_uiQChB.setChecked(not self.mobile_uiQChB.isChecked())
343
+ else:
344
+ self.accept()
@@ -174,7 +174,7 @@ class Progress(QDialog):
174
174
  self.cancelQPB.setText(self.tr("Close"))
175
175
 
176
176
  if self.shutdownQCB.isChecked():
177
- # Locating systemctl in WSL would be pointless
177
+ # Locating systemctl in WSL would be pointless, use_wsl is False
178
178
  if utils.is_installed('systemctl', False):
179
179
  subprocess.call(shlex.split('systemctl poweroff'))
180
180
  else:
@@ -25,6 +25,7 @@ import shlex
25
25
  import subprocess
26
26
  import time
27
27
  import string
28
+ import threading
28
29
 
29
30
  from PyQt5.QtCore import pyqtSignal, QSize, Qt, QSettings
30
31
  from PyQt5.QtWidgets import (
@@ -33,6 +34,11 @@ from PyQt5.QtWidgets import (
33
34
  )
34
35
  from ffconverter import config
35
36
 
37
+ class ThreadWithReturn(threading.Thread):
38
+ def __init__(self, group=None, target=None, name=None, args=(), kwargs={}, *, daemon=None):
39
+ def function():
40
+ self.result = target(*args, **kwargs)
41
+ super().__init__(group=group, target=function, name=name, daemon=daemon)
36
42
 
37
43
  def duration_in_seconds(duration):
38
44
  """
@@ -64,7 +70,8 @@ def is_installed(program, use_wsl):
64
70
  if os.path.isfile(fpath) and os.access(fpath, os.X_OK):
65
71
  return fpath
66
72
  # imagemagick 6 uses 'convert', version 7 uses 'magick'
67
- if program == 'magick':
73
+ # 'convert' is reserved for fat-to-ntfs conversion in windows
74
+ if program == 'magick' and os.name != 'nt':
68
75
  return is_installed('convert', use_wsl)
69
76
 
70
77
  # only if nothing was found on the regular system, check WSL
@@ -165,7 +172,7 @@ def get_all_conversions(settings, get_conv_for_ext = False,
165
172
  supported_tmp.append(ffmpeg_conversions)
166
173
  else:
167
174
  ffmpeg_conversions = [[], []]
168
-
175
+
169
176
  # poll pandoc
170
177
  if 'pandoc' not in missing:
171
178
  extraformats_markdown = (settings.value('extraformats_markdown') or [])
@@ -224,7 +231,7 @@ def get_all_conversions(settings, get_conv_for_ext = False,
224
231
  supported_tmp.append(magick_conversions)
225
232
  else:
226
233
  magick_conversions = [[], []]
227
-
234
+
228
235
  # libreoffice exts
229
236
  # cant actually get those right now, so have some predefined lists instead
230
237
  if 'unoconv' not in missing:
@@ -319,7 +326,7 @@ def get_extension(file_path):
319
326
 
320
327
  # if there are quotation marks around the file path, remove them
321
328
  settings = QSettings()
322
-
329
+
323
330
  file_path = file_path.replace("\"", "")
324
331
 
325
332
  remainder, first_ext = os.path.splitext(file_path)
@@ -332,6 +339,39 @@ def get_extension(file_path):
332
339
  return joined_ext
333
340
  return first_ext
334
341
 
342
+ def get_combobox_content(self, list_of_files, all_supported_conversions,
343
+ common=[]):
344
+ possible_outputs = []
345
+ for input_file in list_of_files:
346
+ file_outputs = []
347
+ input_file_ext = get_extension(input_file)
348
+ for conv in all_supported_conversions:
349
+ if input_file_ext in conv[0]:
350
+ # append to possible_outputs only once per file
351
+ file_outputs += conv[1]
352
+ possible_outputs.append(file_outputs)
353
+ # possible_outputs: list of lists of output formats, one per input file
354
+ # valid_outputs: list of outputs possible for ALL files
355
+ if len(list_of_files) > 1:
356
+ valid_outputs = []
357
+ for extension in sum(possible_outputs, []):
358
+ available = True
359
+ for i in possible_outputs:
360
+ if extension not in i:
361
+ available = False
362
+ break
363
+ if available and extension not in valid_outputs:
364
+ valid_outputs.append(extension)
365
+ else:
366
+ # flatten and deduplicate
367
+ first_output_list = possible_outputs[0] if possible_outputs else []
368
+ valid_outputs = list(dict.fromkeys(first_output_list))
369
+
370
+ if common != []:
371
+ # remove all uncommon formats from the list
372
+ valid_outputs[:] = [ext for ext in valid_outputs if ext in common]
373
+ return valid_outputs
374
+
335
375
  def start_office_listener():
336
376
  """
337
377
  Start a openoffice/libreoffice listener.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ffconverter
3
- Version: 2.2.2
3
+ Version: 2.3.0
4
4
  Summary: File Format Converter with Qt GUI
5
5
  Home-page: https://github.com/l-koehler/FF-converter
6
6
  Author: Ilias Stamatis
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env xdg-open
2
2
  [Desktop Entry]
3
3
  Type=Application
4
- Version=2.2.0
4
+ Version=2.3.0
5
5
  Name=FF Converter
6
6
  GenericName=File Format Converter
7
7
  GenericName[pt]=Conversor de ficheiros
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes