From 15d70474d4df16cd03f4eb672d409166f793eabf Mon Sep 17 00:00:00 2001 From: tassaron Date: Tue, 25 Jul 2017 22:02:47 -0400 Subject: [PATCH] error can be locked within properties() and simplified the componenterrors again --- src/component.py | 52 +++++++++++++--------------------------- src/components/video.py | 35 ++++++++++++--------------- src/mainwindow.py | 10 ++++---- src/presetmanager.pyc | Bin 0 -> 10936 bytes src/toolkit/ffmpeg.py | 4 ++-- src/video_thread.py | 11 +++++---- 6 files changed, 46 insertions(+), 66 deletions(-) create mode 100644 src/presetmanager.pyc diff --git a/src/component.py b/src/component.py index 7a768ed..5de67d1 100644 --- a/src/component.py +++ b/src/component.py @@ -19,7 +19,7 @@ class ComponentMetaclass(type(QtCore.QObject)): return func(self, *args, **kwargs) except Exception: try: - raise ComponentInitError(self, 'initialization process') + raise ComponentError(self, 'initialization process') except ComponentError: return return initializationWrapper @@ -63,7 +63,13 @@ class ComponentMetaclass(type(QtCore.QObject)): if self._lockedProperties is not None: return self._lockedProperties else: - return func(self) + try: + return func(self) + except Exception: + try: + raise ComponentError(self, 'properties') + except ComponentError: + return [] return propertiesWrapper def errorWrapper(func): @@ -396,18 +402,18 @@ class Component(QtCore.QObject, metaclass=ComponentMetaclass): ''' -class ComponentException(RuntimeError): - '''A base class for component errors''' +class ComponentError(RuntimeError): + '''Gives the MainWindow a traceback to display, and cancels the export.''' - _prevErrors = [] + prevErrors = [] - def __init__(self, caller, name, immediate): + def __init__(self, caller, name): print('ComponentError by %s: %s' % (caller.name, name)) super().__init__() - if len(ComponentException._prevErrors) > 1: - ComponentException._prevErrors.pop() - ComponentException._prevErrors.insert(0, name) - if name in ComponentException._prevErrors[1:]: + if len(ComponentError.prevErrors) > 1: + ComponentError.prevErrors.pop() + ComponentError.prevErrors.insert(0, name) + if name in ComponentError.prevErrors[1:]: # Don't create multiple windows for repeated messages return @@ -434,28 +440,4 @@ class ComponentException(RuntimeError): ) ) - if immediate: - caller._error.emit(string, detail) - else: - caller.lockProperties(['error']) - caller.lockError((string, detail)) - - -class ComponentError(ComponentException): - ''' - Use for general Python errors caused by a component at any time. - Raising this gives the MainWindow a traceback to display and - cancels any export in progress. - ''' - def __init__(self, caller, name): - ComponentException.__init__(self, caller, name, True) - - -class ComponentInitError(ComponentError): - ''' - Use for Python errors in preFrameRender, while the export is starting. - This will end the video thread in a clean way by locking the component - into an error state so the export definitely won't begin. - ''' - def __init__(self, caller, name): - ComponentException.__init__(self, caller, name, False) + caller._error.emit(string, detail) diff --git a/src/components/video.py b/src/components/video.py index 6b0a04a..8872fbf 100644 --- a/src/components/video.py +++ b/src/components/video.py @@ -154,33 +154,28 @@ class Component(Component): return frame def properties(self): - # TODO: Disallow selecting the same video you're exporting to props = [] - if not self.videoPath or self.badVideo \ - or not os.path.exists(self.videoPath): - return ['error'] + + if not self.videoPath: + self.lockError("There is no video selected.") + elif self.badVideo: + self.lockError("Could not identify an audio stream in this video.") + elif not os.path.exists(self.videoPath): + self.lockError("The video selected does not exist!") + elif (os.path.realpath(self.videoPath) == + os.path.realpath( + self.parent.window.lineEdit_outputFile.text())): + self.lockError("Input and output paths match.") if self.useAudio: props.append('audio') - self.testAudioStream() - if self.badAudio: - return ['error'] + if not testAudioStream(self.videoPath) \ + and self.error() is None: + self.lockError( + "Could not identify an audio stream in this video.") return props - def error(self): - if self.badAudio: - return "Could not identify an audio stream in this video." - if not self.videoPath: - return "There is no video selected." - if not os.path.exists(self.videoPath): - return "The video selected does not exist!" - if self.badVideo: - return "The video selected is corrupt!" - - def testAudioStream(self): - self.badAudio = testAudioStream(self.videoPath) - def audio(self): params = {} if self.volume != 1.0: diff --git a/src/mainwindow.py b/src/mainwindow.py index 3cc5d26..e478d19 100644 --- a/src/mainwindow.py +++ b/src/mainwindow.py @@ -573,16 +573,16 @@ class MainWindow(QtWidgets.QMainWindow): @QtCore.pyqtSlot(str, str) def videoThreadError(self, msg, detail): - self.showMessage( - msg=msg, - detail=detail, - icon='Warning', - ) try: self.stopVideo() except AttributeError as e: if 'videoWorker' not in str(e): raise + self.showMessage( + msg=msg, + detail=detail, + icon='Warning', + ) def changeEncodingStatus(self, status): self.encoding = status diff --git a/src/presetmanager.pyc b/src/presetmanager.pyc new file mode 100644 index 0000000000000000000000000000000000000000..97069d2510a2ecfd40acb69d20dc6c8bf76ef1e6 GIT binary patch literal 10936 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 literal 0 HcmV?d00001 diff --git a/src/toolkit/ffmpeg.py b/src/toolkit/ffmpeg.py index 8f5ae87..8d63659 100644 --- a/src/toolkit/ffmpeg.py +++ b/src/toolkit/ffmpeg.py @@ -224,9 +224,9 @@ def testAudioStream(filename): try: checkOutput(audioTestCommand, stderr=subprocess.DEVNULL) except subprocess.CalledProcessError: - return True - else: return False + else: + return True def getAudioDuration(filename): diff --git a/src/video_thread.py b/src/video_thread.py index 8cbe8a8..48f3729 100644 --- a/src/video_thread.py +++ b/src/video_thread.py @@ -163,24 +163,27 @@ class Worker(QtCore.QObject): except ComponentError: pass - if 'error' in comp.properties(): + compProps = comp.properties() + if 'error' in compProps or comp.error() is not None: self.cancel() self.canceled = True canceledByComponent = True compError = comp.error() \ if type(comp.error()) is tuple else (comp.error(), '') errMsg = ( - "Component #%s encountered an error!" % compNo + "Component #%s (%s) encountered an error!" % ( + str(compNo), comp.name + ) if comp.error() is None else 'Export cancelled by component #%s (%s): %s' % ( str(compNo), - str(comp), + comp.name, compError[0] ) ) comp._error.emit(errMsg, compError[1]) break - if 'static' in comp.properties(): + if 'static' in compProps: self.staticComponents[compNo] = \ comp.frameRender(compNo, 0).copy()