31 import xml.dom.minidom
as xml
38 from classes
import info, ui_util, settings
39 from classes.app
import get_app
40 from classes.logger
import log
49 ui_path = os.path.join(info.PATH,
'windows',
'ui',
'export.ui')
54 QDialog.__init__(self)
77 self.buttonBox.addButton(self.
export_button, QDialogButtonBox.AcceptRole)
78 self.buttonBox.addButton(QPushButton(_(
'Cancel')), QDialogButtonBox.RejectRole)
84 fps =
get_app().window.timeline_sync.timeline.info.fps
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)
95 export_path =
get_app().project.get([
"export_path"])
96 if os.path.exists(export_path):
98 self.txtExportFolder.setText(export_path)
101 self.txtExportFolder.setText(recommended_path)
104 if not get_app().project.current_filepath:
106 self.txtFileName.setText(_(
"Untitled Project"))
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())
115 self.txtImageFormat.setText(
"%05.png")
118 export_options = [_(
"Video & Audio"), _(
"Image Sequence")]
119 for option
in export_options:
121 self.cboExportTo.addItem(option)
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)"))]:
131 self.channel_layout_choices.append(layout[0])
132 self.cboChannelLayout.addItem(layout[1], layout[0])
136 self.cboSimpleProjectType.currentIndexChanged.connect(
139 self.cboSimpleTarget.currentIndexChanged.connect(
141 self.cboSimpleVideoProfile.currentIndexChanged.connect(
143 self.cboSimpleQuality.currentIndexChanged.connect(
145 self.cboChannelLayout.currentIndexChanged.connect(self.
updateChannels)
152 for profile_folder
in [info.USER_PROFILES_PATH, info.PROFILES_PATH]:
153 for file
in os.listdir(profile_folder):
155 profile_path = os.path.join(profile_folder, file)
156 profile = openshot.Profile(profile_path)
159 self.profile_names.append(profile.info.description)
163 self.profile_names.sort()
171 self.cboProfile.addItem(profile_name, self.
profile_paths[profile_name])
174 if app.project.get([
'profile']) == profile_name:
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))
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
200 self.cboSimpleProjectType.setCurrentIndex(selected_type)
213 self.cboChannelLayout.currentIndexChanged.connect(self.
updateFrameRate)
221 log.info(
"updateChannels")
222 channels = self.txtChannels.value()
223 channel_layout = self.cboChannelLayout.currentData()
225 if channel_layout == openshot.LAYOUT_MONO:
227 elif channel_layout == openshot.LAYOUT_STEREO:
229 elif channel_layout == openshot.LAYOUT_SURROUND:
231 elif channel_layout == openshot.LAYOUT_5POINT1:
233 elif channel_layout == openshot.LAYOUT_7POINT1:
237 self.txtChannels.setValue(channels)
242 log.info(
'Adjust the framerate of the project')
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())
253 get_app().window.timeline_sync.timeline.ApplyMapperToClips()
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()
260 clip_last_frame = clip.Position() + clip.Duration()
261 if clip_last_frame > timeline_length:
263 timeline_length = clip_last_frame
271 self.txtStartFrame.setValue(1)
275 selected_project = widget.itemData(index)
279 self.cboSimpleTarget.clear()
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")
291 if _(type[0].childNodes[0].data) == selected_project:
292 titles = xmldoc.getElementsByTagName(
"title")
294 project_types.append(_(title.childNodes[0].data))
299 for item
in sorted(project_types):
300 self.cboSimpleTarget.addItem(item, item)
303 if item == _(
"MP4 (h.264)"):
304 selected_preset = preset_index
309 self.cboSimpleTarget.setCurrentIndex(selected_preset)
312 selected_profile_path = widget.itemData(index)
313 log.info(selected_profile_path)
320 profile = openshot.Profile(selected_profile_path)
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)
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)
339 self.cboInterlaced.setCurrentIndex(1)
342 selected_target = widget.itemData(index)
343 log.info(selected_target)
346 self.cboSimpleVideoProfile.clear()
347 self.cboSimpleQuality.clear()
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")
371 for profile
in profiles:
372 profiles_list.append(_(profile.childNodes[0].data))
377 profiles_list.append(profile_name)
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}
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}
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")
410 if layout == int(c[0].childNodes[0].data):
411 self.cboChannelLayout.setCurrentIndex(layout_index)
416 for item
in sorted(profiles_list):
417 self.cboSimpleVideoProfile.addItem(item, self.
profile_paths[item])
426 self.cboSimpleQuality.addItem(_(
"Low"),
"Low")
428 self.cboSimpleQuality.addItem(_(
"Med"),
"Med")
430 self.cboSimpleQuality.addItem(_(
"High"),
"High")
433 self.cboSimpleQuality.setCurrentIndex(self.cboSimpleQuality.count() - 1)
436 selected_profile_path = widget.itemData(index)
437 log.info(selected_profile_path)
449 if self.
profile_paths[profile_name] == selected_profile_path:
451 self.cboProfile.setCurrentIndex(profile_index)
458 selected_quality = widget.itemData(index)
459 log.info(selected_quality)
467 self.txtVideoBitRate.setText(_(self.
vbr[_(selected_quality)]))
468 self.txtAudioBitrate.setText(_(self.
abr[_(selected_quality)]))
471 log.info(
"btnBrowse_clicked")
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)
483 get_app().updates.update([
"export_path"], file_path)
489 s = BitRateString.lower().split(
" ")
495 raw_number_string = s[0]
496 raw_measurement = s[1]
499 raw_number = locale.atof(raw_number_string)
501 if "kb" in raw_measurement:
503 bit_rate_bytes = raw_number * 1000.0
505 elif "mb" in raw_measurement:
507 bit_rate_bytes = raw_number * 1000.0 * 1000.0
513 return str(int(bit_rate_bytes))
522 self.txtFileName.setEnabled(
False)
523 self.txtExportFolder.setEnabled(
False)
524 self.tabWidget.setEnabled(
False)
525 self.export_button.setEnabled(
False)
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)
538 if os.path.exists(export_file_path):
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:
545 self.txtFileName.setEnabled(
True)
546 self.txtExportFolder.setEnabled(
True)
547 self.tabWidget.setEnabled(
True)
548 self.export_button.setEnabled(
True)
554 w = openshot.FFmpegWriter(export_file_path)
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()),
570 w.SetAudioOptions(
True,
571 self.txtAudioCodec.text(),
572 self.txtSampleRate.value(),
573 self.txtChannels.value(),
574 self.cboChannelLayout.currentData(),
581 self.progressExportVideo.setMinimum(self.txtStartFrame.value())
582 self.progressExportVideo.setMaximum(self.txtEndFrame.value())
585 for frame
in range(self.txtStartFrame.value(), self.txtEndFrame.value() + 1):
587 self.progressExportVideo.setValue(frame)
589 QCoreApplication.processEvents()
592 w.WriteFrame(
get_app().window.timeline_sync.timeline.GetFrame(frame))
602 except Exception
as e:
605 error_type_str = str(e)
606 log.info(
"Error type string: %s" % error_type_str)
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()))
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()))
616 elif "InvalidFormat" in error_type_str:
617 log.info(
"Error setting invalid format (%s)" % (self.txtVideoFormat.text()))
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()))
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()))
629 friendly_error = error_type_str.split(
"> ")[0].replace(
"<",
"")
634 msg.setWindowTitle(_(
"Export Error"))
635 msg.setText(_(
"Sorry, there was an error exporting your video: \n%s") % friendly_error)
639 super(Export, self).
accept()
644 log.info(
"End Accept")
649 log.info(
"restoreTimeline")
660 get_app().window.timeline_sync.timeline.ApplyMapperToClips()
664 log.info(
"Start Reject")
668 super(Export, self).
reject()
673 log.info(
"End Reject")
def cboSimpleQuality_index_changed(self, widget, index)
def restoreTimeline(self)
Restore timeline setting to original settings.
def updateChannels(self)
Update the # of channels to match the channel layout.
def get_app()
Returns the current QApplication instance of OpenShot.
def cboSimpleProjectType_index_changed(self, widget, index)
def btnBrowse_clicked(self)
def updateFrameRate(self)
Callback for changing the frame rate.
def cboProfile_index_changed(self, widget, index)
def cboSimpleTarget_index_changed(self, widget, index)
def populateAllProfiles(self, selected_profile_path)
Populate the full list of profiles.
def get_settings()
Get the current QApplication's settings instance.
def init_ui(window)
Initialize all child widgets and action of a window or dialog.
def track_metric_screen(screen_name)
Track a GUI screen being shown.
def track_metric_error(error_name, is_fatal=False)
Track an error has occurred.
def cboSimpleVideoProfile_index_changed(self, widget, index)
def load_ui(window, path)
Load a Qt *.ui file, and also load an XML parsed version.
def convert_to_bytes(self, BitRateString)
def accept(self)
Start exporting video, but don't close window.