Skip to content

Commit 09207c2

Browse files
authored
Merge branch 'develop' into develop
2 parents 4ad1d72 + e28ed36 commit 09207c2

11 files changed

+186
-79
lines changed

src/classes/exporters/final_cut_pro.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ def createEffect(xmldoc, name, node, points, scale):
5757
first_value = None
5858
for keyframeTime in sorted(keyframes.keys()):
5959
keyframeValue = keyframes.get(keyframeTime)
60-
if first_value == None:
60+
if first_value is None:
6161
first_value = keyframeValue
6262

6363
# Create keyframe element for each point
@@ -230,4 +230,6 @@ def export_xml():
230230
file.close()
231231
except IOError as inst:
232232
log.error("Error writing XML export: {}".format(str(inst)))
233-
233+
finally:
234+
# Free up DOM memory
235+
xmldoc.unlink()

src/classes/importers/final_cut_pro.py

+3
Original file line numberDiff line numberDiff line change
@@ -201,3 +201,6 @@ def import_xml():
201201
# Update the preview and reselect current frame in properties
202202
app.window.refreshFrameSignal.emit()
203203
app.window.propertyTableView.select_frame(app.window.preview_thread.player.Position())
204+
205+
# Free up DOM memory
206+
xmldoc.unlink()

src/classes/json_data.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
from classes.app import get_app
4040

4141
# Compiled path regex
42-
path_regex = re.compile(r'\"(image|path)\":.*?\"(.*?)\"')
42+
path_regex = re.compile(r'"(image|path)"\s*:\s*"(.*?)"')
4343
path_context = {}
4444

4545

src/settings/_default.settings

+24
Original file line numberDiff line numberDiff line change
@@ -791,6 +791,30 @@
791791
"value": "Ctrl+J",
792792
"type": "text"
793793
},
794+
{
795+
"category": "Keyboard",
796+
"title": "Slice Selected: Keep Both Sides",
797+
"restart": true,
798+
"setting": "sliceSelectedKeepBothSides",
799+
"value": "s",
800+
"type": "text"
801+
},
802+
{
803+
"category": "Keyboard",
804+
"title": "Slice Selected: Keep Left Side",
805+
"restart": true,
806+
"setting": "sliceSelectedKeepLeftSide",
807+
"value": "d",
808+
"type": "text"
809+
},
810+
{
811+
"category": "Keyboard",
812+
"title": "Slice Selected: Keep Right Side",
813+
"restart": true,
814+
"setting": "sliceSelectedKeepRightSide",
815+
"value": "a",
816+
"type": "text"
817+
},
794818
{
795819
"category": "Keyboard",
796820
"title": "Copy",

src/windows/export.py

+19-4
Original file line numberDiff line numberDiff line change
@@ -30,24 +30,31 @@
3030
import time
3131
import tempfile
3232

33+
import openshot
34+
3335
# Try to get the security-patched XML functions from defusedxml
3436
try:
3537
from defusedxml import minidom as xml
3638
except ImportError:
3739
from xml.dom import minidom as xml
3840

39-
from PyQt5.QtCore import *
40-
from PyQt5.QtWidgets import *
41+
from PyQt5.QtCore import Qt, QCoreApplication, QTimer, QSize
42+
from PyQt5.QtWidgets import (
43+
QMessageBox, QDialog, QFileDialog, QDialogButtonBox, QPushButton
44+
)
4145
from PyQt5.QtGui import QIcon
4246

4347
from classes import info
4448
from classes import ui_util
49+
from classes import settings
50+
from classes.logger import log
4551
from classes.app import get_app
46-
from classes.metrics import *
52+
from classes.metrics import track_metric_screen, track_metric_error
4753
from classes.query import File
4854

4955
import json
5056

57+
5158
class Export(QDialog):
5259
""" Export Dialog """
5360

@@ -236,6 +243,7 @@ def __init__(self):
236243
# ********* Simple Project Type **********
237244
# load the simple project type dropdown
238245
presets = []
246+
239247
for preset_folder in [info.EXPORT_PRESETS_PATH, info.USER_PRESETS_PATH]:
240248
for file in os.listdir(preset_folder):
241249
preset_path = os.path.join(preset_folder, file)
@@ -416,6 +424,9 @@ def cboSimpleProjectType_index_changed(self, widget, index):
416424
# This indicates an invalid Preset file - display an error and continue
417425
log.error("Failed to parse file '%s' as a preset: %s" % (preset_path, e))
418426

427+
# Free up DOM memory
428+
xmldoc.unlink()
429+
419430
# Add all targets for selected project type
420431
preset_index = 0
421432
selected_preset = 0
@@ -553,6 +564,7 @@ def cboSimpleTarget_index_changed(self, widget, index):
553564
self.txtAudioCodec.setText("ac3")
554565
else:
555566
# fallback audio codec
567+
556568
self.txtAudioCodec.setText(audio_codec_name)
557569

558570
layout_index = 0
@@ -561,11 +573,14 @@ def cboSimpleTarget_index_changed(self, widget, index):
561573
self.cboChannelLayout.setCurrentIndex(layout_index)
562574
break
563575
layout_index += 1
576+
577+
# Free up DOM memory
578+
xmldoc.unlink()
564579

565580
except ExpatError as e:
566581
# This indicates an invalid Preset file - display an error and continue
567582
log.error("Failed to parse file '%s' as a preset: %s" % (preset_path, e))
568-
583+
569584
# init the profiles combo
570585
for item in sorted(profiles_list):
571586
self.cboSimpleVideoProfile.addItem(self.getProfileName(self.getProfilePath(item)), self.getProfilePath(item))

src/windows/file_properties.py

+4-3
Original file line numberDiff line numberDiff line change
@@ -37,15 +37,16 @@
3737
from xml.dom import minidom as xml
3838

3939
from PyQt5.QtCore import *
40-
from PyQt5.QtWidgets import *
40+
from PyQt5.QtWidgets import QDialog, QFileDialog, QDialogButtonBox, QPushButton
4141

4242
# Python module for libopenshot (required video editing module installed separately)
4343
import openshot
4444

4545
from classes import info, ui_util, settings
4646
from classes.app import get_app
4747
from classes.logger import log
48-
from classes.metrics import *
48+
from classes.metrics import track_metric_screen
49+
4950

5051

5152
class FileProperties(QDialog):
@@ -190,7 +191,7 @@ def accept(self):
190191
self.file.data["name"] = self.txtFileName.text()
191192
self.file.data["tags"] = self.txtTags.text()
192193

193-
#experimental: update file path
194+
# experimental: update file path
194195
self.file.data["path"] = self.txtFilePath.text()
195196

196197
# Update Framerate

src/windows/main_window.py

+85-31
Original file line numberDiff line numberDiff line change
@@ -1290,13 +1290,50 @@ def actionAddMarker_trigger(self, event):
12901290
marker.data = {"position": position, "icon": "blue.png"}
12911291
marker.save()
12921292

1293-
def actionPreviousMarker_trigger(self, event):
1294-
log.info("actionPreviousMarker_trigger")
12951293

1296-
# Calculate current position (in seconds)
1297-
fps = get_app().project.get("fps")
1298-
fps_float = float(fps["num"]) / float(fps["den"])
1299-
current_position = (self.preview_thread.current_frame - 1) / fps_float
1294+
def findAllMarkerPositions(self):
1295+
"""Build and return a list of all seekable locations for the currently-selected timeline elements"""
1296+
1297+
def getTimelineObjectPositions(obj):
1298+
""" Add boundaries & all keyframes of a timeline object (clip, transition...) to all_marker_positions """
1299+
positions = []
1300+
1301+
fps = get_app().project.get("fps")
1302+
fps_float = float(fps["num"]) / float(fps["den"])
1303+
1304+
clip_start_time = obj.data["position"]
1305+
clip_orig_time = clip_start_time - obj.data["start"]
1306+
clip_stop_time = clip_orig_time + obj.data["end"]
1307+
1308+
# add clip boundaries
1309+
positions.append(clip_start_time)
1310+
positions.append(clip_stop_time)
1311+
1312+
# add all keyframes
1313+
for property in obj.data :
1314+
try :
1315+
for point in obj.data[property]["Points"] :
1316+
keyframe_time = (point["co"]["X"]-1)/fps_float - obj.data["start"] + obj.data["position"]
1317+
if keyframe_time > clip_start_time and keyframe_time < clip_stop_time :
1318+
positions.append(keyframe_time)
1319+
except (TypeError, KeyError):
1320+
pass
1321+
1322+
1323+
# Add all Effect keyframes
1324+
if "effects" in obj.data:
1325+
for effect_data in obj.data["effects"]:
1326+
for property in effect_data:
1327+
try:
1328+
for point in effect_data[property]["Points"]:
1329+
keyframe_time = (point["co"]["X"]-1)/fps_float + clip_orig_time
1330+
if keyframe_time > clip_start_time and keyframe_time < clip_stop_time:
1331+
positions.append(keyframe_time)
1332+
except (TypeError, KeyError):
1333+
pass
1334+
1335+
return positions
1336+
13001337
all_marker_positions = []
13011338

13021339
# Get list of marker and important positions (like selected clip bounds)
@@ -1308,16 +1345,28 @@ def actionPreviousMarker_trigger(self, event):
13081345
# Get selected object
13091346
selected_clip = Clip.get(id=clip_id)
13101347
if selected_clip:
1311-
all_marker_positions.append(selected_clip.data["position"])
1312-
all_marker_positions.append(selected_clip.data["position"] + (selected_clip.data["end"] - selected_clip.data["start"]))
1348+
all_marker_positions.extend(getTimelineObjectPositions(selected_clip))
13131349

13141350
# Loop through selected transitions (and add key positions)
13151351
for tran_id in self.selected_transitions:
13161352
# Get selected object
13171353
selected_tran = Transition.get(id=tran_id)
13181354
if selected_tran:
1319-
all_marker_positions.append(selected_tran.data["position"])
1320-
all_marker_positions.append(selected_tran.data["position"] + (selected_tran.data["end"] - selected_tran.data["start"]))
1355+
all_marker_positions.extend(getTimelineObjectPositions(selected_tran))
1356+
1357+
# remove duplicates
1358+
all_marker_positions = list(set(all_marker_positions))
1359+
1360+
return all_marker_positions
1361+
1362+
def actionPreviousMarker_trigger(self, event):
1363+
log.info("actionPreviousMarker_trigger")
1364+
1365+
# Calculate current position (in seconds)
1366+
fps = get_app().project.get("fps")
1367+
fps_float = float(fps["num"]) / float(fps["den"])
1368+
current_position = (self.preview_thread.current_frame - 1) / fps_float
1369+
all_marker_positions = self.findAllMarkerPositions()
13211370

13221371
# Loop through all markers, and find the closest one to the left
13231372
closest_position = None
@@ -1349,27 +1398,7 @@ def actionNextMarker_trigger(self, event):
13491398
fps = get_app().project.get("fps")
13501399
fps_float = float(fps["num"]) / float(fps["den"])
13511400
current_position = (self.preview_thread.current_frame - 1) / fps_float
1352-
all_marker_positions = []
1353-
1354-
# Get list of marker and important positions (like selected clip bounds)
1355-
for marker in Marker.filter():
1356-
all_marker_positions.append(marker.data["position"])
1357-
1358-
# Loop through selected clips (and add key positions)
1359-
for clip_id in self.selected_clips:
1360-
# Get selected object
1361-
selected_clip = Clip.get(id=clip_id)
1362-
if selected_clip:
1363-
all_marker_positions.append(selected_clip.data["position"])
1364-
all_marker_positions.append(selected_clip.data["position"] + (selected_clip.data["end"] - selected_clip.data["start"]))
1365-
1366-
# Loop through selected transitions (and add key positions)
1367-
for tran_id in self.selected_transitions:
1368-
# Get selected object
1369-
selected_tran = Transition.get(id=tran_id)
1370-
if selected_tran:
1371-
all_marker_positions.append(selected_tran.data["position"])
1372-
all_marker_positions.append(selected_tran.data["position"] + (selected_tran.data["end"] - selected_tran.data["start"]))
1401+
all_marker_positions = self.findAllMarkerPositions()
13731402

13741403
# Loop through all markers, and find the closest one to the right
13751404
closest_position = None
@@ -1590,6 +1619,31 @@ def keyPressEvent(self, event):
15901619
clip_ids = [c.id for c in intersecting_clips]
15911620
trans_ids = [t.id for t in intersecting_trans]
15921621
self.timeline.Slice_Triggered(2, clip_ids, trans_ids, playhead_position)
1622+
elif key.matches(self.getShortcutByName("sliceSelectedKeepBothSides")) == QKeySequence.ExactMatch:
1623+
intersecting_clips = Clip.filter(intersect=playhead_position)
1624+
intersecting_trans = Transition.filter(intersect=playhead_position)
1625+
if intersecting_clips or intersecting_trans:
1626+
# Get list of clip ids
1627+
clip_ids = [c.id for c in intersecting_clips if c.id in self.selected_clips]
1628+
trans_ids = [t.id for t in intersecting_trans if t.id in self.selected_transitions]
1629+
self.timeline.Slice_Triggered(0, clip_ids, trans_ids, playhead_position)
1630+
elif key.matches(self.getShortcutByName("sliceSelectedKeepLeftSide")) == QKeySequence.ExactMatch:
1631+
intersecting_clips = Clip.filter(intersect=playhead_position)
1632+
intersecting_trans = Transition.filter(intersect=playhead_position)
1633+
if intersecting_clips or intersecting_trans:
1634+
# Get list of clip ids
1635+
clip_ids = [c.id for c in intersecting_clips if c.id in self.selected_clips]
1636+
trans_ids = [t.id for t in intersecting_trans if t.id in self.selected_transitions]
1637+
self.timeline.Slice_Triggered(1, clip_ids, trans_ids, playhead_position)
1638+
elif key.matches(self.getShortcutByName("sliceSelectedKeepRightSide")) == QKeySequence.ExactMatch:
1639+
intersecting_clips = Clip.filter(intersect=playhead_position)
1640+
intersecting_trans = Transition.filter(intersect=playhead_position)
1641+
if intersecting_clips or intersecting_trans:
1642+
# Get list of ids that are also selected
1643+
clip_ids = [c.id for c in intersecting_clips if c.id in self.selected_clips]
1644+
trans_ids = [t.id for t in intersecting_trans if t.id in self.selected_transitions]
1645+
self.timeline.Slice_Triggered(2, clip_ids, trans_ids, playhead_position)
1646+
15931647
elif key.matches(self.getShortcutByName("copyAll")) == QKeySequence.ExactMatch:
15941648
self.timeline.Copy_Triggered(-1, self.selected_clips, self.selected_transitions)
15951649
elif key.matches(self.getShortcutByName("pasteAll")) == QKeySequence.ExactMatch:

0 commit comments

Comments
 (0)