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.
- {ffconverter-2.2.2 → ffconverter-2.3.0}/ChangeLog +8 -0
- {ffconverter-2.2.2 → ffconverter-2.3.0}/PKG-INFO +1 -1
- {ffconverter-2.2.2 → ffconverter-2.3.0}/README.md +1 -1
- {ffconverter-2.2.2 → ffconverter-2.3.0}/ffconverter/__init__.py +2 -2
- {ffconverter-2.2.2 → ffconverter-2.3.0}/ffconverter/dynamictab.py +6 -31
- {ffconverter-2.2.2 → ffconverter-2.3.0}/ffconverter/ffconverter.py +163 -74
- {ffconverter-2.2.2 → ffconverter-2.3.0}/ffconverter/preferences_dlg.py +31 -5
- {ffconverter-2.2.2 → ffconverter-2.3.0}/ffconverter/progress.py +1 -1
- {ffconverter-2.2.2 → ffconverter-2.3.0}/ffconverter/utils.py +44 -4
- {ffconverter-2.2.2 → ffconverter-2.3.0}/ffconverter.egg-info/PKG-INFO +1 -1
- {ffconverter-2.2.2 → ffconverter-2.3.0}/share/ffconverter.desktop +1 -1
- {ffconverter-2.2.2 → ffconverter-2.3.0}/.gitignore +0 -0
- {ffconverter-2.2.2 → ffconverter-2.3.0}/AUTHORS +0 -0
- {ffconverter-2.2.2 → ffconverter-2.3.0}/COPYING +0 -0
- {ffconverter-2.2.2 → ffconverter-2.3.0}/MANIFEST.in +0 -0
- {ffconverter-2.2.2 → ffconverter-2.3.0}/TRANSLATORS +0 -0
- {ffconverter-2.2.2 → ffconverter-2.3.0}/bin/ffconverter +0 -0
- {ffconverter-2.2.2 → ffconverter-2.3.0}/ffconverter/about_dlg.py +0 -0
- {ffconverter-2.2.2 → ffconverter-2.3.0}/ffconverter/audiovideotab.py +0 -0
- {ffconverter-2.2.2 → ffconverter-2.3.0}/ffconverter/config.py +0 -0
- {ffconverter-2.2.2 → ffconverter-2.3.0}/ffconverter/imagetab.py +0 -0
- {ffconverter-2.2.2 → ffconverter-2.3.0}/ffconverter/presets_dlgs.py +0 -0
- {ffconverter-2.2.2 → ffconverter-2.3.0}/ffconverter/qrc_resources.py +0 -0
- {ffconverter-2.2.2 → ffconverter-2.3.0}/ffconverter.egg-info/SOURCES.txt +0 -0
- {ffconverter-2.2.2 → ffconverter-2.3.0}/ffconverter.egg-info/dependency_links.txt +0 -0
- {ffconverter-2.2.2 → ffconverter-2.3.0}/ffconverter.egg-info/top_level.txt +0 -0
- {ffconverter-2.2.2 → ffconverter-2.3.0}/launcher +0 -0
- {ffconverter-2.2.2 → ffconverter-2.3.0}/locale/ffconverter.pro +0 -0
- {ffconverter-2.2.2 → ffconverter-2.3.0}/locale/ffconverter_bg.ts +0 -0
- {ffconverter-2.2.2 → ffconverter-2.3.0}/locale/ffconverter_ca.ts +0 -0
- {ffconverter-2.2.2 → ffconverter-2.3.0}/locale/ffconverter_cs.ts +0 -0
- {ffconverter-2.2.2 → ffconverter-2.3.0}/locale/ffconverter_de_DE.ts +0 -0
- {ffconverter-2.2.2 → ffconverter-2.3.0}/locale/ffconverter_el.ts +0 -0
- {ffconverter-2.2.2 → ffconverter-2.3.0}/locale/ffconverter_en.ts +0 -0
- {ffconverter-2.2.2 → ffconverter-2.3.0}/locale/ffconverter_es.ts +0 -0
- {ffconverter-2.2.2 → ffconverter-2.3.0}/locale/ffconverter_fr.ts +0 -0
- {ffconverter-2.2.2 → ffconverter-2.3.0}/locale/ffconverter_gl.ts +0 -0
- {ffconverter-2.2.2 → ffconverter-2.3.0}/locale/ffconverter_gl_ES.ts +0 -0
- {ffconverter-2.2.2 → ffconverter-2.3.0}/locale/ffconverter_hu.ts +0 -0
- {ffconverter-2.2.2 → ffconverter-2.3.0}/locale/ffconverter_it.ts +0 -0
- {ffconverter-2.2.2 → ffconverter-2.3.0}/locale/ffconverter_ms_MY.ts +0 -0
- {ffconverter-2.2.2 → ffconverter-2.3.0}/locale/ffconverter_pl_PL.ts +0 -0
- {ffconverter-2.2.2 → ffconverter-2.3.0}/locale/ffconverter_pt.ts +0 -0
- {ffconverter-2.2.2 → ffconverter-2.3.0}/locale/ffconverter_pt_BR.ts +0 -0
- {ffconverter-2.2.2 → ffconverter-2.3.0}/locale/ffconverter_ro_RO.ts +0 -0
- {ffconverter-2.2.2 → ffconverter-2.3.0}/locale/ffconverter_ru.ts +0 -0
- {ffconverter-2.2.2 → ffconverter-2.3.0}/locale/ffconverter_tr.ts +0 -0
- {ffconverter-2.2.2 → ffconverter-2.3.0}/locale/ffconverter_vi.ts +0 -0
- {ffconverter-2.2.2 → ffconverter-2.3.0}/locale/ffconverter_zh_CN.ts +0 -0
- {ffconverter-2.2.2 → ffconverter-2.3.0}/locale/ffconverter_zh_TW.ts +0 -0
- {ffconverter-2.2.2 → ffconverter-2.3.0}/man/ffconverter.1.gz +0 -0
- {ffconverter-2.2.2 → ffconverter-2.3.0}/resources.qrc +0 -0
- {ffconverter-2.2.2 → ffconverter-2.3.0}/setup.cfg +0 -0
- {ffconverter-2.2.2 → ffconverter-2.3.0}/setup.py +0 -0
- {ffconverter-2.2.2 → ffconverter-2.3.0}/share/ffconverter.png +0 -0
- {ffconverter-2.2.2 → ffconverter-2.3.0}/share/presets.xml +0 -0
- {ffconverter-2.2.2 → ffconverter-2.3.0}/test/test_dialogs.py +0 -0
|
@@ -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.
|
|
@@ -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
|
-
|
|
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
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
self.extQCB.addItems(
|
|
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
|
-
|
|
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
|
-
|
|
282
|
-
|
|
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.
|
|
370
|
+
if not self.mobile_ui:
|
|
371
|
+
self.audiovideo_tab.fill_video_comboboxes(videocodecs,
|
|
310
372
|
audiocodecs, extraformats_video)
|
|
311
|
-
|
|
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
|
-
|
|
325
|
-
|
|
326
|
-
|
|
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
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
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
|
-
|
|
73
|
+
other_optionsGrid = utils.add_to_grid([self.use_wslQChB],[self.mobile_uiQChB])
|
|
73
74
|
else:
|
|
74
|
-
|
|
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),
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
|
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
|
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|