31 from random
import shuffle, randint, uniform
36 from classes
import settings
37 from classes
import info, ui_util
38 from classes.logger
import log
39 from classes.query
import Track, Clip, Transition
40 from classes.app
import get_app
42 from windows.views.add_to_timeline_treeview
import TimelineTreeView
49 import simplejson
as json
56 ui_path = os.path.join(info.PATH,
'windows',
'ui',
'add-to-timeline.ui')
61 log.info(
"btnMoveUpClicked")
64 files = self.treeFiles.timeline_model.files
65 selected_index = self.treeFiles.selected.row()
72 new_index = max(selected_index - 1, 0)
76 files.insert(new_index, files.pop(selected_index))
79 self.treeFiles.refresh_view()
82 idx = self.treeFiles.timeline_model.model.index(new_index, 0)
83 self.treeFiles.setCurrentIndex(idx)
88 log.info(
"btnMoveDownClicked")
91 files = self.treeFiles.timeline_model.files
92 selected_index = self.treeFiles.selected.row()
99 new_index = min(selected_index + 1, len(files) - 1)
103 files.insert(new_index, files.pop(selected_index))
106 self.treeFiles.refresh_view()
109 idx = self.treeFiles.timeline_model.model.index(new_index, 0)
110 self.treeFiles.setCurrentIndex(idx)
115 log.info(
"btnShuffleClicked")
118 files = shuffle(self.treeFiles.timeline_model.files)
121 self.treeFiles.refresh_view()
126 log.info(
"btnRemoveClicked")
129 files = self.treeFiles.timeline_model.files
130 selected_index = self.treeFiles.selected.row()
137 files.pop(selected_index)
140 self.treeFiles.refresh_view()
143 new_index = max(len(files) - 1, 0)
146 idx = self.treeFiles.timeline_model.model.index(new_index, 0)
147 self.treeFiles.setCurrentIndex(idx)
158 start_position = self.txtStartTime.value()
159 track_num = self.cmbTrack.currentData()
160 fade_value = self.cmbFade.currentData()
161 fade_length = self.txtFadeLength.value()
162 transition_path = self.cmbTransition.currentData()
163 transition_length = self.txtTransitionLength.value()
164 image_length = self.txtImageLength.value()
165 zoom_value = self.cmbZoom.currentData()
168 position = start_position
170 random_transition =
False 171 if transition_path ==
"random":
172 random_transition =
True 175 fps =
get_app().project.get([
"fps"])
176 fps_float = float(fps[
"num"]) / float(fps[
"den"])
179 for file
in self.treeFiles.timeline_model.files:
184 if (file.data[
"media_type"] ==
"video" or file.data[
"media_type"] ==
"image"):
186 thumb_path = os.path.join(info.THUMBNAIL_PATH,
"%s.png" % file.data[
"id"])
189 thumb_path = os.path.join(info.PATH,
"images",
"AudioThumbnail.png")
192 path, filename = os.path.split(file.data[
"path"])
195 file_path = file.absolute_path()
198 c = openshot.Clip(file_path)
201 new_clip = json.loads(c.Json())
202 new_clip[
"position"] = position
203 new_clip[
"layer"] = track_num
204 new_clip[
"file_id"] = file.id
205 new_clip[
"title"] = filename
206 new_clip[
"image"] = thumb_path
209 file_properties_fps = float(file.data[
"fps"][
"num"]) / float(file.data[
"fps"][
"den"])
210 file_fps = float(new_clip[
"reader"][
"fps"][
"num"]) / float(new_clip[
"reader"][
"fps"][
"den"])
211 fps_diff = file_fps / file_properties_fps
212 new_clip[
"reader"][
"fps"][
"num"] = file.data[
"fps"][
"num"]
213 new_clip[
"reader"][
"fps"][
"den"] = file.data[
"fps"][
"den"]
215 new_clip[
"reader"][
"duration"] *= fps_diff
216 new_clip[
"end"] *= fps_diff
217 new_clip[
"duration"] *= fps_diff
221 end_time = new_clip[
"reader"][
"duration"]
223 if 'start' in file.data.keys():
224 start_time = file.data[
'start']
225 new_clip[
"start"] = start_time
226 if 'end' in file.data.keys():
227 end_time = file.data[
'end']
228 new_clip[
"end"] = end_time
231 new_clip[
"duration"] = new_clip[
"reader"][
"duration"]
232 if file.data[
"media_type"] ==
"image":
233 end_time = image_length
234 new_clip[
"end"] = end_time
237 if not transition_path:
238 if fade_value !=
None:
240 position = max(start_position, new_clip[
"position"] - fade_length)
241 new_clip[
"position"] = position
243 if fade_value ==
'Fade In' or fade_value ==
'Fade In & Out':
244 start = openshot.Point((start_time * fps_float) + 1, 0.0, openshot.BEZIER)
245 start_object = json.loads(start.Json())
246 end = openshot.Point(min((start_time + fade_length) * fps_float, end_time * fps_float), 1.0, openshot.BEZIER)
247 end_object = json.loads(end.Json())
248 new_clip[
'alpha'][
"Points"].append(start_object)
249 new_clip[
'alpha'][
"Points"].append(end_object)
251 if fade_value ==
'Fade Out' or fade_value ==
'Fade In & Out':
252 start = openshot.Point(max((end_time * fps_float) - (fade_length * fps_float), start_time * fps_float), 1.0, openshot.BEZIER)
253 start_object = json.loads(start.Json())
254 end = openshot.Point(end_time * fps_float, 0.0, openshot.BEZIER)
255 end_object = json.loads(end.Json())
256 new_clip[
'alpha'][
"Points"].append(start_object)
257 new_clip[
'alpha'][
"Points"].append(end_object)
260 if zoom_value !=
None:
262 if zoom_value ==
"Random":
263 animate_start_x = uniform(-0.5, 0.5)
264 animate_end_x = uniform(-0.15, 0.15)
265 animate_start_y = uniform(-0.5, 0.5)
266 animate_end_y = uniform(-0.15, 0.15)
269 start_scale = uniform(0.5, 1.5)
270 end_scale = uniform(0.85, 1.15)
272 elif zoom_value ==
"Zoom In":
273 animate_start_x = 0.0
275 animate_start_y = 0.0
282 elif zoom_value ==
"Zoom Out":
283 animate_start_x = 0.0
285 animate_start_y = 0.0
293 start = openshot.Point((start_time * fps_float) + 1, start_scale, openshot.BEZIER)
294 start_object = json.loads(start.Json())
295 end = openshot.Point(end_time * fps_float, end_scale, openshot.BEZIER)
296 end_object = json.loads(end.Json())
297 new_clip[
"gravity"] = openshot.GRAVITY_CENTER
298 new_clip[
"scale_x"][
"Points"].append(start_object)
299 new_clip[
"scale_x"][
"Points"].append(end_object)
300 new_clip[
"scale_y"][
"Points"].append(start_object)
301 new_clip[
"scale_y"][
"Points"].append(end_object)
304 start_x = openshot.Point((start_time * fps_float) + 1, animate_start_x, openshot.BEZIER)
305 start_x_object = json.loads(start_x.Json())
306 end_x = openshot.Point(end_time * fps_float, animate_end_x, openshot.BEZIER)
307 end_x_object = json.loads(end_x.Json())
308 start_y = openshot.Point((start_time * fps_float) + 1, animate_start_y, openshot.BEZIER)
309 start_y_object = json.loads(start_y.Json())
310 end_y = openshot.Point(end_time * fps_float, animate_end_y, openshot.BEZIER)
311 end_y_object = json.loads(end_y.Json())
312 new_clip[
"gravity"] = openshot.GRAVITY_CENTER
313 new_clip[
"location_x"][
"Points"].append(start_x_object)
314 new_clip[
"location_x"][
"Points"].append(end_x_object)
315 new_clip[
"location_y"][
"Points"].append(start_y_object)
316 new_clip[
"location_y"][
"Points"].append(end_y_object)
321 if random_transition:
326 transition_reader = openshot.QtImageReader(transition_path)
328 brightness = openshot.Keyframe()
329 brightness.AddPoint(1, 1.0, openshot.BEZIER)
330 brightness.AddPoint(min(transition_length, end_time - start_time) * fps_float, -1.0, openshot.BEZIER)
331 contrast = openshot.Keyframe(3.0)
336 "title":
"Transition",
339 "end": min(transition_length, end_time - start_time),
340 "brightness": json.loads(brightness.Json()),
341 "contrast": json.loads(contrast.Json()),
342 "reader": json.loads(transition_reader.Json()),
343 "replace_image":
False 347 position = max(start_position, position - transition_length)
348 transitions_data[
"position"] = position
349 new_clip[
"position"] = position
353 tran.data = transitions_data
362 position += (end_time - start_time)
366 super(AddToTimeline, self).
accept()
376 fade_value = self.cmbFade.currentData()
377 fade_length = self.txtFadeLength.value()
378 transition_path = self.cmbTransition.currentData()
379 transition_length = self.txtTransitionLength.value()
382 for file
in self.treeFiles.timeline_model.files:
384 duration = file.data[
"duration"]
385 if file.data[
"media_type"] ==
"image":
386 duration = self.txtImageLength.value()
390 if not transition_path:
392 if fade_value !=
None:
394 duration -= fade_length
397 duration -= transition_length
403 fps =
get_app().project.get([
"fps"])
406 total_parts = self.
secondsToTime(total, fps[
"num"], fps[
"den"])
407 timestamp =
"%s:%s:%s:%s" % (total_parts[
"hour"], total_parts[
"min"], total_parts[
"sec"], total_parts[
"frame"])
408 self.lblTotalLengthValue.setText(timestamp)
411 format_mask =
'%%0%sd' % pad_length
412 return format_mask % value
416 milliseconds = secs * 1000
417 sec = math.floor(milliseconds/1000)
418 milli = milliseconds % 1000
419 min = math.floor(sec/60)
421 hour = math.floor(min/60)
423 day = math.floor(hour/24)
425 week = math.floor(day/7)
428 frame = round((milli / 1000.0) * (fps_num / fps_den)) + 1
429 return {
"week":self.
padNumber(week,2),
"day":self.
padNumber(day,2),
"hour":self.
padNumber(hour,2),
"min":self.
padNumber(min,2),
"sec":self.
padNumber(sec,2),
"milli":self.
padNumber(milli,2),
"frame":self.
padNumber(frame,2) };
437 super(AddToTimeline, self).
reject()
441 QDialog.__init__(self)
461 self.vboxTreeParent.insertWidget(0, self.
treeFiles)
464 self.treeFiles.timeline_model.update_model(files)
467 self.treeFiles.refresh_view()
470 self.txtStartTime.setValue(position)
473 self.txtImageLength.setValue(self.settings.get(
"default-image-length"))
474 self.txtImageLength.valueChanged.connect(self.
updateTotal)
475 self.cmbTransition.currentIndexChanged.connect(self.
updateTotal)
476 self.cmbFade.currentIndexChanged.connect(self.
updateTotal)
477 self.txtFadeLength.valueChanged.connect(self.
updateTotal)
478 self.txtTransitionLength.valueChanged.connect(self.
updateTotal)
481 tracks = Track.filter()
482 for track
in reversed(tracks):
484 self.cmbTrack.addItem(_(
'Track %s' % track.data[
'number']), track.data[
'number'])
487 self.cmbFade.addItem(_(
'None'),
None)
488 self.cmbFade.addItem(_(
'Fade In'),
'Fade In')
489 self.cmbFade.addItem(_(
'Fade Out'),
'Fade Out')
490 self.cmbFade.addItem(_(
'Fade In & Out'),
'Fade In & Out')
493 self.cmbZoom.addItem(_(
'None'),
None)
494 self.cmbZoom.addItem(_(
'Random'),
'Random')
495 self.cmbZoom.addItem(_(
'Zoom In'),
'Zoom In')
496 self.cmbZoom.addItem(_(
'Zoom Out'),
'Zoom Out')
499 transitions_dir = os.path.join(info.PATH,
"transitions")
500 common_dir = os.path.join(transitions_dir,
"common")
501 extra_dir = os.path.join(transitions_dir,
"extra")
502 transition_groups = [{
"type":
"common",
"dir": common_dir,
"files": os.listdir(common_dir)},
503 {
"type":
"extra",
"dir": extra_dir,
"files": os.listdir(extra_dir)}]
505 self.cmbTransition.addItem(_(
'None'),
None)
506 self.cmbTransition.addItem(_(
'Random'),
'random')
508 for group
in transition_groups:
511 files = group[
"files"]
513 for filename
in sorted(files):
514 path = os.path.join(dir, filename)
515 (fileBaseName, fileExtension) = os.path.splitext(filename)
518 if filename[0] ==
"." or "thumbs.db" in filename.lower():
523 name_parts = fileBaseName.split(
"_")
524 if name_parts[-1].isdigit():
525 suffix_number = name_parts[-1]
528 trans_name = fileBaseName.replace(
"_",
" ").capitalize()
532 trans_name = trans_name.replace(suffix_number,
"%s")
533 trans_name = _(trans_name) % suffix_number
535 trans_name = _(trans_name)
538 thumb_path = os.path.join(info.IMAGES_PATH,
"cache",
"{}.png".
format(fileBaseName))
541 if not os.path.exists(thumb_path):
543 thumb_path = os.path.join(info.CACHE_PATH,
"{}.png".
format(fileBaseName))
546 self.transitions.append(path)
547 self.cmbTransition.addItem(QIcon(thumb_path), _(trans_name), path)
554 self.btnBox.accepted.connect(self.
accept)
555 self.btnBox.rejected.connect(self.
reject)
def btnShuffleClicked(self, event)
Callback for move up button click.
def get_app()
Returns the current QApplication instance of OpenShot.
def btnRemoveClicked(self, event)
Callback for move up button click.
def accept(self)
Ok button clicked.
def btnMoveDownClicked(self, event)
Callback for move up button click.
def padNumber(self, value, pad_length)
def __init__(self, files=None, position=0.0)
def secondsToTime(self, secs, fps_num, fps_den)
def btnMoveUpClicked(self, event)
Callback for move up button click.
def updateTotal(self)
Calculate the total length of what's about to be added to the timeline.
def get_settings()
Get the current QApplication's settings instance.
def ImageLengthChanged(self, value)
Handle callback for image length being changed.
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 load_ui(window, path)
Load a Qt *.ui file, and also load an XML parsed version.
def reject(self)
Cancel button clicked.