32 from copy
import deepcopy
33 from functools
import partial
34 from random
import uniform
37 from PyQt5.QtCore import QFileInfo, pyqtSlot, QUrl, Qt, QCoreApplication, QTimer
39 from PyQt5.QtWebKitWidgets
import QWebView
42 from classes
import info, updates
43 from classes
import settings
44 from classes.app
import get_app
45 from classes.logger
import log
46 from classes.query
import File, Clip, Transition, Track
47 from classes.waveform
import get_audio_data
52 import simplejson
as json
55 JS_SCOPE_SELECTOR =
"$('body').scope()" 60 MENU_FADE_OUT_FAST = 3
61 MENU_FADE_OUT_SLOW = 4
62 MENU_FADE_IN_OUT_FAST = 5
63 MENU_FADE_IN_OUT_SLOW = 6
66 MENU_ROTATE_90_RIGHT = 1
67 MENU_ROTATE_90_LEFT = 2
68 MENU_ROTATE_180_FLIP = 3
71 MENU_LAYOUT_CENTER = 1
72 MENU_LAYOUT_TOP_LEFT = 2
73 MENU_LAYOUT_TOP_RIGHT = 3
74 MENU_LAYOUT_BOTTOM_LEFT = 4
75 MENU_LAYOUT_BOTTOM_RIGHT = 5
76 MENU_LAYOUT_ALL_WITH_ASPECT = 6
77 MENU_LAYOUT_ALL_WITHOUT_ASPECT = 7
83 MENU_ANIMATE_IN_50_100 = 1
84 MENU_ANIMATE_IN_75_100 = 2
85 MENU_ANIMATE_IN_100_150 = 3
86 MENU_ANIMATE_OUT_100_75 = 4
87 MENU_ANIMATE_OUT_100_50 = 5
88 MENU_ANIMATE_OUT_150_100 = 6
89 MENU_ANIMATE_CENTER_TOP = 7
90 MENU_ANIMATE_CENTER_LEFT = 8
91 MENU_ANIMATE_CENTER_RIGHT = 9
92 MENU_ANIMATE_CENTER_BOTTOM = 10
93 MENU_ANIMATE_TOP_CENTER = 11
94 MENU_ANIMATE_LEFT_CENTER = 12
95 MENU_ANIMATE_RIGHT_CENTER = 13
96 MENU_ANIMATE_BOTTOM_CENTER = 14
97 MENU_ANIMATE_TOP_BOTTOM = 15
98 MENU_ANIMATE_LEFT_RIGHT = 16
99 MENU_ANIMATE_RIGHT_LEFT = 17
100 MENU_ANIMATE_BOTTOM_TOP = 18
101 MENU_ANIMATE_RANDOM = 19
104 MENU_VOLUME_FADE_IN_FAST = 2
105 MENU_VOLUME_FADE_IN_SLOW = 3
106 MENU_VOLUME_FADE_OUT_FAST = 4
107 MENU_VOLUME_FADE_OUT_SLOW = 5
108 MENU_VOLUME_FADE_IN_OUT_FAST = 6
109 MENU_VOLUME_FADE_IN_OUT_SLOW = 7
110 MENU_VOLUME_LEVEL_100 = 100
111 MENU_VOLUME_LEVEL_90 = 90
112 MENU_VOLUME_LEVEL_80 = 80
113 MENU_VOLUME_LEVEL_70 = 70
114 MENU_VOLUME_LEVEL_60 = 60
115 MENU_VOLUME_LEVEL_50 = 50
116 MENU_VOLUME_LEVEL_40 = 40
117 MENU_VOLUME_LEVEL_30 = 30
118 MENU_VOLUME_LEVEL_20 = 20
119 MENU_VOLUME_LEVEL_10 = 10
120 MENU_VOLUME_LEVEL_0 = 0
123 MENU_TIME_FORWARD = 1
124 MENU_TIME_BACKWARD = 2
128 MENU_COPY_KEYFRAMES_ALL = 1
129 MENU_COPY_KEYFRAMES_ALPHA = 2
130 MENU_COPY_KEYFRAMES_SCALE = 3
131 MENU_COPY_KEYFRAMES_ROTATE = 4
132 MENU_COPY_KEYFRAMES_LOCATION = 5
133 MENU_COPY_KEYFRAMES_TIME = 6
134 MENU_COPY_KEYFRAMES_VOLUME = 7
135 MENU_COPY_EFFECTS = 8
138 MENU_COPY_TRANSITION = 10
139 MENU_COPY_KEYFRAMES_BRIGHTNESS = 11
140 MENU_COPY_KEYFRAMES_CONTRAST = 12
142 MENU_SLICE_KEEP_BOTH = 0
143 MENU_SLICE_KEEP_LEFT = 1
144 MENU_SLICE_KEEP_RIGHT = 2
146 MENU_SPLIT_AUDIO_SINGLE = 0
147 MENU_SPLIT_AUDIO_MULTIPLE = 1
155 html_path = os.path.join(info.PATH,
'timeline',
'index.html')
158 return self.page().mainFrame().evaluateJavaScript(code)
163 if action.type ==
"load":
166 self.
eval_js(JS_SCOPE_SELECTOR +
".SetTrackLabel('" + _(
"Track %s") +
"');")
169 code = JS_SCOPE_SELECTOR +
".LoadJson(" + action.json() +
");" 172 code = JS_SCOPE_SELECTOR +
".ApplyJsonDiff([" + action.json() +
"]);" 176 if action.type ==
"load":
188 if not isinstance(clip_json, dict):
189 clip_data = json.loads(clip_json)
191 clip_data = clip_json
197 existing_clip = Clip.get(id=clip_data[
"id"])
198 if not existing_clip:
200 existing_clip = Clip()
203 start_changed =
False 204 if existing_clip.data
and existing_clip.data[
"start"] != clip_data[
"start"]
and clip_data[
"reader"][
"has_video"]
and not clip_data[
"reader"][
"has_single_image"]:
209 existing_clip.data = clip_data
213 existing_clip.data = {}
214 existing_clip.data[
"id"] = clip_data[
"id"]
215 existing_clip.data[
"layer"] = clip_data[
"layer"]
216 existing_clip.data[
"position"] = clip_data[
"position"]
217 existing_clip.data[
"image"] = clip_data[
"image"]
218 existing_clip.data[
"start"] = clip_data[
"start"]
219 existing_clip.data[
"end"] = clip_data[
"end"]
222 if ignore_reader
and "reader" in existing_clip.data:
223 existing_clip.data.pop(
"reader")
229 get_app().window.refreshFrameSignal.emit()
237 fps = clip_data[
"reader"][
"fps"]
238 fps_float = float(fps[
"num"]) / float(fps[
"den"])
241 start_frame = round(float(clip_data[
"start"]) * fps_float) + 1
244 thumb_path = os.path.join(info.THUMBNAIL_PATH,
"{}-{}.png".
format(clip_data[
"id"], start_frame))
245 log.info(
'Updating thumbnail image: %s' % thumb_path)
248 if not os.path.exists(thumb_path):
251 file = File.get(id=clip_data[
"file_id"])
254 file_path = file.absolute_path()
257 clip = openshot.Clip(file_path)
258 reader = clip.Reader()
265 if file.data[
"media_type"] ==
"video":
266 overlay_path = os.path.join(info.IMAGES_PATH,
"overlay.png")
269 reader.GetFrame(start_frame).Thumbnail(thumb_path, 98, 64, os.path.join(info.IMAGES_PATH,
"mask.png"),
270 overlay_path,
"#000",
False)
275 clip_data[
"image"] = thumb_path
281 transition_details = json.loads(transition_json)
284 fps =
get_app().project.get([
"fps"])
285 fps_float = float(fps[
"num"]) / float(fps[
"den"])
288 transition_reader = openshot.QtImageReader(
289 os.path.join(info.PATH,
"transitions",
"common",
"fade.svg"))
292 transition_object = openshot.Mask()
295 brightness = transition_object.brightness
296 brightness.AddPoint(1, 1.0, openshot.BEZIER)
297 brightness.AddPoint((transition_details[
"end"]) * fps_float, -1.0, openshot.BEZIER)
298 contrast = openshot.Keyframe(3.0)
302 "id":
get_app().project.generate_id(),
303 "layer": transition_details[
"layer"],
304 "title":
"Transition",
306 "position": transition_details[
"position"],
307 "start": transition_details[
"start"],
308 "end": transition_details[
"end"],
309 "brightness": json.loads(brightness.Json()),
310 "contrast": json.loads(contrast.Json()),
311 "reader": json.loads(transition_reader.Json()),
312 "replace_image":
False 325 if not isinstance(transition_json, dict):
326 transition_data = json.loads(transition_json)
328 transition_data = transition_json
331 existing_item = Transition.get(id=transition_data[
"id"])
333 if not existing_item:
335 existing_item = Transition()
337 existing_item.data = transition_data
340 fps =
get_app().project.get([
"fps"])
341 fps_float = float(fps[
"num"]) / float(fps[
"den"])
342 duration = existing_item.data[
"end"] - existing_item.data[
"start"]
350 brightness = existing_item.data[
"brightness"]
351 if len(brightness[
"Points"]) > 1:
353 brightness[
"Points"][-1][
"co"][
"X"] = duration * fps_float
356 contrast = existing_item.data[
"contrast"]
357 if len(contrast[
"Points"]) > 1:
359 contrast[
"Points"][-1][
"co"][
"X"] = duration * fps_float
362 b = openshot.Keyframe()
363 b.AddPoint(1, 1.0, openshot.BEZIER)
364 b.AddPoint(duration * fps_float, -1.0, openshot.BEZIER)
365 brightness = json.loads(b.Json())
369 existing_item.data = {}
370 existing_item.data[
"id"] = transition_data[
"id"]
371 existing_item.data[
"layer"] = transition_data[
"layer"]
372 existing_item.data[
"position"] = transition_data[
"position"]
373 existing_item.data[
"start"] = transition_data[
"start"]
374 existing_item.data[
"end"] = transition_data[
"end"]
376 log.info(
'transition start: %s' % transition_data[
"start"])
377 log.info(
'transition end: %s' % transition_data[
"end"])
380 existing_item.data[
"brightness"] = brightness
382 existing_item.data[
"contrast"] = contrast
394 log.info(
'ShowPlayheadMenu: %s' % position)
400 intersecting_clips = Clip.filter(intersect=position)
401 intersecting_trans = Transition.filter(intersect=position)
404 if intersecting_clips
or intersecting_trans:
406 clip_ids = [c.id
for c
in intersecting_clips]
407 trans_ids = [t.id
for t
in intersecting_trans]
410 Slice_Menu = QMenu(_(
"Slice All"), self)
411 Slice_Keep_Both = Slice_Menu.addAction(_(
"Keep Both Sides"))
412 Slice_Keep_Both.triggered.connect(partial(self.
Slice_Triggered, MENU_SLICE_KEEP_BOTH, clip_ids, trans_ids, position))
413 Slice_Keep_Left = Slice_Menu.addAction(_(
"Keep Left Side"))
414 Slice_Keep_Left.triggered.connect(partial(self.
Slice_Triggered, MENU_SLICE_KEEP_LEFT, clip_ids, trans_ids, position))
415 Slice_Keep_Right = Slice_Menu.addAction(_(
"Keep Right Side"))
416 Slice_Keep_Right.triggered.connect(partial(self.
Slice_Triggered, MENU_SLICE_KEEP_RIGHT, clip_ids, trans_ids, position))
417 menu.addMenu(Slice_Menu)
418 return menu.popup(QCursor.pos())
422 log.info(
'ShowEffectMenu: %s' % effect_id)
425 self.window.addSelection(effect_id,
'effect',
True)
429 menu.addAction(self.window.actionProperties)
433 menu.addAction(self.window.actionRemoveEffect)
434 return menu.popup(QCursor.pos())
436 @pyqtSlot(float, int)
438 log.info(
'ShowTimelineMenu: position: %s, layer: %s' % (position, layer_id))
445 clipboard_clip_ids = [k
for k, v
in self.copy_clipboard.items()
if v.get(
'id')]
446 clipboard_tran_ids = [k
for k, v
in self.copy_transition_clipboard.items()
if v.get(
'id')]
450 if len(clipboard_clip_ids) + len(clipboard_tran_ids) > 0:
452 Paste_Clip = menu.addAction(_(
"Paste"))
453 Paste_Clip.setShortcut(QKeySequence(self.window.getShortcutByName(
"pasteAll")))
454 Paste_Clip.triggered.connect(partial(self.
Paste_Triggered, MENU_PASTE, float(position), int(layer_id), [], []))
456 return menu.popup(QCursor.pos())
460 log.info(
'ShowClipMenu: %s' % clip_id)
466 if clip_id
not in self.window.selected_clips:
467 self.window.addSelection(clip_id,
'clip')
469 clip_ids = self.window.selected_clips
470 tran_ids = self.window.selected_transitions
473 fps =
get_app().project.get([
"fps"])
474 fps_float = float(fps[
"num"]) / float(fps[
"den"])
477 clip = Clip.get(id=clip_id)
478 playhead_position = float(self.window.preview_thread.current_frame) / fps_float
481 translations = [_(
"Start of Clip"), _(
"End of Clip"), _(
"Entire Clip"), _(
"Normal"), _(
"Fast"), _(
"Slow"), _(
"Forward"), _(
"Backward")]
487 if len(tran_ids) + len(clip_ids) > 1:
489 Copy_All = menu.addAction(_(
"Copy"))
490 Copy_All.setShortcut(QKeySequence(self.window.getShortcutByName(
"copyAll")))
491 Copy_All.triggered.connect(partial(self.
Copy_Triggered, MENU_COPY_ALL, clip_ids, tran_ids))
494 Copy_Menu = QMenu(_(
"Copy"), self)
495 Copy_Clip = Copy_Menu.addAction(_(
"Clip"))
496 Copy_Clip.setShortcut(QKeySequence(self.window.getShortcutByName(
"copyAll")))
497 Copy_Clip.triggered.connect(partial(self.
Copy_Triggered, MENU_COPY_CLIP, [clip_id], []))
499 Keyframe_Menu = QMenu(_(
"Keyframes"), self)
500 Copy_Keyframes_All = Keyframe_Menu.addAction(_(
"All"))
501 Copy_Keyframes_All.triggered.connect(partial(self.
Copy_Triggered, MENU_COPY_KEYFRAMES_ALL, [clip_id], []))
502 Keyframe_Menu.addSeparator()
503 Copy_Keyframes_Alpha = Keyframe_Menu.addAction(_(
"Alpha"))
504 Copy_Keyframes_Alpha.triggered.connect(partial(self.
Copy_Triggered, MENU_COPY_KEYFRAMES_ALPHA, [clip_id], []))
505 Copy_Keyframes_Scale = Keyframe_Menu.addAction(_(
"Scale"))
506 Copy_Keyframes_Scale.triggered.connect(partial(self.
Copy_Triggered, MENU_COPY_KEYFRAMES_SCALE, [clip_id], []))
507 Copy_Keyframes_Rotate = Keyframe_Menu.addAction(_(
"Rotation"))
508 Copy_Keyframes_Rotate.triggered.connect(partial(self.
Copy_Triggered, MENU_COPY_KEYFRAMES_ROTATE, [clip_id], []))
509 Copy_Keyframes_Locate = Keyframe_Menu.addAction(_(
"Location"))
510 Copy_Keyframes_Locate.triggered.connect(partial(self.
Copy_Triggered, MENU_COPY_KEYFRAMES_LOCATION, [clip_id], []))
511 Copy_Keyframes_Time = Keyframe_Menu.addAction(_(
"Time"))
512 Copy_Keyframes_Time.triggered.connect(partial(self.
Copy_Triggered, MENU_COPY_KEYFRAMES_TIME, [clip_id], []))
513 Copy_Keyframes_Volume = Keyframe_Menu.addAction(_(
"Volume"))
514 Copy_Keyframes_Volume.triggered.connect(partial(self.
Copy_Triggered, MENU_COPY_KEYFRAMES_VOLUME, [clip_id], []))
517 Copy_Effects = Copy_Menu.addAction(_(
"Effects"))
518 Copy_Effects.triggered.connect(partial(self.
Copy_Triggered, MENU_COPY_EFFECTS, [clip_id], []))
519 Copy_Menu.addMenu(Keyframe_Menu)
520 menu.addMenu(Copy_Menu)
524 clipboard_clip_ids = [k
for k, v
in self.copy_clipboard.items()
if v.get(
'id')]
525 clipboard_tran_ids = [k
for k, v
in self.copy_transition_clipboard.items()
if v.get(
'id')]
527 if self.
copy_clipboard and len(clipboard_clip_ids) + len(clipboard_tran_ids) == 0:
529 Paste_Clip = menu.addAction(_(
"Paste"))
530 Paste_Clip.triggered.connect(partial(self.
Paste_Triggered, MENU_PASTE, 0.0, 0, clip_ids, []))
535 if len(clip_ids) > 1:
536 Alignment_Menu = QMenu(_(
"Align"), self)
537 Align_Left = Alignment_Menu.addAction(_(
"Left"))
538 Align_Left.triggered.connect(partial(self.
Align_Triggered, MENU_ALIGN_LEFT, clip_ids, tran_ids))
539 Align_Right = Alignment_Menu.addAction(_(
"Right"))
540 Align_Right.triggered.connect(partial(self.
Align_Triggered, MENU_ALIGN_RIGHT, clip_ids, tran_ids))
543 menu.addMenu(Alignment_Menu)
546 Fade_Menu = QMenu(_(
"Fade"), self)
547 Fade_None = Fade_Menu.addAction(_(
"No Fade"))
548 Fade_None.triggered.connect(partial(self.
Fade_Triggered, MENU_FADE_NONE, clip_ids))
549 Fade_Menu.addSeparator()
550 for position
in [
"Start of Clip",
"End of Clip",
"Entire Clip"]:
551 Position_Menu = QMenu(_(position), self)
553 if position ==
"Start of Clip":
554 Fade_In_Fast = Position_Menu.addAction(_(
"Fade In (Fast)"))
555 Fade_In_Fast.triggered.connect(partial(self.
Fade_Triggered, MENU_FADE_IN_FAST, clip_ids, position))
556 Fade_In_Slow = Position_Menu.addAction(_(
"Fade In (Slow)"))
557 Fade_In_Slow.triggered.connect(partial(self.
Fade_Triggered, MENU_FADE_IN_SLOW, clip_ids, position))
559 elif position ==
"End of Clip":
560 Fade_Out_Fast = Position_Menu.addAction(_(
"Fade Out (Fast)"))
561 Fade_Out_Fast.triggered.connect(partial(self.
Fade_Triggered, MENU_FADE_OUT_FAST, clip_ids, position))
562 Fade_Out_Slow = Position_Menu.addAction(_(
"Fade Out (Slow)"))
563 Fade_Out_Slow.triggered.connect(partial(self.
Fade_Triggered, MENU_FADE_OUT_SLOW, clip_ids, position))
566 Fade_In_Out_Fast = Position_Menu.addAction(_(
"Fade In and Out (Fast)"))
567 Fade_In_Out_Fast.triggered.connect(partial(self.
Fade_Triggered, MENU_FADE_IN_OUT_FAST, clip_ids, position))
568 Fade_In_Out_Slow = Position_Menu.addAction(_(
"Fade In and Out (Slow)"))
569 Fade_In_Out_Slow.triggered.connect(partial(self.
Fade_Triggered, MENU_FADE_IN_OUT_SLOW, clip_ids, position))
570 Position_Menu.addSeparator()
571 Fade_In_Slow = Position_Menu.addAction(_(
"Fade In (Entire Clip)"))
572 Fade_In_Slow.triggered.connect(partial(self.
Fade_Triggered, MENU_FADE_IN_SLOW, clip_ids, position))
573 Fade_Out_Slow = Position_Menu.addAction(_(
"Fade Out (Entire Clip)"))
574 Fade_Out_Slow.triggered.connect(partial(self.
Fade_Triggered, MENU_FADE_OUT_SLOW, clip_ids, position))
576 Fade_Menu.addMenu(Position_Menu)
577 menu.addMenu(Fade_Menu)
581 Animate_Menu = QMenu(_(
"Animate"), self)
582 Animate_None = Animate_Menu.addAction(_(
"No Animation"))
583 Animate_None.triggered.connect(partial(self.
Animate_Triggered, MENU_ANIMATE_NONE, clip_ids))
584 Animate_Menu.addSeparator()
585 for position
in [
"Start of Clip",
"End of Clip",
"Entire Clip"]:
586 Position_Menu = QMenu(_(position), self)
589 Scale_Menu = QMenu(_(
"Zoom"), self)
590 Animate_In_50_100 = Scale_Menu.addAction(_(
"Zoom In (50% to 100%)"))
591 Animate_In_50_100.triggered.connect(partial(self.
Animate_Triggered, MENU_ANIMATE_IN_50_100, clip_ids, position))
592 Animate_In_75_100 = Scale_Menu.addAction(_(
"Zoom In (75% to 100%)"))
593 Animate_In_75_100.triggered.connect(partial(self.
Animate_Triggered, MENU_ANIMATE_IN_75_100, clip_ids, position))
594 Animate_In_100_150 = Scale_Menu.addAction(_(
"Zoom In (100% to 150%)"))
595 Animate_In_100_150.triggered.connect(partial(self.
Animate_Triggered, MENU_ANIMATE_IN_100_150, clip_ids, position))
596 Animate_Out_100_75 = Scale_Menu.addAction(_(
"Zoom Out (100% to 75%)"))
597 Animate_Out_100_75.triggered.connect(partial(self.
Animate_Triggered, MENU_ANIMATE_OUT_100_75, clip_ids, position))
598 Animate_Out_100_50 = Scale_Menu.addAction(_(
"Zoom Out (100% to 50%)"))
599 Animate_Out_100_50.triggered.connect(partial(self.
Animate_Triggered, MENU_ANIMATE_OUT_100_50, clip_ids, position))
600 Animate_Out_150_100 = Scale_Menu.addAction(_(
"Zoom Out (150% to 100%)"))
601 Animate_Out_150_100.triggered.connect(partial(self.
Animate_Triggered, MENU_ANIMATE_OUT_150_100, clip_ids, position))
602 Position_Menu.addMenu(Scale_Menu)
605 Center_Edge_Menu = QMenu(_(
"Center to Edge"), self)
606 Animate_Center_Top = Center_Edge_Menu.addAction(_(
"Center to Top"))
607 Animate_Center_Top.triggered.connect(partial(self.
Animate_Triggered, MENU_ANIMATE_CENTER_TOP, clip_ids, position))
608 Animate_Center_Left = Center_Edge_Menu.addAction(_(
"Center to Left"))
609 Animate_Center_Left.triggered.connect(partial(self.
Animate_Triggered, MENU_ANIMATE_CENTER_LEFT, clip_ids, position))
610 Animate_Center_Right = Center_Edge_Menu.addAction(_(
"Center to Right"))
611 Animate_Center_Right.triggered.connect(partial(self.
Animate_Triggered, MENU_ANIMATE_CENTER_RIGHT, clip_ids, position))
612 Animate_Center_Bottom = Center_Edge_Menu.addAction(_(
"Center to Bottom"))
613 Animate_Center_Bottom.triggered.connect(partial(self.
Animate_Triggered, MENU_ANIMATE_CENTER_BOTTOM, clip_ids, position))
614 Position_Menu.addMenu(Center_Edge_Menu)
617 Edge_Center_Menu = QMenu(_(
"Edge to Center"), self)
618 Animate_Top_Center = Edge_Center_Menu.addAction(_(
"Top to Center"))
619 Animate_Top_Center.triggered.connect(partial(self.
Animate_Triggered, MENU_ANIMATE_TOP_CENTER, clip_ids, position))
620 Animate_Left_Center = Edge_Center_Menu.addAction(_(
"Left to Center"))
621 Animate_Left_Center.triggered.connect(partial(self.
Animate_Triggered, MENU_ANIMATE_LEFT_CENTER, clip_ids, position))
622 Animate_Right_Center = Edge_Center_Menu.addAction(_(
"Right to Center"))
623 Animate_Right_Center.triggered.connect(partial(self.
Animate_Triggered, MENU_ANIMATE_RIGHT_CENTER, clip_ids, position))
624 Animate_Bottom_Center = Edge_Center_Menu.addAction(_(
"Bottom to Center"))
625 Animate_Bottom_Center.triggered.connect(partial(self.
Animate_Triggered, MENU_ANIMATE_BOTTOM_CENTER, clip_ids, position))
626 Position_Menu.addMenu(Edge_Center_Menu)
629 Edge_Edge_Menu = QMenu(_(
"Edge to Edge"), self)
630 Animate_Top_Bottom = Edge_Edge_Menu.addAction(_(
"Top to Bottom"))
631 Animate_Top_Bottom.triggered.connect(partial(self.
Animate_Triggered, MENU_ANIMATE_TOP_BOTTOM, clip_ids, position))
632 Animate_Left_Right = Edge_Edge_Menu.addAction(_(
"Left to Right"))
633 Animate_Left_Right.triggered.connect(partial(self.
Animate_Triggered, MENU_ANIMATE_LEFT_RIGHT, clip_ids, position))
634 Animate_Right_Left = Edge_Edge_Menu.addAction(_(
"Right to Left"))
635 Animate_Right_Left.triggered.connect(partial(self.
Animate_Triggered, MENU_ANIMATE_RIGHT_LEFT, clip_ids, position))
636 Animate_Bottom_Top = Edge_Edge_Menu.addAction(_(
"Bottom to Top"))
637 Animate_Bottom_Top.triggered.connect(partial(self.
Animate_Triggered, MENU_ANIMATE_BOTTOM_TOP, clip_ids, position))
638 Position_Menu.addMenu(Edge_Edge_Menu)
641 Position_Menu.addSeparator()
642 Random = Position_Menu.addAction(_(
"Random"))
643 Random.triggered.connect(partial(self.
Animate_Triggered, MENU_ANIMATE_RANDOM, clip_ids, position))
646 Animate_Menu.addMenu(Position_Menu)
649 menu.addMenu(Animate_Menu)
652 Rotation_Menu = QMenu(_(
"Rotate"), self)
653 Rotation_None = Rotation_Menu.addAction(_(
"No Rotation"))
654 Rotation_None.triggered.connect(partial(self.
Rotate_Triggered, MENU_ROTATE_NONE, clip_ids))
655 Rotation_Menu.addSeparator()
656 Rotation_90_Right = Rotation_Menu.addAction(_(
"Rotate 90 (Right)"))
657 Rotation_90_Right.triggered.connect(partial(self.
Rotate_Triggered, MENU_ROTATE_90_RIGHT, clip_ids))
658 Rotation_90_Left = Rotation_Menu.addAction(_(
"Rotate 90 (Left)"))
659 Rotation_90_Left.triggered.connect(partial(self.
Rotate_Triggered, MENU_ROTATE_90_LEFT, clip_ids))
660 Rotation_180_Flip = Rotation_Menu.addAction(_(
"Rotate 180 (Flip)"))
661 Rotation_180_Flip.triggered.connect(partial(self.
Rotate_Triggered, MENU_ROTATE_180_FLIP, clip_ids))
662 menu.addMenu(Rotation_Menu)
665 Layout_Menu = QMenu(_(
"Layout"), self)
666 Layout_None = Layout_Menu.addAction(_(
"Reset Layout"))
667 Layout_None.triggered.connect(partial(self.
Layout_Triggered, MENU_LAYOUT_NONE, clip_ids))
668 Layout_Menu.addSeparator()
669 Layout_Center = Layout_Menu.addAction(_(
"1/4 Size - Center"))
670 Layout_Center.triggered.connect(partial(self.
Layout_Triggered, MENU_LAYOUT_CENTER, clip_ids))
671 Layout_Top_Left = Layout_Menu.addAction(_(
"1/4 Size - Top Left"))
672 Layout_Top_Left.triggered.connect(partial(self.
Layout_Triggered, MENU_LAYOUT_TOP_LEFT, clip_ids))
673 Layout_Top_Right = Layout_Menu.addAction(_(
"1/4 Size - Top Right"))
674 Layout_Top_Right.triggered.connect(partial(self.
Layout_Triggered, MENU_LAYOUT_TOP_RIGHT, clip_ids))
675 Layout_Bottom_Left = Layout_Menu.addAction(_(
"1/4 Size - Bottom Left"))
676 Layout_Bottom_Left.triggered.connect(partial(self.
Layout_Triggered, MENU_LAYOUT_BOTTOM_LEFT, clip_ids))
677 Layout_Bottom_Right = Layout_Menu.addAction(_(
"1/4 Size - Bottom Right"))
678 Layout_Bottom_Right.triggered.connect(partial(self.
Layout_Triggered, MENU_LAYOUT_BOTTOM_RIGHT, clip_ids))
679 Layout_Menu.addSeparator()
680 Layout_Bottom_All_With_Aspect = Layout_Menu.addAction(_(
"Show All (Maintain Ratio)"))
681 Layout_Bottom_All_With_Aspect.triggered.connect(partial(self.
Layout_Triggered, MENU_LAYOUT_ALL_WITH_ASPECT, clip_ids))
682 Layout_Bottom_All_Without_Aspect = Layout_Menu.addAction(_(
"Show All (Distort)"))
683 Layout_Bottom_All_Without_Aspect.triggered.connect(partial(self.
Layout_Triggered, MENU_LAYOUT_ALL_WITHOUT_ASPECT, clip_ids))
684 menu.addMenu(Layout_Menu)
687 Time_Menu = QMenu(_(
"Time"), self)
688 Time_None = Time_Menu.addAction(_(
"Reset Time"))
689 Time_None.triggered.connect(partial(self.
Time_Triggered, MENU_TIME_NONE, clip_ids,
'1X'))
690 Time_Menu.addSeparator()
691 for speed, speed_values
in [(
"Normal", [
'1X']), (
"Fast", [
'2X',
'4X',
'8X',
'16X',
'32X']), (
"Slow", [
'1/2X',
'1/4X',
'1/8X',
'1/16X',
'1/32X'])]:
692 Speed_Menu = QMenu(_(speed), self)
694 for direction, direction_value
in [(
"Forward", MENU_TIME_FORWARD), (
"Backward", MENU_TIME_BACKWARD)]:
695 Direction_Menu = QMenu(_(direction), self)
697 for actual_speed
in speed_values:
699 Time_Option = Direction_Menu.addAction(_(actual_speed))
700 Time_Option.triggered.connect(partial(self.
Time_Triggered, direction_value, clip_ids, actual_speed))
703 Speed_Menu.addMenu(Direction_Menu)
705 Time_Menu.addMenu(Speed_Menu)
708 menu.addMenu(Time_Menu)
711 Volume_Menu = QMenu(_(
"Volume"), self)
712 Volume_None = Volume_Menu.addAction(_(
"Reset Volume"))
713 Volume_None.triggered.connect(partial(self.
Volume_Triggered, MENU_VOLUME_NONE, clip_ids))
714 Volume_Menu.addSeparator()
715 for position
in [
"Start of Clip",
"End of Clip",
"Entire Clip"]:
716 Position_Menu = QMenu(_(position), self)
718 if position ==
"Start of Clip":
719 Fade_In_Fast = Position_Menu.addAction(_(
"Fade In (Fast)"))
720 Fade_In_Fast.triggered.connect(partial(self.
Volume_Triggered, MENU_VOLUME_FADE_IN_FAST, clip_ids, position))
721 Fade_In_Slow = Position_Menu.addAction(_(
"Fade In (Slow)"))
722 Fade_In_Slow.triggered.connect(partial(self.
Volume_Triggered, MENU_VOLUME_FADE_IN_SLOW, clip_ids, position))
724 elif position ==
"End of Clip":
725 Fade_Out_Fast = Position_Menu.addAction(_(
"Fade Out (Fast)"))
726 Fade_Out_Fast.triggered.connect(partial(self.
Volume_Triggered, MENU_VOLUME_FADE_OUT_FAST, clip_ids, position))
727 Fade_Out_Slow = Position_Menu.addAction(_(
"Fade Out (Slow)"))
728 Fade_Out_Slow.triggered.connect(partial(self.
Volume_Triggered, MENU_VOLUME_FADE_OUT_SLOW, clip_ids, position))
731 Fade_In_Out_Fast = Position_Menu.addAction(_(
"Fade In and Out (Fast)"))
732 Fade_In_Out_Fast.triggered.connect(partial(self.
Volume_Triggered, MENU_VOLUME_FADE_IN_OUT_FAST, clip_ids, position))
733 Fade_In_Out_Slow = Position_Menu.addAction(_(
"Fade In and Out (Slow)"))
734 Fade_In_Out_Slow.triggered.connect(partial(self.
Volume_Triggered, MENU_VOLUME_FADE_IN_OUT_SLOW, clip_ids, position))
735 Position_Menu.addSeparator()
736 Fade_In_Slow = Position_Menu.addAction(_(
"Fade In (Entire Clip)"))
737 Fade_In_Slow.triggered.connect(partial(self.
Volume_Triggered, MENU_VOLUME_FADE_IN_SLOW, clip_ids, position))
738 Fade_Out_Slow = Position_Menu.addAction(_(
"Fade Out (Entire Clip)"))
739 Fade_Out_Slow.triggered.connect(partial(self.
Volume_Triggered, MENU_VOLUME_FADE_OUT_SLOW, clip_ids, position))
742 Position_Menu.addSeparator()
743 Volume_100 = Position_Menu.addAction(_(
"Level 100%"))
744 Volume_100.triggered.connect(partial(self.
Volume_Triggered, MENU_VOLUME_LEVEL_100, clip_ids, position))
745 Volume_90 = Position_Menu.addAction(_(
"Level 90%"))
746 Volume_90.triggered.connect(partial(self.
Volume_Triggered, MENU_VOLUME_LEVEL_90, clip_ids, position))
747 Volume_80 = Position_Menu.addAction(_(
"Level 80%"))
748 Volume_80.triggered.connect(partial(self.
Volume_Triggered, MENU_VOLUME_LEVEL_80, clip_ids, position))
749 Volume_70 = Position_Menu.addAction(_(
"Level 70%"))
750 Volume_70.triggered.connect(partial(self.
Volume_Triggered, MENU_VOLUME_LEVEL_70, clip_ids, position))
751 Volume_60 = Position_Menu.addAction(_(
"Level 60%"))
752 Volume_60.triggered.connect(partial(self.
Volume_Triggered, MENU_VOLUME_LEVEL_60, clip_ids, position))
753 Volume_50 = Position_Menu.addAction(_(
"Level 50%"))
754 Volume_50.triggered.connect(partial(self.
Volume_Triggered, MENU_VOLUME_LEVEL_50, clip_ids, position))
755 Volume_40 = Position_Menu.addAction(_(
"Level 40%"))
756 Volume_40.triggered.connect(partial(self.
Volume_Triggered, MENU_VOLUME_LEVEL_40, clip_ids, position))
757 Volume_30 = Position_Menu.addAction(_(
"Level 30%"))
758 Volume_30.triggered.connect(partial(self.
Volume_Triggered, MENU_VOLUME_LEVEL_30, clip_ids, position))
759 Volume_20 = Position_Menu.addAction(_(
"Level 20%"))
760 Volume_20.triggered.connect(partial(self.
Volume_Triggered, MENU_VOLUME_LEVEL_20, clip_ids, position))
761 Volume_10 = Position_Menu.addAction(_(
"Level 10%"))
762 Volume_10.triggered.connect(partial(self.
Volume_Triggered, MENU_VOLUME_LEVEL_10, clip_ids, position))
763 Volume_0 = Position_Menu.addAction(_(
"Level 0%"))
764 Volume_0.triggered.connect(partial(self.
Volume_Triggered, MENU_VOLUME_LEVEL_0, clip_ids, position))
766 Volume_Menu.addMenu(Position_Menu)
767 menu.addMenu(Volume_Menu)
770 Split_Audio_Channels_Menu = QMenu(_(
"Separate Audio"), self)
771 Split_Single_Clip = Split_Audio_Channels_Menu.addAction(_(
"Single Clip (all channels)"))
772 Split_Single_Clip.triggered.connect(partial(self.
Split_Audio_Triggered, MENU_SPLIT_AUDIO_SINGLE, clip_ids))
773 Split_Multiple_Clips = Split_Audio_Channels_Menu.addAction(_(
"Multiple Clips (each channel)"))
774 Split_Multiple_Clips.triggered.connect(partial(self.
Split_Audio_Triggered, MENU_SPLIT_AUDIO_MULTIPLE, clip_ids))
775 menu.addMenu(Split_Audio_Channels_Menu)
778 start_of_clip = float(clip.data[
"start"])
779 end_of_clip = float(clip.data[
"end"])
780 position_of_clip = float(clip.data[
"position"])
781 if playhead_position >= position_of_clip
and playhead_position <= (position_of_clip + (end_of_clip - start_of_clip)):
783 Slice_Menu = QMenu(_(
"Slice"), self)
784 Slice_Keep_Both = Slice_Menu.addAction(_(
"Keep Both Sides"))
785 Slice_Keep_Both.setShortcut(QKeySequence(self.window.getShortcutByName(
"sliceAllKeepBothSides")))
786 Slice_Keep_Both.triggered.connect(partial(self.
Slice_Triggered, MENU_SLICE_KEEP_BOTH, [clip_id], [], playhead_position))
787 Slice_Keep_Left = Slice_Menu.addAction(_(
"Keep Left Side"))
788 Slice_Keep_Left.setShortcut(QKeySequence(self.window.getShortcutByName(
"sliceAllKeepLeftSide")))
789 Slice_Keep_Left.triggered.connect(partial(self.
Slice_Triggered, MENU_SLICE_KEEP_LEFT, [clip_id], [], playhead_position))
790 Slice_Keep_Right = Slice_Menu.addAction(_(
"Keep Right Side"))
791 Slice_Keep_Right.setShortcut(QKeySequence(self.window.getShortcutByName(
"sliceAllKeepRightSide")))
792 Slice_Keep_Right.triggered.connect(partial(self.
Slice_Triggered, MENU_SLICE_KEEP_RIGHT, [clip_id], [], playhead_position))
793 menu.addMenu(Slice_Menu)
797 Waveform_Menu = QMenu(_(
"Display"), self)
798 ShowWaveform = Waveform_Menu.addAction(_(
"Show Waveform"))
800 HideWaveform = Waveform_Menu.addAction(_(
"Show Thumbnail"))
802 menu.addMenu(Waveform_Menu)
805 menu.addAction(self.window.actionProperties)
809 menu.addAction(self.window.actionRemoveClip)
812 return menu.popup(QCursor.pos())
819 for clip_id
in clip_ids:
822 clip = Clip.get(id=clip_id)
823 file_path = clip.data[
"reader"][
"path"]
827 clips =
get_app().window.timeline_sync.timeline.Clips()
828 for clip_object
in clips:
829 if clip_object.Id() == clip_id:
832 if c
and c.Reader()
and not c.Reader().info.has_single_image:
834 channel_filter = c.channel_filter.GetInt(1)
837 get_app().setOverrideCursor(QCursor(Qt.WaitCursor))
840 channel_filter = channel_filter
848 for clip_id
in clip_ids:
851 clip = Clip.get(id=clip_id)
854 cmd = JS_SCOPE_SELECTOR +
".hideAudioData('" + clip_id +
"');" 855 self.page().mainFrame().evaluateJavaScript(cmd)
860 log.info(
"Waveform_Ready for clip ID: %s" % (clip_id))
863 serialized_audio_data = json.dumps(audio_data)
866 cmd = JS_SCOPE_SELECTOR +
".setAudioData('" + clip_id +
"', " + serialized_audio_data +
");" 867 self.page().mainFrame().evaluateJavaScript(cmd)
870 get_app().restoreOverrideCursor()
875 log.info(
"Split_Audio_Triggered")
878 for clip_id
in clip_ids:
881 clip = Clip.get(id=clip_id)
884 p = openshot.Point(1, 0.0, openshot.CONSTANT)
885 p_object = json.loads(p.Json())
886 clip.data[
"has_audio"] = {
"Points" : [p_object]}
892 p = openshot.Point(1, -1.0, openshot.CONSTANT)
893 p_object = json.loads(p.Json())
894 clip.data[
"has_audio"] = {
"Points" : [p_object]}
902 if action == MENU_SPLIT_AUDIO_SINGLE:
904 p = openshot.Point(1, -1.0, openshot.CONSTANT)
905 p_object = json.loads(p.Json())
906 clip.data[
"channel_filter"] = {
"Points" : [p_object]}
909 p = openshot.Point(1, 0.0, openshot.CONSTANT)
910 p_object = json.loads(p.Json())
911 clip.data[
"has_video"] = {
"Points" : [p_object]}
914 clip.data[
'layer'] = clip.data[
'layer'] - 1
922 if action == MENU_SPLIT_AUDIO_MULTIPLE:
924 channels = int(clip.data[
"reader"][
"channels"])
927 for channel
in range(0, channels):
928 log.info(
"Adding clip for channel %s" % channel)
931 p = openshot.Point(1, channel, openshot.CONSTANT)
932 p_object = json.loads(p.Json())
933 clip.data[
"channel_filter"] = {
"Points" : [p_object]}
936 p = openshot.Point(1, 0.0, openshot.CONSTANT)
937 p_object = json.loads(p.Json())
938 clip.data[
"has_video"] = {
"Points" : [p_object]}
941 clip.data[
'layer'] = max(clip.data[
'layer'] - 1, 0)
960 for clip_id
in clip_ids:
963 clip = Clip.get(id=clip_id)
965 new_gravity = openshot.GRAVITY_CENTER
966 if action == MENU_LAYOUT_CENTER:
967 new_gravity = openshot.GRAVITY_CENTER
968 if action == MENU_LAYOUT_TOP_LEFT:
969 new_gravity = openshot.GRAVITY_TOP_LEFT
970 elif action == MENU_LAYOUT_TOP_RIGHT:
971 new_gravity = openshot.GRAVITY_TOP_RIGHT
972 elif action == MENU_LAYOUT_BOTTOM_LEFT:
973 new_gravity = openshot.GRAVITY_BOTTOM_LEFT
974 elif action == MENU_LAYOUT_BOTTOM_RIGHT:
975 new_gravity = openshot.GRAVITY_BOTTOM_RIGHT
977 if action == MENU_LAYOUT_NONE:
979 clip.data[
"scale"] = openshot.SCALE_FIT
980 clip.data[
"gravity"] = openshot.GRAVITY_CENTER
983 p = openshot.Point(1, 1.0, openshot.BEZIER)
984 p_object = json.loads(p.Json())
985 clip.data[
"scale_x"] = {
"Points" : [p_object]}
986 clip.data[
"scale_y"] = {
"Points" : [p_object]}
989 p = openshot.Point(1, 0.0, openshot.BEZIER)
990 p_object = json.loads(p.Json())
991 clip.data[
"location_x"] = {
"Points" : [p_object]}
992 clip.data[
"location_y"] = {
"Points" : [p_object]}
994 if action == MENU_LAYOUT_CENTER
or \
995 action == MENU_LAYOUT_TOP_LEFT
or \
996 action == MENU_LAYOUT_TOP_RIGHT
or \
997 action == MENU_LAYOUT_BOTTOM_LEFT
or \
998 action == MENU_LAYOUT_BOTTOM_RIGHT:
1000 clip.data[
"scale"] = openshot.SCALE_FIT
1001 clip.data[
"gravity"] = new_gravity
1004 p = openshot.Point(1, 0.5, openshot.BEZIER)
1005 p_object = json.loads(p.Json())
1006 clip.data[
"scale_x"] = {
"Points" : [p_object]}
1007 clip.data[
"scale_y"] = {
"Points" : [p_object]}
1010 p = openshot.Point(1, 0.0, openshot.BEZIER)
1011 p_object = json.loads(p.Json())
1012 clip.data[
"location_x"] = {
"Points" : [p_object]}
1013 clip.data[
"location_y"] = {
"Points" : [p_object]}
1016 if action == MENU_LAYOUT_ALL_WITH_ASPECT:
1020 elif action == MENU_LAYOUT_ALL_WITHOUT_ASPECT:
1026 self.
update_clip_data(clip.data, only_basic_props=
False, ignore_reader=
True)
1034 for clip_id
in clip_ids:
1037 clip = Clip.get(id=clip_id)
1040 fps =
get_app().project.get([
"fps"])
1041 fps_float = float(fps[
"num"]) / float(fps[
"den"])
1044 clip = Clip.get(id=clip_id)
1045 start_of_clip = round(float(clip.data[
"start"]) * fps_float) + 1.0
1046 end_of_clip = round(float(clip.data[
"end"]) * fps_float) + 1.0
1050 start_animation = start_of_clip
1051 end_animation = end_of_clip
1052 if position ==
"Start of Clip":
1053 start_animation = start_of_clip
1054 end_animation = min(start_of_clip + (1.0 * fps_float), end_of_clip)
1055 elif position ==
"End of Clip":
1056 start_animation = max(1.0, end_of_clip - (1.0 * fps_float))
1057 end_animation = end_of_clip
1059 if action == MENU_ANIMATE_NONE:
1061 default_zoom = openshot.Point(start_animation, 1.0, openshot.BEZIER)
1062 default_zoom_object = json.loads(default_zoom.Json())
1063 default_loc = openshot.Point(start_animation, 0.0, openshot.BEZIER)
1064 default_loc_object = json.loads(default_loc.Json())
1065 clip.data[
"gravity"] = openshot.GRAVITY_CENTER
1066 clip.data[
"scale_x"] = {
"Points" : [default_zoom_object]}
1067 clip.data[
"scale_y"] = {
"Points" : [default_zoom_object]}
1068 clip.data[
"location_x"] = {
"Points" : [default_loc_object]}
1069 clip.data[
"location_y"] = {
"Points" : [default_loc_object]}
1071 if action
in [MENU_ANIMATE_IN_50_100, MENU_ANIMATE_IN_75_100, MENU_ANIMATE_IN_100_150, MENU_ANIMATE_OUT_100_75, MENU_ANIMATE_OUT_100_50, MENU_ANIMATE_OUT_150_100]:
1075 if action == MENU_ANIMATE_IN_50_100:
1077 elif action == MENU_ANIMATE_IN_75_100:
1079 elif action == MENU_ANIMATE_IN_100_150:
1081 elif action == MENU_ANIMATE_OUT_100_75:
1083 elif action == MENU_ANIMATE_OUT_100_50:
1085 elif action == MENU_ANIMATE_OUT_150_100:
1089 start = openshot.Point(start_animation, start_scale, openshot.BEZIER)
1090 start_object = json.loads(start.Json())
1091 end = openshot.Point(end_animation, end_scale, openshot.BEZIER)
1092 end_object = json.loads(end.Json())
1093 clip.data[
"gravity"] = openshot.GRAVITY_CENTER
1094 clip.data[
"scale_x"][
"Points"].append(start_object)
1095 clip.data[
"scale_x"][
"Points"].append(end_object)
1096 clip.data[
"scale_y"][
"Points"].append(start_object)
1097 clip.data[
"scale_y"][
"Points"].append(end_object)
1100 if action
in [MENU_ANIMATE_CENTER_TOP, MENU_ANIMATE_CENTER_LEFT, MENU_ANIMATE_CENTER_RIGHT, MENU_ANIMATE_CENTER_BOTTOM,
1101 MENU_ANIMATE_TOP_CENTER, MENU_ANIMATE_LEFT_CENTER, MENU_ANIMATE_RIGHT_CENTER, MENU_ANIMATE_BOTTOM_CENTER,
1102 MENU_ANIMATE_TOP_BOTTOM, MENU_ANIMATE_LEFT_RIGHT, MENU_ANIMATE_RIGHT_LEFT, MENU_ANIMATE_BOTTOM_TOP]:
1104 animate_start_x = 0.0
1106 animate_start_y = 0.0
1109 if action == MENU_ANIMATE_CENTER_TOP:
1110 animate_end_y = -1.0
1111 elif action == MENU_ANIMATE_CENTER_LEFT:
1112 animate_end_x = -1.0
1113 elif action == MENU_ANIMATE_CENTER_RIGHT:
1115 elif action == MENU_ANIMATE_CENTER_BOTTOM:
1119 elif action == MENU_ANIMATE_TOP_CENTER:
1120 animate_start_y = -1.0
1121 elif action == MENU_ANIMATE_LEFT_CENTER:
1122 animate_start_x = -1.0
1123 elif action == MENU_ANIMATE_RIGHT_CENTER:
1124 animate_start_x = 1.0
1125 elif action == MENU_ANIMATE_BOTTOM_CENTER:
1126 animate_start_y = 1.0
1129 elif action == MENU_ANIMATE_TOP_BOTTOM:
1130 animate_start_y = -1.0
1132 elif action == MENU_ANIMATE_LEFT_RIGHT:
1133 animate_start_x = -1.0
1135 elif action == MENU_ANIMATE_RIGHT_LEFT:
1136 animate_start_x = 1.0
1137 animate_end_x = -1.0
1138 elif action == MENU_ANIMATE_BOTTOM_TOP:
1139 animate_start_y = 1.0
1140 animate_end_y = -1.0
1143 start_x = openshot.Point(start_animation, animate_start_x, openshot.BEZIER)
1144 start_x_object = json.loads(start_x.Json())
1145 end_x = openshot.Point(end_animation, animate_end_x, openshot.BEZIER)
1146 end_x_object = json.loads(end_x.Json())
1147 start_y = openshot.Point(start_animation, animate_start_y, openshot.BEZIER)
1148 start_y_object = json.loads(start_y.Json())
1149 end_y = openshot.Point(end_animation, animate_end_y, openshot.BEZIER)
1150 end_y_object = json.loads(end_y.Json())
1151 clip.data[
"gravity"] = openshot.GRAVITY_CENTER
1152 clip.data[
"location_x"][
"Points"].append(start_x_object)
1153 clip.data[
"location_x"][
"Points"].append(end_x_object)
1154 clip.data[
"location_y"][
"Points"].append(start_y_object)
1155 clip.data[
"location_y"][
"Points"].append(end_y_object)
1157 if action == MENU_ANIMATE_RANDOM:
1159 animate_start_x = uniform(-0.5, 0.5)
1160 animate_end_x = uniform(-0.15, 0.15)
1161 animate_start_y = uniform(-0.5, 0.5)
1162 animate_end_y = uniform(-0.15, 0.15)
1165 start_scale = uniform(0.5, 1.5)
1166 end_scale = uniform(0.85, 1.15)
1169 start = openshot.Point(start_animation, start_scale, openshot.BEZIER)
1170 start_object = json.loads(start.Json())
1171 end = openshot.Point(end_animation, end_scale, openshot.BEZIER)
1172 end_object = json.loads(end.Json())
1173 clip.data[
"gravity"] = openshot.GRAVITY_CENTER
1174 clip.data[
"scale_x"][
"Points"].append(start_object)
1175 clip.data[
"scale_x"][
"Points"].append(end_object)
1176 clip.data[
"scale_y"][
"Points"].append(start_object)
1177 clip.data[
"scale_y"][
"Points"].append(end_object)
1180 start_x = openshot.Point(start_animation, animate_start_x, openshot.BEZIER)
1181 start_x_object = json.loads(start_x.Json())
1182 end_x = openshot.Point(end_animation, animate_end_x, openshot.BEZIER)
1183 end_x_object = json.loads(end_x.Json())
1184 start_y = openshot.Point(start_animation, animate_start_y, openshot.BEZIER)
1185 start_y_object = json.loads(start_y.Json())
1186 end_y = openshot.Point(end_animation, animate_end_y, openshot.BEZIER)
1187 end_y_object = json.loads(end_y.Json())
1188 clip.data[
"gravity"] = openshot.GRAVITY_CENTER
1189 clip.data[
"location_x"][
"Points"].append(start_x_object)
1190 clip.data[
"location_x"][
"Points"].append(end_x_object)
1191 clip.data[
"location_y"][
"Points"].append(start_y_object)
1192 clip.data[
"location_y"][
"Points"].append(end_y_object)
1195 self.
update_clip_data(clip.data, only_basic_props=
False, ignore_reader=
True)
1207 for clip_id
in clip_ids:
1210 clip = Clip.get(id=clip_id)
1213 if action == MENU_COPY_CLIP
or action == MENU_COPY_ALL:
1215 elif action == MENU_COPY_KEYFRAMES_ALL:
1221 self.
copy_clipboard[clip_id][
'location_x'] = clip.data[
'location_x']
1222 self.
copy_clipboard[clip_id][
'location_y'] = clip.data[
'location_y']
1225 elif action == MENU_COPY_KEYFRAMES_ALPHA:
1227 elif action == MENU_COPY_KEYFRAMES_SCALE:
1231 elif action == MENU_COPY_KEYFRAMES_ROTATE:
1234 elif action == MENU_COPY_KEYFRAMES_LOCATION:
1236 self.
copy_clipboard[clip_id][
'location_x'] = clip.data[
'location_x']
1237 self.
copy_clipboard[clip_id][
'location_y'] = clip.data[
'location_y']
1238 elif action == MENU_COPY_KEYFRAMES_TIME:
1240 elif action == MENU_COPY_KEYFRAMES_VOLUME:
1242 elif action == MENU_COPY_EFFECTS:
1246 for tran_id
in tran_ids:
1249 tran = Transition.get(id=tran_id)
1252 if action == MENU_COPY_TRANSITION
or action == MENU_COPY_ALL:
1254 elif action == MENU_COPY_KEYFRAMES_ALL:
1257 elif action == MENU_COPY_KEYFRAMES_BRIGHTNESS:
1259 elif action == MENU_COPY_KEYFRAMES_CONTRAST:
1269 clipboard_clip_ids = [k
for k, v
in self.copy_clipboard.items()
if v.get(
'id')]
1270 clipboard_tran_ids = [k
for k, v
in self.copy_transition_clipboard.items()
if v.get(
'id')]
1273 if len(clipboard_clip_ids) + len(clipboard_tran_ids):
1274 left_most_position = -1.0
1277 for clip_id
in clipboard_clip_ids:
1279 clip = Clip.get(id=clip_id)
1280 if clip.data[
'position'] < left_most_position
or left_most_position == -1.0:
1281 left_most_position = clip.data[
'position']
1282 if clip.data[
'layer'] > top_most_layer
or top_most_layer == -1.0:
1283 top_most_layer = clip.data[
'layer']
1285 for tran_id
in clipboard_tran_ids:
1287 tran = Transition.get(id=tran_id)
1288 if tran.data[
'position'] < left_most_position
or left_most_position == -1.0:
1289 left_most_position = tran.data[
'position']
1290 if tran.data[
'layer'] > top_most_layer
or top_most_layer == -1.0:
1291 top_most_layer = tran.data[
'layer']
1295 layer_id = top_most_layer
1298 position_diff = position - left_most_position
1299 layer_diff = layer_id - top_most_layer
1302 for clip_id
in clipboard_clip_ids:
1304 clip = Clip.get(id=clip_id)
1308 clip.type =
'insert' 1313 clip.data[
'position'] += position_diff
1314 clip.data[
'layer'] += layer_diff
1320 for tran_id
in clipboard_tran_ids:
1322 tran = Transition.get(id=tran_id)
1326 tran.type =
'insert' 1331 tran.data[
'position'] += position_diff
1332 tran.data[
'layer'] += layer_diff
1339 for clip_id
in clip_ids:
1342 clip = Clip.get(id=clip_id)
1355 for tran_id
in tran_ids:
1358 tran = Transition.get(id=tran_id)
1373 prop_name =
"position" 1378 for clip_id
in clip_ids:
1380 clip = Clip.get(id=clip_id)
1381 position = float(clip.data[
"position"])
1382 start_of_clip = float(clip.data[
"start"])
1383 end_of_clip = float(clip.data[
"end"])
1385 if position < left_edge
or left_edge == -1.0:
1386 left_edge = position
1387 if position + (end_of_clip - start_of_clip) > right_edge
or right_edge == -1.0:
1388 right_edge = position + (end_of_clip - start_of_clip)
1391 for tran_id
in tran_ids:
1393 tran = Transition.get(id=tran_id)
1394 position = float(tran.data[
"position"])
1395 start_of_tran = float(tran.data[
"start"])
1396 end_of_tran = float(tran.data[
"end"])
1398 if position < left_edge
or left_edge == -1.0:
1399 left_edge = position
1400 if position + (end_of_tran - start_of_tran) > right_edge
or right_edge == -1.0:
1401 right_edge = position + (end_of_tran - start_of_tran)
1405 for clip_id
in clip_ids:
1407 clip = Clip.get(id=clip_id)
1409 if action == MENU_ALIGN_LEFT:
1410 clip.data[
'position'] = left_edge
1411 elif action == MENU_ALIGN_RIGHT:
1412 position = float(clip.data[
"position"])
1413 start_of_clip = float(clip.data[
"start"])
1414 end_of_clip = float(clip.data[
"end"])
1415 right_clip_edge = position + (end_of_clip - start_of_clip)
1417 clip.data[
'position'] = position + (right_edge - right_clip_edge)
1420 self.
update_clip_data(clip.data, only_basic_props=
False, ignore_reader=
True)
1423 for tran_id
in tran_ids:
1425 tran = Transition.get(id=tran_id)
1427 if action == MENU_ALIGN_LEFT:
1428 tran.data[
'position'] = left_edge
1429 elif action == MENU_ALIGN_RIGHT:
1430 position = float(tran.data[
"position"])
1431 start_of_tran = float(tran.data[
"start"])
1432 end_of_tran = float(tran.data[
"end"])
1433 right_tran_edge = position + (end_of_tran - start_of_tran)
1435 tran.data[
'position'] = position + (right_edge - right_tran_edge)
1447 fps =
get_app().project.get([
"fps"])
1448 fps_float = float(fps[
"num"]) / float(fps[
"den"])
1451 for clip_id
in clip_ids:
1454 clip = Clip.get(id=clip_id)
1455 start_of_clip = round(float(clip.data[
"start"]) * fps_float) + 1.0
1456 end_of_clip = round(float(clip.data[
"end"]) * fps_float) + 1.0
1460 start_animation = start_of_clip
1461 end_animation = end_of_clip
1462 if position ==
"Start of Clip" and action
in [MENU_FADE_IN_FAST, MENU_FADE_OUT_FAST]:
1463 start_animation = start_of_clip
1464 end_animation = min(start_of_clip + (1.0 * fps_float), end_of_clip)
1465 elif position ==
"Start of Clip" and action
in [MENU_FADE_IN_SLOW, MENU_FADE_OUT_SLOW]:
1466 start_animation = start_of_clip
1467 end_animation = min(start_of_clip + (3.0 * fps_float), end_of_clip)
1468 elif position ==
"End of Clip" and action
in [MENU_FADE_IN_FAST, MENU_FADE_OUT_FAST]:
1469 start_animation = max(1.0, end_of_clip - (1.0 * fps_float))
1470 end_animation = end_of_clip
1471 elif position ==
"End of Clip" and action
in [MENU_FADE_IN_SLOW, MENU_FADE_OUT_SLOW]:
1472 start_animation = max(1.0, end_of_clip - (3.0 * fps_float))
1473 end_animation = end_of_clip
1476 if position ==
"Entire Clip" and action == MENU_FADE_IN_OUT_FAST:
1478 self.
Fade_Triggered(MENU_FADE_IN_FAST, clip_ids,
"Start of Clip")
1481 elif position ==
"Entire Clip" and action == MENU_FADE_IN_OUT_SLOW:
1483 self.
Fade_Triggered(MENU_FADE_IN_SLOW, clip_ids,
"Start of Clip")
1487 if action == MENU_FADE_NONE:
1489 p = openshot.Point(1, 1.0, openshot.BEZIER)
1490 p_object = json.loads(p.Json())
1491 clip.data[prop_name] = {
"Points" : [p_object]}
1493 if action
in [MENU_FADE_IN_FAST, MENU_FADE_IN_SLOW]:
1495 start = openshot.Point(start_animation, 0.0, openshot.BEZIER)
1496 start_object = json.loads(start.Json())
1497 end = openshot.Point(end_animation, 1.0, openshot.BEZIER)
1498 end_object = json.loads(end.Json())
1499 clip.data[prop_name][
"Points"].append(start_object)
1500 clip.data[prop_name][
"Points"].append(end_object)
1502 if action
in [MENU_FADE_OUT_FAST, MENU_FADE_OUT_SLOW]:
1504 start = openshot.Point(start_animation, 1.0, openshot.BEZIER)
1505 start_object = json.loads(start.Json())
1506 end = openshot.Point(end_animation, 0.0, openshot.BEZIER)
1507 end_object = json.loads(end.Json())
1508 clip.data[prop_name][
"Points"].append(start_object)
1509 clip.data[prop_name][
"Points"].append(end_object)
1512 self.
update_clip_data(clip.data, only_basic_props=
False, ignore_reader=
True)
1520 for clip_id
in clip_ids:
1523 clip = Clip.get(id=clip_id)
1526 has_audio_data = bool(self.
eval_js(JS_SCOPE_SELECTOR +
".hasAudioData('" + clip_id +
"');"))
1528 if action == MENU_SLICE_KEEP_LEFT
or action == MENU_SLICE_KEEP_BOTH:
1530 position_of_clip = float(clip.data[
"position"])
1531 start_of_clip = float(clip.data[
"start"])
1532 end_of_clip = float(clip.data[
"end"])
1535 clip.data[
"end"] = start_of_clip + (playhead_position - position_of_clip)
1537 elif action == MENU_SLICE_KEEP_RIGHT:
1539 position_of_clip = float(clip.data[
"position"])
1540 start_of_clip = float(clip.data[
"start"])
1541 end_of_clip = float(clip.data[
"end"])
1544 clip.data[
"position"] = playhead_position
1545 clip.data[
"start"] = start_of_clip + (playhead_position - position_of_clip)
1550 if action == MENU_SLICE_KEEP_BOTH:
1553 right_clip = Clip.get(id=clip_id)
1556 right_clip.id =
None 1557 right_clip.type =
'insert' 1558 right_clip.data.pop(
'id')
1559 right_clip.key.pop(1)
1562 position_of_clip = float(right_clip.data[
"position"])
1563 start_of_clip = float(right_clip.data[
"start"])
1566 right_clip.data[
"position"] = playhead_position
1567 right_clip.data[
"start"] = start_of_clip + (playhead_position - position_of_clip)
1576 self.
update_clip_data(right_clip.data, only_basic_props=
False, ignore_reader=
True)
1580 log.info(
"Generate right splice waveform for clip id: %s" % right_clip.id)
1584 self.
update_clip_data(clip.data, only_basic_props=
False, ignore_reader=
True)
1588 log.info(
"Generate left splice waveform for clip id: %s" % clip.id)
1593 for trans_id
in trans_ids:
1595 trans = Transition.get(id=trans_id)
1597 if action == MENU_SLICE_KEEP_LEFT
or action == MENU_SLICE_KEEP_BOTH:
1599 position_of_tran = float(trans.data[
"position"])
1602 trans.data[
"end"] = playhead_position - position_of_tran
1604 elif action == MENU_SLICE_KEEP_RIGHT:
1606 position_of_tran = float(trans.data[
"position"])
1607 end_of_tran = float(trans.data[
"end"])
1610 trans.data[
"position"] = playhead_position
1611 trans.data[
"end"] = end_of_tran - (playhead_position - position_of_tran)
1613 if action == MENU_SLICE_KEEP_BOTH:
1616 right_tran = Transition.get(id=trans_id)
1619 right_tran.id =
None 1620 right_tran.type =
'insert' 1621 right_tran.data.pop(
'id')
1622 right_tran.key.pop(1)
1625 position_of_tran = float(right_tran.data[
"position"])
1626 end_of_tran = float(right_tran.data[
"end"])
1629 right_tran.data[
"position"] = playhead_position
1630 right_tran.data[
"end"] = end_of_tran - (playhead_position - position_of_tran)
1645 prop_name =
"volume" 1648 fps =
get_app().project.get([
"fps"])
1649 fps_float = float(fps[
"num"]) / float(fps[
"den"])
1652 for clip_id
in clip_ids:
1655 clip = Clip.get(id=clip_id)
1656 start_of_clip = round(float(clip.data[
"start"]) * fps_float) + 1.0
1657 end_of_clip = round(float(clip.data[
"end"]) * fps_float) + 1.0
1661 start_animation = start_of_clip
1662 end_animation = end_of_clip
1663 if position ==
"Start of Clip" and action
in [MENU_VOLUME_FADE_IN_FAST, MENU_VOLUME_FADE_OUT_FAST]:
1664 start_animation = start_of_clip
1665 end_animation = min(start_of_clip + (1.0 * fps_float), end_of_clip)
1666 elif position ==
"Start of Clip" and action
in [MENU_VOLUME_FADE_IN_SLOW, MENU_VOLUME_FADE_OUT_SLOW]:
1667 start_animation = start_of_clip
1668 end_animation = min(start_of_clip + (3.0 * fps_float), end_of_clip)
1669 elif position ==
"End of Clip" and action
in [MENU_VOLUME_FADE_IN_FAST, MENU_VOLUME_FADE_OUT_FAST]:
1670 start_animation = max(1.0, end_of_clip - (1.0 * fps_float))
1671 end_animation = end_of_clip
1672 elif position ==
"End of Clip" and action
in [MENU_VOLUME_FADE_IN_SLOW, MENU_VOLUME_FADE_OUT_SLOW]:
1673 start_animation = max(1.0, end_of_clip - (3.0 * fps_float))
1674 end_animation = end_of_clip
1675 elif position ==
"Start of Clip":
1677 start_animation = start_of_clip
1678 end_animation = start_of_clip
1679 elif position ==
"End of Clip":
1681 start_animation = end_of_clip
1682 end_animation = end_of_clip
1685 if position ==
"Entire Clip" and action == MENU_VOLUME_FADE_IN_OUT_FAST:
1690 elif position ==
"Entire Clip" and action == MENU_VOLUME_FADE_IN_OUT_SLOW:
1696 if action == MENU_VOLUME_NONE:
1698 p = openshot.Point(1, 1.0, openshot.BEZIER)
1699 p_object = json.loads(p.Json())
1700 clip.data[prop_name] = {
"Points" : [p_object]}
1702 if action
in [MENU_VOLUME_FADE_IN_FAST, MENU_VOLUME_FADE_IN_SLOW]:
1704 start = openshot.Point(start_animation, 0.0, openshot.BEZIER)
1705 start_object = json.loads(start.Json())
1706 end = openshot.Point(end_animation, 1.0, openshot.BEZIER)
1707 end_object = json.loads(end.Json())
1708 clip.data[prop_name][
"Points"].append(start_object)
1709 clip.data[prop_name][
"Points"].append(end_object)
1711 if action
in [MENU_VOLUME_FADE_OUT_FAST, MENU_VOLUME_FADE_OUT_SLOW]:
1713 start = openshot.Point(start_animation, 1.0, openshot.BEZIER)
1714 start_object = json.loads(start.Json())
1715 end = openshot.Point(end_animation, 0.0, openshot.BEZIER)
1716 end_object = json.loads(end.Json())
1717 clip.data[prop_name][
"Points"].append(start_object)
1718 clip.data[prop_name][
"Points"].append(end_object)
1720 if action
in [MENU_VOLUME_LEVEL_100, MENU_VOLUME_LEVEL_90, MENU_VOLUME_LEVEL_80, MENU_VOLUME_LEVEL_70,
1721 MENU_VOLUME_LEVEL_60, MENU_VOLUME_LEVEL_50, MENU_VOLUME_LEVEL_40, MENU_VOLUME_LEVEL_30,
1722 MENU_VOLUME_LEVEL_20, MENU_VOLUME_LEVEL_10, MENU_VOLUME_LEVEL_0]:
1724 p = openshot.Point(start_animation, float(action) / 100.0, openshot.BEZIER)
1725 p_object = json.loads(p.Json())
1726 clip.data[prop_name][
"Points"].append(p_object)
1729 self.
update_clip_data(clip.data, only_basic_props=
False, ignore_reader=
True)
1732 has_audio_data = bool(self.
eval_js(JS_SCOPE_SELECTOR +
".hasAudioData('" + clip.id +
"');"))
1741 prop_name =
"rotation" 1744 fps =
get_app().project.get([
"fps"])
1745 fps_float = float(fps[
"num"]) / float(fps[
"den"])
1748 for clip_id
in clip_ids:
1751 clip = Clip.get(id=clip_id)
1753 if action == MENU_ROTATE_NONE:
1755 p = openshot.Point(1, 0.0, openshot.BEZIER)
1756 p_object = json.loads(p.Json())
1757 clip.data[prop_name] = {
"Points" : [p_object]}
1759 if action == MENU_ROTATE_90_RIGHT:
1761 p = openshot.Point(1, 90.0, openshot.BEZIER)
1762 p_object = json.loads(p.Json())
1763 clip.data[prop_name] = {
"Points" : [p_object]}
1765 if action == MENU_ROTATE_90_LEFT:
1767 p = openshot.Point(1, -90.0, openshot.BEZIER)
1768 p_object = json.loads(p.Json())
1769 clip.data[prop_name] = {
"Points" : [p_object]}
1771 if action == MENU_ROTATE_180_FLIP:
1773 p = openshot.Point(1, 180.0, openshot.BEZIER)
1774 p_object = json.loads(p.Json())
1775 clip.data[prop_name] = {
"Points" : [p_object]}
1778 self.
update_clip_data(clip.data, only_basic_props=
False, ignore_reader=
True)
1787 fps =
get_app().project.get([
"fps"])
1788 fps_float = float(fps[
"num"]) / float(fps[
"den"])
1791 for clip_id
in clip_ids:
1794 clip = Clip.get(id=clip_id)
1800 speed_label = speed.replace(
'X',
'')
1801 speed_parts = speed_label.split(
'/')
1803 if len(speed_parts) == 2:
1804 speed_factor = float(speed_parts[0]) / float(speed_parts[1])
1805 even_multiple = int(speed_parts[1])
1807 speed_factor = float(speed_label)
1808 even_multiple = int(speed_factor)
1811 p = openshot.Point(start_animation, 0.0, openshot.LINEAR)
1812 p_object = json.loads(p.Json())
1813 clip.data[prop_name] = {
"Points" : [p_object]}
1816 if "original_data" in clip.data.keys():
1817 clip.data[
"end"] = clip.data[
"original_data"][
"end"]
1818 clip.data[
"duration"] = clip.data[
"original_data"][
"duration"]
1819 clip.data[
"reader"][
"video_length"] = clip.data[
"original_data"][
"video_length"]
1820 clip.data.pop(
"original_data")
1823 end_of_clip = (float(clip.data[
"end"]) * fps_float) + 1
1826 start_animation = (float(clip.data[
"start"]) * fps_float) + 1
1827 duration_animation = self.
round_to_multiple(end_of_clip - start_animation, even_multiple)
1828 end_animation = start_animation + duration_animation
1830 if action == MENU_TIME_FORWARD:
1832 start = openshot.Point(start_animation, start_animation, openshot.LINEAR)
1833 start_object = json.loads(start.Json())
1834 clip.data[prop_name] = {
"Points" : [start_object]}
1835 end = openshot.Point(start_animation + (duration_animation / speed_factor), end_animation, openshot.LINEAR)
1836 end_object = json.loads(end.Json())
1837 clip.data[prop_name][
"Points"].append(end_object)
1839 if "original_data" not in clip.data.keys():
1840 clip.data[
"original_data"] = {
"end" : clip.data[
"end"],
1841 "duration" : clip.data[
"duration"],
1842 "video_length" : clip.data[
"reader"][
"video_length"] }
1844 clip.data[
"end"] = (start_animation + (duration_animation / speed_factor)) / fps_float
1845 clip.data[
"duration"] = self.
round_to_multiple(clip.data[
"duration"] / speed_factor, even_multiple)
1846 clip.data[
"reader"][
"video_length"] = str(self.
round_to_multiple(float(clip.data[
"reader"][
"video_length"]) / speed_factor, even_multiple))
1848 if action == MENU_TIME_BACKWARD:
1850 start = openshot.Point(start_animation, end_animation, openshot.LINEAR)
1851 start_object = json.loads(start.Json())
1852 clip.data[prop_name] = {
"Points" : [start_object]}
1853 end = openshot.Point(start_animation + (duration_animation / speed_factor), start_animation, openshot.LINEAR)
1854 end_object = json.loads(end.Json())
1855 clip.data[prop_name][
"Points"].append(end_object)
1857 if "original_data" not in clip.data.keys():
1858 clip.data[
"original_data"] = {
"end" : clip.data[
"end"],
1859 "duration" : clip.data[
"duration"],
1860 "video_length" : clip.data[
"reader"][
"video_length"] }
1862 clip.data[
"end"] = (start_animation + (duration_animation / speed_factor)) / fps_float
1863 clip.data[
"duration"] = self.
round_to_multiple(clip.data[
"duration"] / speed_factor, even_multiple)
1864 clip.data[
"reader"][
"video_length"] = str(self.
round_to_multiple(float(clip.data[
"reader"][
"video_length"]) / speed_factor, even_multiple))
1867 self.
update_clip_data(clip.data, only_basic_props=
False, ignore_reader=
True)
1872 return number - (number % multiple)
1877 from math
import sqrt
1880 available_clips = []
1881 start_position = float(clip.data[
"position"])
1882 for c
in Clip.filter():
1883 if float(c.data[
"position"]) >= (start_position - 0.5)
and float(c.data[
"position"]) <= (start_position + 0.5):
1885 available_clips.append(c)
1888 number_of_clips = len(available_clips)
1889 number_of_rows = int(sqrt(number_of_clips))
1890 max_clips_on_row = float(number_of_clips) / float(number_of_rows)
1893 if max_clips_on_row > float(int(max_clips_on_row)):
1894 max_clips_on_row = int(max_clips_on_row + 1)
1896 max_clips_on_row = int(max_clips_on_row)
1899 height = 1.0 / float(number_of_rows)
1900 width = 1.0 / float(max_clips_on_row)
1905 for row
in range(0, number_of_rows):
1908 column_string =
" - - - " 1909 for col
in range(0, max_clips_on_row):
1910 if clip_index < number_of_clips:
1912 X = float(col) * width
1913 Y = float(row) * height
1916 selected_clip = available_clips[clip_index]
1917 selected_clip.data[
"gravity"] = openshot.GRAVITY_TOP_LEFT
1920 selected_clip.data[
"scale"] = openshot.SCALE_STRETCH
1922 selected_clip.data[
"scale"] = openshot.SCALE_FIT
1925 w = openshot.Point(1, width, openshot.BEZIER)
1926 w_object = json.loads(w.Json())
1927 selected_clip.data[
"scale_x"] = {
"Points" : [w_object]}
1928 h = openshot.Point(1, height, openshot.BEZIER)
1929 h_object = json.loads(h.Json())
1930 selected_clip.data[
"scale_y"] = {
"Points" : [h_object]}
1931 x_point = openshot.Point(1, X, openshot.BEZIER)
1932 x_object = json.loads(x_point.Json())
1933 selected_clip.data[
"location_x"] = {
"Points" : [x_object]}
1934 y_point = openshot.Point(1, Y, openshot.BEZIER)
1935 y_object = json.loads(y_point.Json())
1936 selected_clip.data[
"location_y"] = {
"Points" : [y_object]}
1938 log.info(
'Updating clip id: %s' % selected_clip.data[
"id"])
1939 log.info(
'width: %s, height: %s' % (width, height))
1945 self.
update_clip_data(selected_clip.data, only_basic_props=
False, ignore_reader=
True)
1950 log.info(
"Reverse_Transition_Triggered")
1953 for tran_id
in tran_ids:
1956 tran = Transition.get(id=tran_id)
1959 tran_data_copy = deepcopy(tran.data)
1960 new_index = len(tran.data[
"brightness"][
"Points"])
1961 for point
in tran.data[
"brightness"][
"Points"]:
1963 tran_data_copy[
"brightness"][
"Points"][new_index][
"co"][
"Y"] = point[
"co"][
"Y"]
1964 if "handle_left" in point:
1965 tran_data_copy[
"brightness"][
"Points"][new_index][
"handle_left"][
"Y"] = point[
"handle_left"][
"Y"]
1966 tran_data_copy[
"brightness"][
"Points"][new_index][
"handle_right"][
"Y"] = point[
"handle_right"][
"Y"]
1973 log.info(
'ShowTransitionMenu: %s' % tran_id)
1979 if tran_id
not in self.window.selected_transitions:
1980 self.window.addSelection(tran_id,
'transition')
1982 tran_ids = self.window.selected_transitions
1983 clip_ids = self.window.selected_clips
1986 fps =
get_app().project.get([
"fps"])
1987 fps_float = float(fps[
"num"]) / float(fps[
"den"])
1990 tran = Transition.get(id=tran_id)
1991 playhead_position = float(self.window.preview_thread.current_frame) / fps_float
1996 if len(tran_ids) + len(clip_ids) > 1:
1998 Copy_All = menu.addAction(_(
"Copy"))
1999 Copy_All.setShortcut(QKeySequence(self.window.getShortcutByName(
"copyAll")))
2000 Copy_All.triggered.connect(partial(self.
Copy_Triggered, MENU_COPY_ALL, clip_ids, tran_ids))
2003 Copy_Menu = QMenu(_(
"Copy"), self)
2004 Copy_Tran = Copy_Menu.addAction(_(
"Transition"))
2005 Copy_Tran.setShortcut(QKeySequence(self.window.getShortcutByName(
"copyAll")))
2006 Copy_Tran.triggered.connect(partial(self.
Copy_Triggered, MENU_COPY_TRANSITION, [], [tran_id]))
2008 Keyframe_Menu = QMenu(_(
"Keyframes"), self)
2009 Copy_Keyframes_All = Keyframe_Menu.addAction(_(
"All"))
2010 Copy_Keyframes_All.triggered.connect(partial(self.
Copy_Triggered, MENU_COPY_KEYFRAMES_ALL, [], [tran_id]))
2011 Keyframe_Menu.addSeparator()
2012 Copy_Keyframes_Brightness = Keyframe_Menu.addAction(_(
"Brightness"))
2013 Copy_Keyframes_Brightness.triggered.connect(partial(self.
Copy_Triggered, MENU_COPY_KEYFRAMES_BRIGHTNESS, [], [tran_id]))
2014 Copy_Keyframes_Scale = Keyframe_Menu.addAction(_(
"Contrast"))
2015 Copy_Keyframes_Scale.triggered.connect(partial(self.
Copy_Triggered, MENU_COPY_KEYFRAMES_CONTRAST, [], [tran_id]))
2018 Copy_Menu.addMenu(Keyframe_Menu)
2019 menu.addMenu(Copy_Menu)
2023 clipboard_clip_ids = [k
for k, v
in self.copy_clipboard.items()
if v.get(
'id')]
2024 clipboard_tran_ids = [k
for k, v
in self.copy_transition_clipboard.items()
if v.get(
'id')]
2028 Paste_Tran = menu.addAction(_(
"Paste"))
2029 Paste_Tran.triggered.connect(partial(self.
Paste_Triggered, MENU_PASTE, 0.0, 0, [], tran_ids))
2034 if len(clip_ids) > 1:
2035 Alignment_Menu = QMenu(_(
"Align"), self)
2036 Align_Left = Alignment_Menu.addAction(_(
"Left"))
2037 Align_Left.triggered.connect(partial(self.
Align_Triggered, MENU_ALIGN_LEFT, clip_ids, tran_ids))
2038 Align_Right = Alignment_Menu.addAction(_(
"Right"))
2039 Align_Right.triggered.connect(partial(self.
Align_Triggered, MENU_ALIGN_RIGHT, clip_ids, tran_ids))
2042 menu.addMenu(Alignment_Menu)
2045 start_of_tran = float(tran.data[
"start"])
2046 end_of_tran = float(tran.data[
"end"])
2047 position_of_tran = float(tran.data[
"position"])
2048 if playhead_position >= position_of_tran
and playhead_position <= (position_of_tran + (end_of_tran - start_of_tran)):
2050 Slice_Menu = QMenu(_(
"Slice"), self)
2051 Slice_Keep_Both = Slice_Menu.addAction(_(
"Keep Both Sides"))
2052 Slice_Keep_Both.triggered.connect(partial(self.
Slice_Triggered, MENU_SLICE_KEEP_BOTH, [], [tran_id], playhead_position))
2053 Slice_Keep_Left = Slice_Menu.addAction(_(
"Keep Left Side"))
2054 Slice_Keep_Left.triggered.connect(partial(self.
Slice_Triggered, MENU_SLICE_KEEP_LEFT, [], [tran_id], playhead_position))
2055 Slice_Keep_Right = Slice_Menu.addAction(_(
"Keep Right Side"))
2056 Slice_Keep_Right.triggered.connect(partial(self.
Slice_Triggered, MENU_SLICE_KEEP_RIGHT, [], [tran_id], playhead_position))
2057 menu.addMenu(Slice_Menu)
2060 Reverse_Transition = menu.addAction(_(
"Reverse Transition"))
2065 menu.addAction(self.window.actionProperties)
2069 menu.addAction(self.window.actionRemoveTransition)
2072 return menu.popup(QCursor.pos())
2076 log.info(
'ShowTrackMenu: %s' % layer_id)
2078 if layer_id
not in self.window.selected_tracks:
2079 self.window.selected_tracks = [layer_id]
2082 track = Track.get(id=layer_id)
2085 menu.addAction(self.window.actionAddTrackAbove)
2086 menu.addAction(self.window.actionAddTrackBelow)
2087 menu.addAction(self.window.actionRenameTrack)
2088 if track.data[
'lock']:
2089 menu.addAction(self.window.actionUnlockTrack)
2091 menu.addAction(self.window.actionLockTrack)
2093 menu.addAction(self.window.actionRemoveTrack)
2094 return menu.popup(QCursor.pos())
2098 log.info(
'ShowMarkerMenu: %s' % marker_id)
2100 if marker_id
not in self.window.selected_markers:
2101 self.window.selected_markers = [marker_id]
2104 menu.addAction(self.window.actionRemoveMarker)
2105 return menu.popup(QCursor.pos())
2111 clip = Clip.get(id=clip_id)
2112 path = clip.data[
'reader'][
'path']
2115 frame_number = max(frame_number, 1)
2116 frame_number = min(frame_number, int(clip.data[
'reader'][
'video_length']))
2119 self.window.LoadFileSignal.emit(path)
2120 self.window.SpeedSignal.emit(0)
2123 self.window.SeekSignal.emit(frame_number)
2125 @pyqtSlot(float, int, str)
2129 self.window.LoadFileSignal.emit(
'')
2136 self.window.previewFrame(position_seconds, position_frames, time_code)
2144 code = JS_SCOPE_SELECTOR +
".MovePlayheadToFrame(" + str(position_frames) +
");" 2153 self.
eval_js(JS_SCOPE_SELECTOR +
".SetSnappingMode(%s);" % int(enable_snapping))
2155 @pyqtSlot(str, str, bool)
2161 self.window.addSelection(item_id, item_type, clear_existing)
2169 self.window.removeSelection(item_id, item_type)
2178 self.window.zoomScaleLabel.setText(_(
"{} seconds").
format(newValue))
2180 cmd = JS_SCOPE_SELECTOR +
".setScale(" + str(newValue) +
");" 2181 self.page().mainFrame().evaluateJavaScript(cmd)
2184 self.redraw_audio_timer.start()
2195 if int(QCoreApplication.instance().keyboardModifiers() & Qt.ControlModifier) > 0:
2198 steps = int(event.angleDelta().y() / tick_scale)
2199 self.window.sliderZoom.setValue(self.window.sliderZoom.value() - self.window.sliderZoom.pageStep() * steps)
2207 self.page().mainFrame().addToJavaScriptWindowObject(
'timeline', self)
2208 self.page().mainFrame().addToJavaScriptWindowObject(
'mainWindow', self.
window)
2216 if not self.
new_item and not event.mimeData().hasUrls()
and event.mimeData().hasText():
2224 data = json.loads(event.mimeData().text())
2244 file = File.get(id=file_id)
2246 if (file.data[
"media_type"] ==
"video" or file.data[
"media_type"] ==
"image"):
2248 thumb_path = os.path.join(info.THUMBNAIL_PATH,
"%s.png" % file.data[
"id"])
2251 thumb_path = os.path.join(info.PATH,
"images",
"AudioThumbnail.png")
2254 path, filename = os.path.split(file.data[
"path"])
2257 file_path = file.absolute_path()
2260 c = openshot.Clip(file_path)
2263 new_clip = json.loads(c.Json())
2264 new_clip[
"file_id"] = file.id
2265 new_clip[
"title"] = filename
2266 new_clip[
"image"] = thumb_path
2270 end_frame = new_clip[
"reader"][
"duration"]
2271 if 'start' in file.data.keys():
2272 new_clip[
"start"] = file.data[
'start']
2273 if 'end' in file.data.keys():
2274 new_clip[
"end"] = file.data[
'end']
2277 top_layer = int(self.
eval_js(JS_SCOPE_SELECTOR +
".GetJavaScriptTrack(" + str(position.y()) +
");"))
2278 new_clip[
"layer"] = top_layer
2281 js_position = self.
eval_js(JS_SCOPE_SELECTOR +
".GetJavaScriptPosition(" + str(position.x()) +
");")
2282 new_clip[
"position"] = js_position
2285 new_clip[
"duration"] = new_clip[
"reader"][
"duration"]
2286 if file.data[
"media_type"] ==
"image":
2287 new_clip[
"end"] = self.settings.get(
"default-image-length")
2290 file_properties_fps = float(file.data[
"fps"][
"num"]) / float(file.data[
"fps"][
"den"])
2291 file_fps = float(new_clip[
"reader"][
"fps"][
"num"]) / float(new_clip[
"reader"][
"fps"][
"den"])
2292 fps_diff = file_fps / file_properties_fps
2293 new_clip[
"reader"][
"fps"][
"num"] = file.data[
"fps"][
"num"]
2294 new_clip[
"reader"][
"fps"][
"den"] = file.data[
"fps"][
"den"]
2296 new_clip[
"reader"][
"duration"] *= fps_diff
2297 new_clip[
"end"] *= fps_diff
2298 new_clip[
"duration"] *= fps_diff
2307 code = JS_SCOPE_SELECTOR +
".StartManualMove('clip', '" + self.
item_id +
"');" 2315 get_app().updates.update([
"duration"], new_duration)
2319 log.info(
"addTransition...")
2322 top_layer = int(self.
eval_js(JS_SCOPE_SELECTOR +
".GetJavaScriptTrack(" + str(position.y()) +
");"))
2325 js_position = self.
eval_js(JS_SCOPE_SELECTOR +
".GetJavaScriptPosition(" + str(position.x()) +
");")
2328 fps =
get_app().project.get([
"fps"])
2329 fps_float = float(fps[
"num"]) / float(fps[
"den"])
2332 transition_reader = openshot.QtImageReader(file_ids[0])
2334 brightness = openshot.Keyframe()
2335 brightness.AddPoint(1, 1.0, openshot.BEZIER)
2336 brightness.AddPoint(10 * fps_float, -1.0, openshot.BEZIER)
2337 contrast = openshot.Keyframe(3.0)
2340 transitions_data = {
2341 "id":
get_app().project.generate_id(),
2343 "title":
"Transition",
2345 "position": js_position,
2348 "brightness": json.loads(brightness.Json()),
2349 "contrast": json.loads(contrast.Json()),
2350 "reader": json.loads(transition_reader.Json()),
2351 "replace_image":
False 2358 self.
item_id = transitions_data.get(
'id')
2361 code = JS_SCOPE_SELECTOR +
".StartManualMove('transition', '" + self.
item_id +
"');" 2366 log.info(
"addEffect...")
2368 name = effect_names[0]
2371 closest_track_num = int(self.
eval_js(JS_SCOPE_SELECTOR +
".GetJavaScriptTrack(" + str(position.y()) +
");"))
2372 closest_layer = closest_track_num + 1
2375 js_position = self.
eval_js(JS_SCOPE_SELECTOR +
".GetJavaScriptPosition(" + str(position.x()) +
");")
2378 possible_clips = Clip.filter(layer=closest_layer)
2379 for clip
in possible_clips:
2380 if js_position == 0
or (clip.data[
"position"] <= js_position <= clip.data[
"position"] + (
2381 clip.data[
"end"] - clip.data[
"start"])):
2382 log.info(
"Applying effect to clip")
2386 effect = openshot.EffectInfo().CreateEffect(name)
2389 effect.Id(
get_app().project.generate_id())
2390 effect_json = json.loads(effect.Json())
2393 clip.data[
"effects"].append(effect_json)
2396 self.
update_clip_data(clip.data, only_basic_props=
False, ignore_reader=
True)
2405 code = JS_SCOPE_SELECTOR +
".MoveItem(" + str(pos.x()) +
", " + str(pos.y()) +
", '" + self.
item_type +
"');" 2412 log.info(
'Dropping {} in timeline.'.
format(event.mimeData().text()))
2415 data = json.loads(event.mimeData().text())
2418 if self.
item_type in [
"clip",
"transition"]:
2434 log.info(
'redraw_audio_onTimeout')
2437 self.redraw_audio_timer.stop()
2440 cmd = JS_SCOPE_SELECTOR +
".reDrawAllAudioData();" 2441 self.page().mainFrame().evaluateJavaScript(cmd)
2448 cmd = JS_SCOPE_SELECTOR +
".ClearAllSelections();" 2449 self.page().mainFrame().evaluateJavaScript(cmd)
2456 cmd = JS_SCOPE_SELECTOR +
".SelectAll();" 2457 self.page().mainFrame().evaluateJavaScript(cmd)
2460 QWebView.__init__(self)
2462 self.setAcceptDrops(
True)
2469 get_app().updates.add_listener(self)
2472 self.setUrl(QUrl.fromLocalFile(QFileInfo(self.
html_path).absoluteFilePath()))
2475 self.page().mainFrame().javaScriptWindowObjectCleared.connect(self.
setup_js_data)
2478 window.sliderZoom.valueChanged.connect(self.
update_zoom)
2494 self.redraw_audio_timer.setInterval(300)
def update_clip_data(self, clip_json, only_basic_props=True, ignore_reader=False)
Create an updateAction and send it to the update manager.
def get_app()
Returns the current QApplication instance of OpenShot.
def Animate_Triggered(self, action, clip_ids, position="Entire Clip")
Callback for the animate context menus.
def Rotate_Triggered(self, action, clip_ids, position="Start of Clip")
Callback for rotate context menus.
def keyPressEvent(self, event)
Keypress callback for timeline.
def add_missing_transition(self, transition_json)
def dropEvent(self, event)
def PlayheadMoved(self, position_seconds, position_frames, time_code)
def Split_Audio_Triggered(self, action, clip_ids)
Callback for split audio context menus.
def Reverse_Transition_Triggered(self, tran_ids)
Callback for reversing a transition.
def ShowClipMenu(self, clip_id=None)
def Waveform_Ready(self, clip_id, audio_data)
Callback when audio waveform is ready.
def PreviewClipFrame(self, clip_id, frame_number)
def movePlayhead(self, position_frames)
Move the playhead since the position has changed inside OpenShot (probably due to the video player) ...
def ShowTransitionMenu(self, tran_id=None)
def Layout_Triggered(self, action, clip_ids)
Callback for the layout context menus.
def contextMenuEvent(self, event)
def Fade_Triggered(self, action, clip_ids, position="Entire Clip")
Callback for fade context menus.
A WebView QWidget used to load the Timeline.
def Align_Triggered(self, action, clip_ids, tran_ids)
Callback for alignment context menus.
def qt_log(self, message=None)
def addSelection(self, item_id, item_type, clear_existing=False)
Add the selected item to the current selection.
def ShowPlayheadMenu(self, position=None)
def redraw_audio_onTimeout(self)
Timer is ready to redraw audio (if any)
def changed(self, action)
def Hide_Waveform_Triggered(self, clip_ids)
Hide the waveform for the selected clip.
def Time_Triggered(self, action, clip_ids, speed="1X")
Callback for rotate context menus.
def ShowTrackMenu(self, layer_id=None)
def addClip(self, data, position)
def Show_Waveform_Triggered(self, clip_ids)
Show a waveform for the selected clip.
def addTransition(self, file_ids, position)
def resizeTimeline(self, new_duration)
Resize the duration of the timeline.
def UpdateClipThumbnail(self, clip_data)
Update the thumbnail image for clips.
def __init__(self, window)
def get_settings()
Get the current QApplication's settings instance.
def round_to_multiple(self, number, multiple)
Round this to the closest multiple of a given #.
def show_all_clips(self, clip, stretch=False)
Show all clips at the same time (arranged col by col, row by row)
def Volume_Triggered(self, action, clip_ids, position="Entire Clip")
Callback for volume context menus.
def wheelEvent(self, event)
def removeSelection(self, item_id, item_type)
Remove the selected clip from the selection.
def update_zoom(self, newValue)
def ClearAllSelections(self)
Clear all selections in JavaScript.
def ShowMarkerMenu(self, marker_id=None)
def dragMoveEvent(self, event)
copy_transition_clipboard
def dragEnterEvent(self, event)
def Copy_Triggered(self, action, clip_ids, tran_ids)
Callback for copy context menus.
def SelectAll(self)
Select all clips and transitions in JavaScript.
def SetSnappingMode(self, enable_snapping)
Enable / Disable snapping mode.
def addEffect(self, effect_names, position)
def update_transition_data(self, transition_json, only_basic_props=True)
Create an updateAction and send it to the update manager.
Interface for classes that listen for changes (insert, update, and delete).
def Slice_Triggered(self, action, clip_ids, trans_ids, playhead_position=0)
Callback for slice context menus.
def Paste_Triggered(self, action, position, layer_id, clip_ids, tran_ids)
Callback for paste context menus.
def ShowTimelineMenu(self, position, layer_id)
def ShowEffectMenu(self, effect_id=None)