OpenShot Video Editor  2.0.0
export.py
Go to the documentation of this file.
1 ##
2 #
3 # @file
4 # @brief This file loads the Video Export dialog (i.e where is all preferences)
5 # @author Jonathan Thomas <jonathan@openshot.org>
6 #
7 # @section LICENSE
8 #
9 # Copyright (c) 2008-2016 OpenShot Studios, LLC
10 # (http://www.openshotstudios.com). This file is part of
11 # OpenShot Video Editor (http://www.openshot.org), an open-source project
12 # dedicated to delivering high quality video editing and animation solutions
13 # to the world.
14 #
15 # OpenShot Video Editor is free software: you can redistribute it and/or modify
16 # it under the terms of the GNU General Public License as published by
17 # the Free Software Foundation, either version 3 of the License, or
18 # (at your option) any later version.
19 #
20 # OpenShot Video Editor is distributed in the hope that it will be useful,
21 # but WITHOUT ANY WARRANTY; without even the implied warranty of
22 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 # GNU General Public License for more details.
24 #
25 # You should have received a copy of the GNU General Public License
26 # along with OpenShot Library. If not, see <http://www.gnu.org/licenses/>.
27 #
28 
29 import os
30 import locale
31 import xml.dom.minidom as xml
32 import functools
33 
34 from PyQt5.QtCore import *
35 from PyQt5.QtWidgets import *
36 import openshot # Python module for libopenshot (required video editing module installed separately)
37 
38 from classes import info, ui_util, settings
39 from classes.app import get_app
40 from classes.logger import log
41 from classes.metrics import *
42 
43 
44 ##
45 # Export Dialog
46 class Export(QDialog):
47 
48  # Path to ui file
49  ui_path = os.path.join(info.PATH, 'windows', 'ui', 'export.ui')
50 
51  def __init__(self):
52 
53  # Create dialog class
54  QDialog.__init__(self)
55 
56  # Load UI from designer
57  ui_util.load_ui(self, self.ui_path)
58 
59  # Init UI
60  ui_util.init_ui(self)
61 
62  # get translations
63  app = get_app()
64  _ = app._tr
65 
66  # Get settings
68 
69  # Track metrics
70  track_metric_screen("export-screen")
71 
72  # Dynamically load tabs from settings data
73  self.settings_data = settings.get_settings().get_all_settings()
74 
75  # Add buttons to interface
76  self.export_button = QPushButton(_('Export Video'))
77  self.buttonBox.addButton(self.export_button, QDialogButtonBox.AcceptRole)
78  self.buttonBox.addButton(QPushButton(_('Cancel')), QDialogButtonBox.RejectRole)
79  self.exporting = False
80 
81  # Get the original timeline settings
82  self.original_width = get_app().window.timeline_sync.timeline.info.width
83  self.original_height = get_app().window.timeline_sync.timeline.info.height
84  fps = get_app().window.timeline_sync.timeline.info.fps
85  self.original_fps = { "num" : fps.num, "den" : fps.den }
86  self.original_sample_rate = get_app().window.timeline_sync.timeline.info.sample_rate
87  self.original_channels = get_app().window.timeline_sync.timeline.info.channels
88  self.original_channel_layout = get_app().window.timeline_sync.timeline.info.channel_layout
89 
90  # Default export path
91  recommended_path = recommended_path = os.path.join(info.HOME_PATH)
92  if app.project.current_filepath:
93  recommended_path = os.path.dirname(app.project.current_filepath)
94 
95  export_path = get_app().project.get(["export_path"])
96  if os.path.exists(export_path):
97  # Use last selected export path
98  self.txtExportFolder.setText(export_path)
99  else:
100  # Default to home dir
101  self.txtExportFolder.setText(recommended_path)
102 
103  # Is this a saved project?
104  if not get_app().project.current_filepath:
105  # Not saved yet
106  self.txtFileName.setText(_("Untitled Project"))
107  else:
108  # Yes, project is saved
109  # Get just the filename
110  parent_path, filename = os.path.split(get_app().project.current_filepath)
111  filename, ext = os.path.splitext(filename)
112  self.txtFileName.setText(filename.replace("_", " ").replace("-", " ").capitalize())
113 
114  # Default image type
115  self.txtImageFormat.setText("%05.png")
116 
117  # Loop through Export To options
118  export_options = [_("Video & Audio"), _("Image Sequence")]
119  for option in export_options:
120  # append profile to list
121  self.cboExportTo.addItem(option)
122 
123  # Add channel layouts
125  for layout in [(openshot.LAYOUT_MONO, _("Mono (1 Channel)")),
126  (openshot.LAYOUT_STEREO, _("Stereo (2 Channel)")),
127  (openshot.LAYOUT_SURROUND, _("Surround (3 Channel)")),
128  (openshot.LAYOUT_5POINT1, _("Surround (5.1 Channel)")),
129  (openshot.LAYOUT_7POINT1, _("Surround (7.1 Channel)"))]:
130  log.info(layout)
131  self.channel_layout_choices.append(layout[0])
132  self.cboChannelLayout.addItem(layout[1], layout[0])
133 
134  # Connect signals
135  self.btnBrowse.clicked.connect(functools.partial(self.btnBrowse_clicked))
136  self.cboSimpleProjectType.currentIndexChanged.connect(
137  functools.partial(self.cboSimpleProjectType_index_changed, self.cboSimpleProjectType))
138  self.cboProfile.currentIndexChanged.connect(functools.partial(self.cboProfile_index_changed, self.cboProfile))
139  self.cboSimpleTarget.currentIndexChanged.connect(
140  functools.partial(self.cboSimpleTarget_index_changed, self.cboSimpleTarget))
141  self.cboSimpleVideoProfile.currentIndexChanged.connect(
142  functools.partial(self.cboSimpleVideoProfile_index_changed, self.cboSimpleVideoProfile))
143  self.cboSimpleQuality.currentIndexChanged.connect(
144  functools.partial(self.cboSimpleQuality_index_changed, self.cboSimpleQuality))
145  self.cboChannelLayout.currentIndexChanged.connect(self.updateChannels)
146 
147 
148  # ********* Advaned Profile List **********
149  # Loop through profiles
150  self.profile_names = []
151  self.profile_paths = {}
152  for profile_folder in [info.USER_PROFILES_PATH, info.PROFILES_PATH]:
153  for file in os.listdir(profile_folder):
154  # Load Profile
155  profile_path = os.path.join(profile_folder, file)
156  profile = openshot.Profile(profile_path)
157 
158  # Add description of Profile to list
159  self.profile_names.append(profile.info.description)
160  self.profile_paths[profile.info.description] = profile_path
161 
162  # Sort list
163  self.profile_names.sort()
164 
165  # Loop through sorted profiles
166  box_index = 0
168  for profile_name in self.profile_names:
169 
170  # Add to dropdown
171  self.cboProfile.addItem(profile_name, self.profile_paths[profile_name])
172 
173  # Set default (if it matches the project)
174  if app.project.get(['profile']) == profile_name:
175  self.selected_profile_index = box_index
176 
177  # increment item counter
178  box_index += 1
179 
180 
181  # ********* Simple Project Type **********
182  # load the simple project type dropdown
183  presets = []
184  for file in os.listdir(info.EXPORT_PRESETS_DIR):
185  xmldoc = xml.parse(os.path.join(info.EXPORT_PRESETS_DIR, file))
186  type = xmldoc.getElementsByTagName("type")
187  presets.append(_(type[0].childNodes[0].data))
188 
189  # Exclude duplicates
190  type_index = 0
191  selected_type = 0
192  presets = list(set(presets))
193  for item in sorted(presets):
194  self.cboSimpleProjectType.addItem(item, item)
195  if item == _("All Formats"):
196  selected_type = type_index
197  type_index += 1
198 
199  # Always select 'All Formats' option
200  self.cboSimpleProjectType.setCurrentIndex(selected_type)
201 
202 
203  # Populate all profiles
204  self.populateAllProfiles(app.project.get(['profile']))
205 
206  # Connect framerate signals
207  self.txtFrameRateNum.valueChanged.connect(self.updateFrameRate)
208  self.txtFrameRateDen.valueChanged.connect(self.updateFrameRate)
209  self.txtWidth.valueChanged.connect(self.updateFrameRate)
210  self.txtHeight.valueChanged.connect(self.updateFrameRate)
211  self.txtSampleRate.valueChanged.connect(self.updateFrameRate)
212  self.txtChannels.valueChanged.connect(self.updateFrameRate)
213  self.cboChannelLayout.currentIndexChanged.connect(self.updateFrameRate)
214 
215  # Determine the length of the timeline (in frames)
216  self.updateFrameRate()
217 
218  ##
219  # Update the # of channels to match the channel layout
220  def updateChannels(self):
221  log.info("updateChannels")
222  channels = self.txtChannels.value()
223  channel_layout = self.cboChannelLayout.currentData()
224 
225  if channel_layout == openshot.LAYOUT_MONO:
226  channels = 1
227  elif channel_layout == openshot.LAYOUT_STEREO:
228  channels = 2
229  elif channel_layout == openshot.LAYOUT_SURROUND:
230  channels = 3
231  elif channel_layout == openshot.LAYOUT_5POINT1:
232  channels = 6
233  elif channel_layout == openshot.LAYOUT_7POINT1:
234  channels = 8
235 
236  # Update channels to match layout
237  self.txtChannels.setValue(channels)
238 
239  ##
240  # Callback for changing the frame rate
241  def updateFrameRate(self):
242  log.info('Adjust the framerate of the project')
243 
244  # Adjust the main timeline reader
245  get_app().updates.update(["width"], self.txtWidth.value())
246  get_app().updates.update(["height"], self.txtHeight.value())
247  get_app().updates.update(["fps"], {"num" : self.txtFrameRateNum.value(), "den" : self.txtFrameRateDen.value()})
248  get_app().updates.update(["sample_rate"], self.txtSampleRate.value())
249  get_app().updates.update(["channels"], self.txtChannels.value())
250  get_app().updates.update(["channel_layout"], self.cboChannelLayout.currentData())
251 
252  # Force ApplyMapperToClips to apply these changes
253  get_app().window.timeline_sync.timeline.ApplyMapperToClips()
254 
255  # Determine max frame (based on clips)
256  timeline_length = 0.0
257  fps = get_app().window.timeline_sync.timeline.info.fps.ToFloat()
258  clips = get_app().window.timeline_sync.timeline.Clips()
259  for clip in clips:
260  clip_last_frame = clip.Position() + clip.Duration()
261  if clip_last_frame > timeline_length:
262  # Set max length of timeline
263  timeline_length = clip_last_frame
264 
265  # Convert to int and round
266  self.timeline_length_int = round(timeline_length * fps) + 1
267 
268  # Set the min and max frame numbers for this project
269  self.txtStartFrame.setMaximum(self.timeline_length_int)
270  self.txtEndFrame.setMaximum(self.timeline_length_int)
271  self.txtStartFrame.setValue(1)
272  self.txtEndFrame.setValue(self.timeline_length_int)
273 
274  def cboSimpleProjectType_index_changed(self, widget, index):
275  selected_project = widget.itemData(index)
276 
277  # set the target dropdown based on the selected project type
278  # first clear the combo
279  self.cboSimpleTarget.clear()
280 
281  # get translations
282  app = get_app()
283  _ = app._tr
284 
285  # parse the xml files and get targets that match the project type
286  project_types = []
287  for file in os.listdir(info.EXPORT_PRESETS_DIR):
288  xmldoc = xml.parse(os.path.join(info.EXPORT_PRESETS_DIR, file))
289  type = xmldoc.getElementsByTagName("type")
290 
291  if _(type[0].childNodes[0].data) == selected_project:
292  titles = xmldoc.getElementsByTagName("title")
293  for title in titles:
294  project_types.append(_(title.childNodes[0].data))
295 
296  # Add all targets for selected project type
297  preset_index = 0
298  selected_preset = 0
299  for item in sorted(project_types):
300  self.cboSimpleTarget.addItem(item, item)
301 
302  # Find index of MP4/H.264
303  if item == _("MP4 (h.264)"):
304  selected_preset = preset_index
305 
306  preset_index += 1
307 
308  # Select MP4/H.264 as default
309  self.cboSimpleTarget.setCurrentIndex(selected_preset)
310 
311  def cboProfile_index_changed(self, widget, index):
312  selected_profile_path = widget.itemData(index)
313  log.info(selected_profile_path)
314 
315  # get translations
316  app = get_app()
317  _ = app._tr
318 
319  # Load profile
320  profile = openshot.Profile(selected_profile_path)
321 
322  # Load profile settings into advanced editor
323  self.txtWidth.setValue(profile.info.width)
324  self.txtHeight.setValue(profile.info.height)
325  self.txtFrameRateNum.setValue(profile.info.fps.num)
326  self.txtFrameRateDen.setValue(profile.info.fps.den)
327  self.txtAspectRatioNum.setValue(profile.info.display_ratio.num)
328  self.txtAspectRatioDen.setValue(profile.info.display_ratio.den)
329  self.txtPixelRatioNum.setValue(profile.info.pixel_ratio.num)
330  self.txtPixelRatioDen.setValue(profile.info.pixel_ratio.den)
331 
332  # Load the interlaced options
333  self.cboInterlaced.clear()
334  self.cboInterlaced.addItem(_("Yes"), "Yes")
335  self.cboInterlaced.addItem(_("No"), "No")
336  if profile.info.interlaced_frame:
337  self.cboInterlaced.setCurrentIndex(0)
338  else:
339  self.cboInterlaced.setCurrentIndex(1)
340 
341  def cboSimpleTarget_index_changed(self, widget, index):
342  selected_target = widget.itemData(index)
343  log.info(selected_target)
344 
345  # Clear the following options
346  self.cboSimpleVideoProfile.clear()
347  self.cboSimpleQuality.clear()
348 
349  # get translations
350  app = get_app()
351  _ = app._tr
352 
353 
354  # don't do anything if the combo has been cleared
355  if selected_target:
356  profiles_list = []
357 
358  # parse the xml to return suggested profiles
359  profile_index = 0
360  all_profiles = False
361  for file in os.listdir(info.EXPORT_PRESETS_DIR):
362  xmldoc = xml.parse(os.path.join(info.EXPORT_PRESETS_DIR, file))
363  title = xmldoc.getElementsByTagName("title")
364  if _(title[0].childNodes[0].data) == selected_target:
365  profiles = xmldoc.getElementsByTagName("projectprofile")
366 
367  # get the basic profile
368  all_profiles = False
369  if profiles:
370  # if profiles are defined, show them
371  for profile in profiles:
372  profiles_list.append(_(profile.childNodes[0].data))
373  else:
374  # show all profiles
375  all_profiles = True
376  for profile_name in self.profile_names:
377  profiles_list.append(profile_name)
378 
379  # get the video bit rate(s)
380  videobitrate = xmldoc.getElementsByTagName("videobitrate")
381  for rate in videobitrate:
382  v_l = rate.attributes["low"].value
383  v_m = rate.attributes["med"].value
384  v_h = rate.attributes["high"].value
385  self.vbr = {_("Low"): v_l, _("Med"): v_m, _("High"): v_h}
386 
387  # get the audio bit rates
388  audiobitrate = xmldoc.getElementsByTagName("audiobitrate")
389  for audiorate in audiobitrate:
390  a_l = audiorate.attributes["low"].value
391  a_m = audiorate.attributes["med"].value
392  a_h = audiorate.attributes["high"].value
393  self.abr = {_("Low"): a_l, _("Med"): a_m, _("High"): a_h}
394 
395  # get the remaining values
396  vf = xmldoc.getElementsByTagName("videoformat")
397  self.txtVideoFormat.setText(vf[0].childNodes[0].data)
398  vc = xmldoc.getElementsByTagName("videocodec")
399  self.txtVideoCodec.setText(vc[0].childNodes[0].data)
400  ac = xmldoc.getElementsByTagName("audiocodec")
401  self.txtAudioCodec.setText(ac[0].childNodes[0].data)
402  sr = xmldoc.getElementsByTagName("samplerate")
403  self.txtSampleRate.setValue(int(sr[0].childNodes[0].data))
404  c = xmldoc.getElementsByTagName("audiochannels")
405  self.txtChannels.setValue(int(c[0].childNodes[0].data))
406  c = xmldoc.getElementsByTagName("audiochannellayout")
407 
408  layout_index = 0
409  for layout in self.channel_layout_choices:
410  if layout == int(c[0].childNodes[0].data):
411  self.cboChannelLayout.setCurrentIndex(layout_index)
412  break
413  layout_index += 1
414 
415  # init the profiles combo
416  for item in sorted(profiles_list):
417  self.cboSimpleVideoProfile.addItem(item, self.profile_paths[item])
418 
419  if all_profiles:
420  # select the project's current profile
421  self.cboSimpleVideoProfile.setCurrentIndex(self.selected_profile_index)
422 
423  # set the quality combo
424  # only populate with quality settings that exist
425  if v_l or a_l:
426  self.cboSimpleQuality.addItem(_("Low"), "Low")
427  if v_m or a_m:
428  self.cboSimpleQuality.addItem(_("Med"), "Med")
429  if v_h or a_h:
430  self.cboSimpleQuality.addItem(_("High"), "High")
431 
432  # Default to the highest quality setting
433  self.cboSimpleQuality.setCurrentIndex(self.cboSimpleQuality.count() - 1)
434 
435  def cboSimpleVideoProfile_index_changed(self, widget, index):
436  selected_profile_path = widget.itemData(index)
437  log.info(selected_profile_path)
438 
439  # Populate the advanced profile list
440  self.populateAllProfiles(selected_profile_path)
441 
442  ##
443  # Populate the full list of profiles
444  def populateAllProfiles(self, selected_profile_path):
445  # Look for matching profile in advanced options
446  profile_index = 0
447  for profile_name in self.profile_names:
448  # Check for matching profile
449  if self.profile_paths[profile_name] == selected_profile_path:
450  # Matched!
451  self.cboProfile.setCurrentIndex(profile_index)
452  break
453 
454  # increment index
455  profile_index += 1
456 
457  def cboSimpleQuality_index_changed(self, widget, index):
458  selected_quality = widget.itemData(index)
459  log.info(selected_quality)
460 
461  # get translations
462  app = get_app()
463  _ = app._tr
464 
465  # Set the video and audio bitrates
466  if selected_quality:
467  self.txtVideoBitRate.setText(_(self.vbr[_(selected_quality)]))
468  self.txtAudioBitrate.setText(_(self.abr[_(selected_quality)]))
469 
470  def btnBrowse_clicked(self):
471  log.info("btnBrowse_clicked")
472 
473  # get translations
474  app = get_app()
475  _ = app._tr
476 
477  # update export folder path
478  file_path = QFileDialog.getExistingDirectory(self, _("Choose a Folder..."), self.txtExportFolder.text())
479  if os.path.exists(file_path):
480  self.txtExportFolder.setText(file_path)
481 
482  # update export folder path in project file
483  get_app().updates.update(["export_path"], file_path)
484 
485  def convert_to_bytes(self, BitRateString):
486  bit_rate_bytes = 0
487 
488  # split the string into pieces
489  s = BitRateString.lower().split(" ")
490  measurement = "kb"
491 
492  try:
493  # Get Bit Rate
494  if len(s) >= 2:
495  raw_number_string = s[0]
496  raw_measurement = s[1]
497 
498  # convert string number to float (based on locale settings)
499  raw_number = locale.atof(raw_number_string)
500 
501  if "kb" in raw_measurement:
502  measurement = "kb"
503  bit_rate_bytes = raw_number * 1000.0
504 
505  elif "mb" in raw_measurement:
506  measurement = "mb"
507  bit_rate_bytes = raw_number * 1000.0 * 1000.0
508 
509  except:
510  pass
511 
512  # return the bit rate in bytes
513  return str(int(bit_rate_bytes))
514 
515  ##
516  # Start exporting video, but don't close window
517  def accept(self):
518  # Get settings
519  self.s = settings.get_settings()
520 
521  # Disable controls
522  self.txtFileName.setEnabled(False)
523  self.txtExportFolder.setEnabled(False)
524  self.tabWidget.setEnabled(False)
525  self.export_button.setEnabled(False)
526  self.exporting = True
527 
528  # Test Succeeded
529  # Determine final exported file path
530  file_name_with_ext = "%s.%s" % (self.txtFileName.text().strip(), self.txtVideoFormat.text().strip())
531  export_file_path = os.path.join(self.txtExportFolder.text().strip(), file_name_with_ext)
532  log.info(export_file_path)
533 
534  # Translate object
535  _ = get_app()._tr
536 
537  # Handle exception
538  if os.path.exists(export_file_path):
539  # File already exists! Prompt user
540  ret = QMessageBox.question(self, _("Export Video"), _("%s already exists.\nDo you want to replace it?") % file_name_with_ext,
541  QMessageBox.No | QMessageBox.Yes)
542  if ret == QMessageBox.No:
543  # Stop and don't do anything
544  # Re-enable controls
545  self.txtFileName.setEnabled(True)
546  self.txtExportFolder.setEnabled(True)
547  self.tabWidget.setEnabled(True)
548  self.export_button.setEnabled(True)
549  self.exporting = False
550  return
551 
552  # Create FFmpegWriter
553  try:
554  w = openshot.FFmpegWriter(export_file_path)
555 
556  # Set video options
557  w.SetVideoOptions(True,
558  self.txtVideoCodec.text(),
559  openshot.Fraction(self.txtFrameRateNum.value(),
560  self.txtFrameRateDen.value()),
561  self.txtWidth.value(),
562  self.txtHeight.value(),
563  openshot.Fraction(self.txtPixelRatioNum.value(),
564  self.txtPixelRatioDen.value()),
565  False,
566  False,
567  int(self.convert_to_bytes(self.txtVideoBitRate.text())))
568 
569  # Set audio options
570  w.SetAudioOptions(True,
571  self.txtAudioCodec.text(),
572  self.txtSampleRate.value(),
573  self.txtChannels.value(),
574  self.cboChannelLayout.currentData(),
575  int(self.convert_to_bytes(self.txtAudioBitrate.text())))
576 
577  # Open the writer
578  w.Open()
579 
580  # Init progress bar
581  self.progressExportVideo.setMinimum(self.txtStartFrame.value())
582  self.progressExportVideo.setMaximum(self.txtEndFrame.value())
583 
584  # Write each frame in the selected range
585  for frame in range(self.txtStartFrame.value(), self.txtEndFrame.value() + 1):
586  # Update progress bar
587  self.progressExportVideo.setValue(frame)
588  # Process events (to show the progress bar moving)
589  QCoreApplication.processEvents()
590 
591  # Write the frame object to the video
592  w.WriteFrame(get_app().window.timeline_sync.timeline.GetFrame(frame))
593 
594  # Check if we need to bail out
595  if not self.exporting:
596  break
597 
598  # Close writer
599  w.Close()
600 
601 
602  except Exception as e:
603  # TODO: Find a better way to catch the error. This is the only way I have found that
604  # does not throw an error
605  error_type_str = str(e)
606  log.info("Error type string: %s" % error_type_str)
607 
608  if "InvalidChannels" in error_type_str:
609  log.info("Error setting invalid # of channels (%s)" % (self.txtChannels.value()))
610  track_metric_error("invalid-channels-%s-%s-%s-%s" % (self.txtVideoFormat.text(), self.txtVideoCodec.text(), self.txtAudioCodec.text(), self.txtChannels.value()))
611 
612  elif "InvalidSampleRate" in error_type_str:
613  log.info("Error setting invalid sample rate (%s)" % (self.txtSampleRate.value()))
614  track_metric_error("invalid-sample-rate-%s-%s-%s-%s" % (self.txtVideoFormat.text(), self.txtVideoCodec.text(), self.txtAudioCodec.text(), self.txtSampleRate.value()))
615 
616  elif "InvalidFormat" in error_type_str:
617  log.info("Error setting invalid format (%s)" % (self.txtVideoFormat.text()))
618  track_metric_error("invalid-format-%s" % (self.txtVideoFormat.text()))
619 
620  elif "InvalidCodec" in error_type_str:
621  log.info("Error setting invalid codec (%s/%s/%s)" % (self.txtVideoFormat.text(), self.txtVideoCodec.text(), self.txtAudioCodec.text()))
622  track_metric_error("invalid-codec-%s-%s-%s" % (self.txtVideoFormat.text(), self.txtVideoCodec.text(), self.txtAudioCodec.text()))
623 
624  elif "ErrorEncodingVideo" in error_type_str:
625  log.info("Error encoding video frame (%s/%s/%s)" % (self.txtVideoFormat.text(), self.txtVideoCodec.text(), self.txtAudioCodec.text()))
626  track_metric_error("video-encode-%s-%s-%s" % (self.txtVideoFormat.text(), self.txtVideoCodec.text(), self.txtAudioCodec.text()))
627 
628  # Show friendly error
629  friendly_error = error_type_str.split("> ")[0].replace("<", "")
630 
631  # Prompt error message
632  msg = QMessageBox()
633  _ = get_app()._tr
634  msg.setWindowTitle(_("Export Error"))
635  msg.setText(_("Sorry, there was an error exporting your video: \n%s") % friendly_error)
636  msg.exec_()
637 
638  # Accept dialog
639  super(Export, self).accept()
640 
641  # Restore timeline settings
642  self.restoreTimeline()
643 
644  log.info("End Accept")
645 
646  ##
647  # Restore timeline setting to original settings
648  def restoreTimeline(self):
649  log.info("restoreTimeline")
650 
651  # Adjust the main timeline reader
652  get_app().updates.update(["width"], self.original_width)
653  get_app().updates.update(["height"], self.original_height)
654  get_app().updates.update(["fps"], self.original_fps)
655  get_app().updates.update(["sample_rate"], self.original_sample_rate)
656  get_app().updates.update(["channels"], self.original_channels)
657  get_app().updates.update(["channel_layout"], self.original_channel_layout)
658 
659  # Force ApplyMapperToClips to apply these changes
660  get_app().window.timeline_sync.timeline.ApplyMapperToClips()
661 
662  def reject(self):
663 
664  log.info("Start Reject")
665  self.exporting = False
666 
667  # Cancel dialog
668  super(Export, self).reject()
669 
670  # Restore timeline settings
671  self.restoreTimeline()
672 
673  log.info("End Reject")
def cboSimpleQuality_index_changed(self, widget, index)
Definition: export.py:457
def reject(self)
Definition: export.py:662
def __init__(self)
Definition: export.py:51
def restoreTimeline(self)
Restore timeline setting to original settings.
Definition: export.py:648
def updateChannels(self)
Update the # of channels to match the channel layout.
Definition: export.py:220
Export Dialog.
Definition: export.py:46
def get_app()
Returns the current QApplication instance of OpenShot.
Definition: app.py:54
def cboSimpleProjectType_index_changed(self, widget, index)
Definition: export.py:274
def btnBrowse_clicked(self)
Definition: export.py:470
channel_layout_choices
Definition: export.py:124
def updateFrameRate(self)
Callback for changing the frame rate.
Definition: export.py:241
def cboProfile_index_changed(self, widget, index)
Definition: export.py:311
original_sample_rate
Definition: export.py:86
def cboSimpleTarget_index_changed(self, widget, index)
Definition: export.py:341
def populateAllProfiles(self, selected_profile_path)
Populate the full list of profiles.
Definition: export.py:444
original_channel_layout
Definition: export.py:88
selected_profile_index
Definition: export.py:167
timeline_length_int
Definition: export.py:266
def get_settings()
Get the current QApplication&#39;s settings instance.
Definition: settings.py:43
def init_ui(window)
Initialize all child widgets and action of a window or dialog.
Definition: ui_util.py:200
original_channels
Definition: export.py:87
def track_metric_screen(screen_name)
Track a GUI screen being shown.
Definition: metrics.py:94
def track_metric_error(error_name, is_fatal=False)
Track an error has occurred.
Definition: metrics.py:117
def cboSimpleVideoProfile_index_changed(self, widget, index)
Definition: export.py:435
def load_ui(window, path)
Load a Qt *.ui file, and also load an XML parsed version.
Definition: ui_util.py:65
def convert_to_bytes(self, BitRateString)
Definition: export.py:485
def accept(self)
Start exporting video, but don&#39;t close window.
Definition: export.py:517