Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender-addons.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNathan Lovato <nathan@gdquest.com>2020-02-09 04:59:08 +0300
committerNathan Lovato <nathan@gdquest.com>2020-02-13 18:19:35 +0300
commitcf9fde2568aa81197d0f36019ee429880430906d (patch)
treeb74875c507001fa41ca979bc689cfd4a0dee0f13 /power_sequencer
parent47d56e88240dc330e9173c668f1ee9fee39fb36c (diff)
power_sequencer: update to the latest master
This commit brings Power Sequencer to the current rolling version, that is commit 9562eb58d164e234f61225250d6ac5ca858ada7b on https://github.com/GDQuest/blender-power-sequencer/. I had already made an intermediate commit that added new features and fixed some bugs. This commit brings bug fixes and many quality of life improvements. - Human-readable changelog: https://github.com/GDQuest/blender-power-sequencer/blob/master/CHANGELOG.md - 1.4.0 release post: https://github.com/GDQuest/blender-power-sequencer/releases/tag/1.4.0 - All the commits since v1.3.0 (previous update + this one in this repository): https://github.com/GDQuest/blender-power-sequencer/compare/1.3.0...master
Diffstat (limited to 'power_sequencer')
-rwxr-xr-x[-rw-r--r--]power_sequencer/__init__.py63
-rw-r--r--power_sequencer/addon_preferences.py40
-rwxr-xr-x[-rw-r--r--]power_sequencer/operators/__init__.py154
-rw-r--r--power_sequencer/operators/audiosync/convert_and_trim.py1
-rw-r--r--power_sequencer/operators/audiosync/find_offset.py1
-rw-r--r--power_sequencer/operators/audiosync/mfcc/mfcc.py9
-rw-r--r--power_sequencer/operators/concatenate_strips.py58
-rw-r--r--power_sequencer/operators/crossfade_add.py3
-rw-r--r--power_sequencer/operators/crossfade_edit.py1
-rw-r--r--power_sequencer/operators/expand_to_surrounding_cuts.py21
-rw-r--r--power_sequencer/operators/fade_add.py2
-rw-r--r--power_sequencer/operators/fade_clear.py18
-rw-r--r--power_sequencer/operators/gap_remove.py3
-rw-r--r--power_sequencer/operators/grab.py4
-rw-r--r--power_sequencer/operators/import_local_footage.py7
-rw-r--r--power_sequencer/operators/marker_go_to_next.py74
-rw-r--r--power_sequencer/operators/markers_as_timecodes.py10
-rw-r--r--power_sequencer/operators/mouse_trim_instantly.py27
-rw-r--r--power_sequencer/operators/mouse_trim_modal.py24
-rw-r--r--power_sequencer/operators/playback_speed_set.py10
-rw-r--r--power_sequencer/operators/preview_to_selection.py7
-rw-r--r--power_sequencer/operators/render_presets/youtube_1080.py14
-rw-r--r--power_sequencer/operators/ripple_delete.py42
-rw-r--r--power_sequencer/operators/scene_merge_from.py2
-rw-r--r--power_sequencer/operators/scene_open_from_strip.py2
-rw-r--r--power_sequencer/operators/scene_rename_with_strip.py1
-rw-r--r--power_sequencer/operators/snap_selection.py11
-rw-r--r--power_sequencer/operators/space_sequences.py2
-rw-r--r--power_sequencer/operators/speed_up_movie_strip.py32
-rw-r--r--power_sequencer/operators/synchronize_titles.py112
-rw-r--r--power_sequencer/operators/transitions_remove.py1
-rw-r--r--power_sequencer/operators/trim_to_surrounding_cuts.py40
-rw-r--r--power_sequencer/operators/utils/doc.py1
-rw-r--r--power_sequencer/operators/utils/functions.py94
-rw-r--r--power_sequencer/operators/utils/global_settings.py2
-rw-r--r--power_sequencer/operators/utils/info_progress_bar.py1
-rw-r--r--power_sequencer/scripts/BPSProxy/bpsproxy/commands.py6
-rw-r--r--power_sequencer/scripts/BPSProxy/bpsproxy/config.py2
-rw-r--r--power_sequencer/scripts/BPSProxy/bpsproxy/utils.py4
-rw-r--r--power_sequencer/scripts/BPSProxy/setup.py13
-rw-r--r--power_sequencer/scripts/BPSRender/bpsrender/commands.py2
-rw-r--r--power_sequencer/scripts/BPSRender/bpsrender/helpers.py4
-rw-r--r--power_sequencer/scripts/BPSRender/bpsrender/setup.py4
-rw-r--r--power_sequencer/scripts/BPSRender/setup.py4
-rw-r--r--power_sequencer/tools/__init__.py33
-rw-r--r--power_sequencer/tools/trim.py45
-rw-r--r--power_sequencer/ui/menu_contextual.py4
-rwxr-xr-x[-rw-r--r--]power_sequencer/ui/menu_toolbar.py7
-rw-r--r--power_sequencer/utils/register_shortcuts.py7
49 files changed, 433 insertions, 596 deletions
diff --git a/power_sequencer/__init__.py b/power_sequencer/__init__.py
index a51f7342..670bcf41 100644..100755
--- a/power_sequencer/__init__.py
+++ b/power_sequencer/__init__.py
@@ -14,21 +14,23 @@
# You should have received a copy of the GNU General Public License along with Power Sequencer. If
# not, see <https://www.gnu.org/licenses/>.
#
+from typing import List, Tuple, Type
+
import bpy
from .addon_preferences import register_preferences, unregister_preferences
from .addon_properties import register_properties, unregister_properties
-from .operators import classes
-from .utils.register_shortcuts import register_shortcuts
from .handlers import register_handlers, unregister_handlers
-from .utils import addon_auto_imports
+from .operators import get_operator_classes
+from .tools import get_tool_classes
from .ui import register_ui, unregister_ui
-
+from .utils import addon_auto_imports
+from .utils.register_shortcuts import register_shortcuts
# load and reload submodules
##################################
modules = addon_auto_imports.setup_addon_modules(
- __path__, __name__, ignore_packages=[".utils", ".audiosync"], ignore_modules=[]
+ __path__, __name__, ignore_packages=[".utils", ".audiosync"]
)
@@ -38,43 +40,68 @@ bl_info = {
"author": "Nathan Lovato",
"version": (1, 4, 0),
"blender": (2, 80, 0),
- "location": "Video Tools",
+ "location": "Sequencer",
"tracker_url": "https://github.com/GDquest/Blender-power-sequencer/issues",
- "wiki_url": "https://www.youtube.com/playlist?list=PLhqJJNjsQ7KFjp88Cu57Zb9_wFt7nlkEI",
+ "wiki_url": "https://www.gdquest.com/docs/documentation/power-sequencer/",
"support": "COMMUNITY",
"category": "Sequencer",
}
-addon_keymaps = []
+# We use globals to cache loaded keymaps, operators, and tools
+addon_keymaps: List[Type] = []
+classes_operator: List[Type] = []
+classes_tool: List[Type] = []
def register():
global addon_keymaps
+ global classes_operator
+ global classes_tool
register_preferences()
register_properties()
register_handlers()
register_ui()
- for c in classes:
- bpy.utils.register_class(c)
-
- keymaps = register_shortcuts()
+ # Register operators
+ classes_operator = get_operator_classes()
+ for cls in classes_operator:
+ bpy.utils.register_class(cls)
+
+ # Register tools
+ version_min_toolbar = (2, 83, 0)
+ if is_blender_version_compatible(version_min_toolbar):
+ classes_tool = get_tool_classes()
+ last_tool = {"builtin.cut"}
+ for index, cls in enumerate(classes_tool):
+ bpy.utils.register_tool(cls, after=last_tool, separator=index == 0)
+ last_tool = {cls.bl_idname}
+
+ # Register keymaps
+ keymaps = register_shortcuts(classes_operator)
addon_keymaps += keymaps
print("Registered {} with {} modules".format(bl_info["name"], len(modules)))
def unregister():
+ """Unregister"""
global addon_keymaps
+ global classes_operator
+ global classes_tool
for km, kmi in addon_keymaps:
km.keymap_items.remove(kmi)
addon_keymaps.clear()
- for c in classes:
- bpy.utils.unregister_class(c)
+ for cls in classes_operator:
+ bpy.utils.unregister_class(cls)
+
+ version_min_toolbar = (2, 82, 0)
+ if is_blender_version_compatible(version_min_toolbar):
+ for cls in classes_tool:
+ bpy.utils.unregister_tool(cls)
unregister_ui()
unregister_preferences()
@@ -82,3 +109,11 @@ def unregister():
unregister_handlers()
print("Unregistered {}".format(bl_info["name"]))
+
+
+def is_blender_version_compatible(version: Tuple[int, int, int]) -> bool:
+ """Returns True if the `version` is greater or equal to the current Blender version.
+ Converts the versions to integers to compare them."""
+ version_int = version[0] * 1000 + version[1] * 10 + version[2]
+ blender_version_int = bpy.app.version[0] * 1000 + bpy.app.version[1] * 10 + bpy.app.version[2]
+ return blender_version_int >= version_int
diff --git a/power_sequencer/addon_preferences.py b/power_sequencer/addon_preferences.py
index 0e8e7d71..fcc67e9c 100644
--- a/power_sequencer/addon_preferences.py
+++ b/power_sequencer/addon_preferences.py
@@ -17,12 +17,16 @@
"""
Add-on preferences and interface in the Blender preferences window.
"""
+import subprocess
+
import bpy
+from bpy.props import BoolProperty, StringProperty
def get_preferences(context):
return context.preferences.addons[__package__].preferences
+
class PowerSequencerPreferences(bpy.types.AddonPreferences):
bl_idname = __package__
@@ -31,6 +35,32 @@ class PowerSequencerPreferences(bpy.types.AddonPreferences):
proxy_75: bpy.props.BoolProperty(name="75%", default=False)
proxy_100: bpy.props.BoolProperty(name="100%", default=False)
+ # Code adapted from Krzysztof Trzciński's work
+ ffmpeg_executable: StringProperty(
+ name="Path to ffmpeg executable",
+ default="",
+ update=lambda self, context: self.update_ffmpeg_executable(context),
+ subtype="FILE_PATH",
+ )
+ ffmpeg_status: StringProperty(default="")
+ ffmpeg_is_executable_valid: BoolProperty(default=False)
+
+ def update_ffmpeg_executable(self, context):
+ error_message, info = self._try_run_ffmpeg(self.ffmpeg_executable)
+ self.ffmpeg_is_executable_valid = error_message == ""
+ self.ffmpeg_status = error_message if error_message != "" else info
+
+ def _try_run_ffmpeg(self, path):
+ """Runs ffmpeg -version, and returns an error message if it failed"""
+ error_message, info = "", ""
+ try:
+ info: str = subprocess.check_output([path, "-version"]).decode("utf-8")
+ info = info[:info.find("Copyright")]
+ print(info)
+ except (OSError, subprocess.CalledProcessError):
+ error_message = "Path `{}` is not a valid ffmpeg executable".format(path)
+ return error_message, info
+
def draw(self, context):
layout = self.layout
@@ -42,6 +72,16 @@ class PowerSequencerPreferences(bpy.types.AddonPreferences):
row.prop(self, "proxy_75")
row.prop(self, "proxy_100")
+ text = [
+ "(Optional) FFMpeg executable to use for multithread renders and proxy generation. "
+ "Use this to render with a version of ffmpeg that's not on your system's PATH variable."
+ ]
+ for line in text:
+ layout.label(text=line)
+ layout.prop(self, "ffmpeg_executable")
+ icon = "INFO" if self.ffmpeg_is_executable_valid else "ERROR"
+ layout.label(text=self.ffmpeg_status, icon=icon)
+
register_preferences, unregister_preferences = bpy.utils.register_classes_factory(
[PowerSequencerPreferences]
diff --git a/power_sequencer/operators/__init__.py b/power_sequencer/operators/__init__.py
index 93519426..535b29cd 100644..100755
--- a/power_sequencer/operators/__init__.py
+++ b/power_sequencer/operators/__init__.py
@@ -14,143 +14,25 @@
# You should have received a copy of the GNU General Public License along with Power Sequencer. If
# not, see <https://www.gnu.org/licenses/>.
#
-from .speed_up_movie_strip import POWER_SEQUENCER_OT_speed_up_movie_strip
-from .align_audios import POWER_SEQUENCER_OT_align_audios
-from .playback_speed_set import POWER_SEQUENCER_OT_playback_speed_set
-from .channel_offset import POWER_SEQUENCER_OT_channel_offset
-from .concatenate_strips import POWER_SEQUENCER_OT_concatenate_strips
-from .copy_selected_sequences import POWER_SEQUENCER_OT_copy_selected_sequences
-from .crossfade_add import POWER_SEQUENCER_OT_crossfade_add
-from .crossfade_edit import POWER_SEQUENCER_OT_crossfade_edit
-from .transitions_remove import POWER_SEQUENCER_OT_transitions_remove
-from .cut_strips_under_cursor import POWER_SEQUENCER_OT_split_strips_under_cursor
-from .playback_speed_decrease import POWER_SEQUENCER_OT_playback_speed_decrease
-from .delete_direct import POWER_SEQUENCER_OT_delete_direct
-from .deselect_all_left_or_right import POWER_SEQUENCER_OT_deselect_all_strips_left_or_right
-from .deselect_handles_and_grab import POWER_SEQUENCER_OT_deselect_handles_and_grab
-from .duplicate_move import POWER_SEQUENCER_OT_duplicate_move
-from .expand_to_surrounding_cuts import POWER_SEQUENCER_OT_expand_to_surrounding_cuts
-from .fade_add import POWER_SEQUENCER_OT_fade_add
-from .fade_clear import POWER_SEQUENCER_OT_fade_clear
-from .grab_closest_handle_or_cut import POWER_SEQUENCER_OT_grab_closest_cut
-from .grab import POWER_SEQUENCER_OT_grab
-from .grab_sequence_handles import POWER_SEQUENCER_OT_grab_sequence_handles
-from .import_local_footage import POWER_SEQUENCER_OT_import_local_footage
-from .playback_speed_increase import POWER_SEQUENCER_OT_playback_speed_increase
-from .jump_time_offset import POWER_SEQUENCER_OT_jump_time_offset
-from .jump_to_cut import POWER_SEQUENCER_OT_jump_to_cut
-from .make_still_image import POWER_SEQUENCER_OT_make_still_image
-from .marker_delete_closest import POWER_SEQUENCER_OT_marker_delete_closest
-from .marker_delete_direct import POWER_SEQUENCER_OT_marker_delete_direct
-from .marker_go_to_next import POWER_SEQUENCER_OT_marker_go_to_next
-from .markers_as_timecodes import POWER_SEQUENCER_OT_copy_markers_as_timecodes
-from .markers_create_from_selected import POWER_SEQUENCER_OT_markers_create_from_selected_strips
-from .marker_snap_to_cursor import POWER_SEQUENCER_OT_marker_snap_to_cursor
-from .markers_snap_matching_strips import POWER_SEQUENCER_OT_markers_snap_matching_strips
-from .meta_resize_to_content import POWER_SEQUENCER_OT_meta_resize_to_content
-from .meta_ungroup_and_trim import POWER_SEQUENCER_OT_meta_ungroup_and_trim
-from .meta_trim_content_to_bounds import POWER_SEQUENCER_OT_meta_trim_content_to_bounds
-from .mouse_trim_modal import POWER_SEQUENCER_OT_mouse_trim
-from .space_sequences import POWER_SEQUENCER_OT_space_sequences
-from .mouse_toggle_mute import POWER_SEQUENCER_OT_mouse_toggle_mute
-from .mouse_trim_instantly import POWER_SEQUENCER_OT_mouse_trim_instantly
-from .open_project_directory import POWER_SEQUENCER_OT_open_project_directory
-from .preview_closest_cut import POWER_SEQUENCER_OT_preview_closest_cut
-from .preview_to_selection import POWER_SEQUENCER_OT_preview_to_selection
-from .gap_remove import POWER_SEQUENCER_OT_gap_remove
-from .scene_rename_with_strip import POWER_SEQUENCER_OT_scene_rename_with_strip
-from .render_apply_preset import POWER_SEQUENCER_OT_render_apply_preset
-from .ripple_delete import POWER_SEQUENCER_OT_ripple_delete
-from .save_direct import POWER_SEQUENCER_OT_save_direct
-from .scene_create_from_selection import POWER_SEQUENCER_OT_scene_create_from_selection
-from .scene_cycle import POWER_SEQUENCER_OT_scene_cycle
-from .select_closest_to_mouse import POWER_SEQUENCER_OT_select_closest_to_mouse
-from .select_linked_strips import POWER_SEQUENCER_OT_select_linked_strips
-from .select_linked_effect import POWER_SEQUENCER_OT_select_linked_effect
-from .select_related_strips import POWER_SEQUENCER_OT_select_related_strips
-from .select_strips_under_cursor import POWER_SEQUENCER_OT_select_strips_under_cursor
-from .markers_set_preview_in_between import POWER_SEQUENCER_OT_set_preview_between_markers
-from .set_timeline_range import POWER_SEQUENCER_OT_set_timeline_range
-from .trim_left_or_right_handles import POWER_SEQUENCER_OT_trim_left_or_right_handles
-from .snap import POWER_SEQUENCER_OT_snap
-from .snap_selection import POWER_SEQUENCER_OT_snap_selection
-from .speed_remove_effect import POWER_SEQUENCER_OT_speed_remove_effect
-from .swap_strips import POWER_SEQUENCER_OT_swap_strips
-from .select_all_left_or_right import POWER_SEQUENCER_OT_select_all_left_or_right
-from .toggle_selected_mute import POWER_SEQUENCER_OT_toggle_selected_mute
-from .toggle_waveforms import POWER_SEQUENCER_OT_toggle_waveforms
-from .trim_three_point_edit import POWER_SEQUENCER_OT_trim_three_point_edit
-from .trim_to_surrounding_cuts import POWER_SEQUENCER_OT_trim_to_surrounding_cuts
+import importlib
+import os
+
+
+def get_operator_classes():
+ """Returns the list of operators in the add-on"""
+ this_file = os.path.dirname(__file__)
+ module_files = [
+ f for f in os.listdir(this_file) if f.endswith(".py") and not f.startswith("__init__")
+ ]
+ module_paths = ["." + os.path.splitext(f)[0] for f in module_files]
+ classes = []
+ print(__name__)
+ for path in module_paths:
+ module = importlib.import_module(path, package="blender_power_sequencer.operators")
+ operator_names = [entry for entry in dir(module) if entry.startswith("POWER_SEQUENCER_OT")]
+ classes.extend([getattr(module, name) for name in operator_names])
+ return classes
-classes = [
- POWER_SEQUENCER_OT_speed_up_movie_strip,
- POWER_SEQUENCER_OT_align_audios,
- POWER_SEQUENCER_OT_playback_speed_set,
- POWER_SEQUENCER_OT_channel_offset,
- POWER_SEQUENCER_OT_concatenate_strips,
- POWER_SEQUENCER_OT_copy_selected_sequences,
- POWER_SEQUENCER_OT_crossfade_add,
- POWER_SEQUENCER_OT_crossfade_edit,
- POWER_SEQUENCER_OT_transitions_remove,
- POWER_SEQUENCER_OT_split_strips_under_cursor,
- POWER_SEQUENCER_OT_playback_speed_decrease,
- POWER_SEQUENCER_OT_delete_direct,
- POWER_SEQUENCER_OT_deselect_all_strips_left_or_right,
- POWER_SEQUENCER_OT_deselect_handles_and_grab,
- POWER_SEQUENCER_OT_duplicate_move,
- POWER_SEQUENCER_OT_expand_to_surrounding_cuts,
- POWER_SEQUENCER_OT_fade_add,
- POWER_SEQUENCER_OT_fade_clear,
- POWER_SEQUENCER_OT_grab_closest_cut,
- POWER_SEQUENCER_OT_grab,
- POWER_SEQUENCER_OT_grab_sequence_handles,
- POWER_SEQUENCER_OT_import_local_footage,
- POWER_SEQUENCER_OT_playback_speed_increase,
- POWER_SEQUENCER_OT_jump_time_offset,
- POWER_SEQUENCER_OT_jump_to_cut,
- POWER_SEQUENCER_OT_make_still_image,
- POWER_SEQUENCER_OT_marker_delete_closest,
- POWER_SEQUENCER_OT_marker_delete_direct,
- POWER_SEQUENCER_OT_marker_go_to_next,
- POWER_SEQUENCER_OT_copy_markers_as_timecodes,
- POWER_SEQUENCER_OT_markers_create_from_selected_strips,
- POWER_SEQUENCER_OT_marker_snap_to_cursor,
- POWER_SEQUENCER_OT_markers_snap_matching_strips,
- POWER_SEQUENCER_OT_meta_resize_to_content,
- POWER_SEQUENCER_OT_meta_ungroup_and_trim,
- POWER_SEQUENCER_OT_meta_trim_content_to_bounds,
- POWER_SEQUENCER_OT_mouse_trim,
- POWER_SEQUENCER_OT_space_sequences,
- POWER_SEQUENCER_OT_mouse_toggle_mute,
- POWER_SEQUENCER_OT_mouse_trim_instantly,
- POWER_SEQUENCER_OT_open_project_directory,
- POWER_SEQUENCER_OT_preview_closest_cut,
- POWER_SEQUENCER_OT_preview_to_selection,
- POWER_SEQUENCER_OT_gap_remove,
- POWER_SEQUENCER_OT_scene_rename_with_strip,
- POWER_SEQUENCER_OT_render_apply_preset,
- POWER_SEQUENCER_OT_ripple_delete,
- POWER_SEQUENCER_OT_save_direct,
- POWER_SEQUENCER_OT_scene_create_from_selection,
- POWER_SEQUENCER_OT_scene_cycle,
- POWER_SEQUENCER_OT_select_closest_to_mouse,
- POWER_SEQUENCER_OT_select_linked_strips,
- POWER_SEQUENCER_OT_select_linked_effect,
- POWER_SEQUENCER_OT_select_related_strips,
- POWER_SEQUENCER_OT_select_strips_under_cursor,
- POWER_SEQUENCER_OT_set_preview_between_markers,
- POWER_SEQUENCER_OT_set_timeline_range,
- POWER_SEQUENCER_OT_trim_left_or_right_handles,
- POWER_SEQUENCER_OT_snap,
- POWER_SEQUENCER_OT_snap_selection,
- POWER_SEQUENCER_OT_speed_remove_effect,
- POWER_SEQUENCER_OT_swap_strips,
- POWER_SEQUENCER_OT_toggle_selected_mute,
- POWER_SEQUENCER_OT_toggle_waveforms,
- POWER_SEQUENCER_OT_trim_three_point_edit,
- POWER_SEQUENCER_OT_select_all_left_or_right,
- POWER_SEQUENCER_OT_trim_to_surrounding_cuts,
-]
doc = {
"sequencer.refresh_all": {
diff --git a/power_sequencer/operators/audiosync/convert_and_trim.py b/power_sequencer/operators/audiosync/convert_and_trim.py
index 1ce1a4c1..859186d8 100644
--- a/power_sequencer/operators/audiosync/convert_and_trim.py
+++ b/power_sequencer/operators/audiosync/convert_and_trim.py
@@ -14,7 +14,6 @@
# You should have received a copy of the GNU General Public License along with Power Sequencer. If
# not, see <https://www.gnu.org/licenses/>.
#
-import os
import subprocess
import tempfile
diff --git a/power_sequencer/operators/audiosync/find_offset.py b/power_sequencer/operators/audiosync/find_offset.py
index 90153e1a..35b17a14 100644
--- a/power_sequencer/operators/audiosync/find_offset.py
+++ b/power_sequencer/operators/audiosync/find_offset.py
@@ -18,7 +18,6 @@
This code is an adaptation of 'audio-offset-finder' by BBC
"""
import os
-import warnings
import numpy as np
from .mfcc import mfcc
diff --git a/power_sequencer/operators/audiosync/mfcc/mfcc.py b/power_sequencer/operators/audiosync/mfcc/mfcc.py
index 5d9c9467..7f90a5b1 100644
--- a/power_sequencer/operators/audiosync/mfcc/mfcc.py
+++ b/power_sequencer/operators/audiosync/mfcc/mfcc.py
@@ -16,6 +16,10 @@
#
import numpy as np
+from scipy.signal import hamming, lfilter
+from scipy.fftpack import fft
+from scipy.fftpack.realtransforms import dct
+
from .trfbank import trfbank
from .segment_axis import segment_axis
@@ -52,10 +56,6 @@ def mfcc(input, nwin=256, nfft=512, fs=16000, nceps=13):
spoken sentences", IEEE Trans. Acoustics. Speech, Signal Proc.
ASSP-28 (4): 357-366, August 1980."""
- from scipy.signal import hamming, lfilter
- from scipy.fftpack import fft
- from scipy.fftpack.realtransforms import dct
-
# MFCC parameters: taken from auditory toolbox
over = nwin - 160
# Pre-emphasis factor (to take into account the -6dB/octave rolloff of the
@@ -70,7 +70,6 @@ def mfcc(input, nwin=256, nfft=512, fs=16000, nceps=13):
nlinfil = 13
nlogfil = 27
- nfil = nlinfil + nlogfil
w = hamming(nwin, sym=0)
diff --git a/power_sequencer/operators/concatenate_strips.py b/power_sequencer/operators/concatenate_strips.py
index 4ef2407d..11dff248 100644
--- a/power_sequencer/operators/concatenate_strips.py
+++ b/power_sequencer/operators/concatenate_strips.py
@@ -18,7 +18,11 @@ import bpy
from operator import attrgetter
from .utils.global_settings import SequenceTypes
-from .utils.functions import find_sequences_after, get_mouse_frame_and_channel, sequencer_workaround_2_80_audio_bug
+from .utils.functions import (
+ find_sequences_after,
+ get_mouse_frame_and_channel,
+ ripple_move,
+)
from .utils.doc import doc_name, doc_idname, doc_brief, doc_description
@@ -45,22 +49,22 @@ class POWER_SEQUENCER_OT_concatenate_strips(bpy.types.Operator):
"shortcuts": [
(
{"type": "C", "value": "PRESS"},
- {"concatenate_all": False, "to_left": True},
+ {"concatenate_all": False, "is_towards_left": True},
("Concatenate and select the next strip in the channel"),
),
(
{"type": "C", "value": "PRESS", "shift": True},
- {"concatenate_all": True, "to_left": True},
+ {"concatenate_all": True, "is_towards_left": True},
"Concatenate all strips in selected channels",
),
(
{"type": "C", "value": "PRESS", "alt": True},
- {"concatenate_all": False, "to_left": False},
+ {"concatenate_all": False, "is_towards_left": False},
("Concatenate and select the previous strip in the channel towards the right"),
),
(
{"type": "C", "value": "PRESS", "shift": True, "alt": True},
- {"concatenate_all": True, "to_left": False},
+ {"concatenate_all": True, "is_towards_left": False},
"Shift Alt C; Concatenate all strips in channel towards the right",
),
],
@@ -76,11 +80,16 @@ class POWER_SEQUENCER_OT_concatenate_strips(bpy.types.Operator):
description=("If only one strip selected, concatenate" " the entire channel"),
default=False,
)
- to_left: bpy.props.BoolProperty(
+ is_towards_left: bpy.props.BoolProperty(
name="To Left",
description="Concatenate strips moving them back in time (default) or forward in time",
default=True,
)
+ do_ripple: bpy.props.BoolProperty(
+ name="Ripple Edit",
+ description="Ripple the time offset to strips after the concatenated one",
+ default=False,
+ )
@classmethod
def poll(cls, context):
@@ -96,11 +105,12 @@ class POWER_SEQUENCER_OT_concatenate_strips(bpy.types.Operator):
selection = context.selected_sequences
channels = {s.channel for s in selection}
- if len(selection) == len(channels):
+ is_one_strip_per_channel = len(selection) == len(channels)
+ if is_one_strip_per_channel:
for s in selection:
candidates = (
find_sequences_before(context, s)
- if not self.to_left
+ if not self.is_towards_left
else find_sequences_after(context, s)
)
to_concatenate = [
@@ -110,45 +120,53 @@ class POWER_SEQUENCER_OT_concatenate_strips(bpy.types.Operator):
and not strip.lock
and strip.type in SequenceTypes.CUTABLE
]
- self.concatenate(s, to_concatenate)
+ if not to_concatenate:
+ continue
+ self.concatenate(context, s, to_concatenate)
else:
for channel in channels:
to_concatenate = [s for s in selection if s.channel == channel]
strip_target = (
min(to_concatenate, key=lambda s: s.frame_final_start)
- if self.to_left
+ if self.is_towards_left
else max(to_concatenate, key=lambda s: s.frame_final_start)
)
to_concatenate.remove(strip_target)
- self.concatenate(strip_target, to_concatenate, force_all=True)
+ self.concatenate(context, strip_target, to_concatenate, force_all=True)
- sequencer_workaround_2_80_audio_bug(context)
return {"FINISHED"}
- def concatenate(self, strip_target, sequences, force_all=False):
+ def concatenate(self, context, strip_target, sequences, force_all=False):
to_concatenate = sorted(sequences, key=attrgetter("frame_final_start"))
- to_concatenate = list(reversed(to_concatenate)) if not self.to_left else to_concatenate
+ to_concatenate = (
+ list(reversed(to_concatenate)) if not self.is_towards_left else to_concatenate
+ )
to_concatenate = (
[to_concatenate[0]] if not (self.concatenate_all or force_all) else to_concatenate
)
- attribute_target = "frame_final_end" if self.to_left else "frame_final_start"
- attribute_concat = "frame_final_start" if self.to_left else "frame_final_end"
+ attribute_target = "frame_final_end" if self.is_towards_left else "frame_final_start"
+ attribute_concat = "frame_final_start" if self.is_towards_left else "frame_final_end"
concatenate_start = getattr(strip_target, attribute_target)
last_gap = 0
for s in to_concatenate:
if isinstance(s, bpy.types.EffectSequence):
concatenate_start = (
- s.frame_final_end - last_gap if self.to_left else s.frame_final_start - last_gap
+ s.frame_final_end - last_gap
+ if self.is_towards_left
+ else s.frame_final_start - last_gap
)
continue
concat_strip_frame = getattr(s, attribute_concat)
gap = concat_strip_frame - concatenate_start
- s.frame_start -= gap
- concatenate_start = s.frame_final_end if self.to_left else s.frame_final_start
+ if self.do_ripple and self.is_towards_left:
+ ripple_move(context, [s], -gap)
+ else:
+ s.frame_start -= gap
+ concatenate_start = s.frame_final_end if self.is_towards_left else s.frame_final_start
last_gap = gap
- if not self.concatenate_all:
+ if not (self.concatenate_all or force_all):
strip_target.select = False
to_concatenate[0].select = True
diff --git a/power_sequencer/operators/crossfade_add.py b/power_sequencer/operators/crossfade_add.py
index 31f97af2..11558e56 100644
--- a/power_sequencer/operators/crossfade_add.py
+++ b/power_sequencer/operators/crossfade_add.py
@@ -26,7 +26,6 @@ class POWER_SEQUENCER_OT_crossfade_add(bpy.types.Operator):
"""
*brief* Adds cross fade between selected sequence and the closest sequence to its right
-
Based on the active strip, finds the closest next sequence of a similar type, moves it
so it overlaps the active strip, and adds a gamma cross effect between them. Works with
MOVIE, IMAGE and META strips
@@ -66,7 +65,7 @@ class POWER_SEQUENCER_OT_crossfade_add(bpy.types.Operator):
sorted_selection = sorted(context.selected_sequences, key=lambda s: s.frame_final_start)
for s in sorted_selection:
s_next = self.get_next_sequence_after(context, s)
- s_to_offset = s_next if s_next.type not in SequenceTypes.EFFECT else s_next.input_1
+ s_to_offset = s_next.input_1 if hasattr(s_next, "input_1") else s_next
if self.auto_move_strip:
offset = s_to_offset.frame_final_start - s.frame_final_end
diff --git a/power_sequencer/operators/crossfade_edit.py b/power_sequencer/operators/crossfade_edit.py
index 127ab603..553251c9 100644
--- a/power_sequencer/operators/crossfade_edit.py
+++ b/power_sequencer/operators/crossfade_edit.py
@@ -48,7 +48,6 @@ class POWER_SEQUENCER_OT_crossfade_edit(bpy.types.Operator):
def poll(cls, context):
return context.scene.sequence_editor.active_strip and context.selected_sequences
-
def execute(self, context):
active = context.scene.sequence_editor.active_strip
if active.type not in self.crossfade_types:
diff --git a/power_sequencer/operators/expand_to_surrounding_cuts.py b/power_sequencer/operators/expand_to_surrounding_cuts.py
index 34507da8..852268bb 100644
--- a/power_sequencer/operators/expand_to_surrounding_cuts.py
+++ b/power_sequencer/operators/expand_to_surrounding_cuts.py
@@ -15,11 +15,9 @@
# not, see <https://www.gnu.org/licenses/>.
#
import bpy
-from math import floor
-from .utils.functions import convert_duration_to_frames
-from .utils.doc import doc_name, doc_idname, doc_brief, doc_description
from .utils.functions import slice_selection
+from .utils.doc import doc_name, doc_idname, doc_brief, doc_description
class POWER_SEQUENCER_OT_expand_to_surrounding_cuts(bpy.types.Operator):
@@ -63,16 +61,15 @@ class POWER_SEQUENCER_OT_expand_to_surrounding_cuts(bpy.types.Operator):
def invoke(self, context, event):
sequence_blocks = slice_selection(context, context.selected_sequences)
for sequences in sequence_blocks:
- sequences_frame_start = min(sequences, key=lambda s: s.frame_final_start).frame_final_start
+ sequences_frame_start = min(
+ sequences, key=lambda s: s.frame_final_start
+ ).frame_final_start
sequences_frame_end = max(sequences, key=lambda s: s.frame_final_end).frame_final_end
frame_left, frame_right = find_closest_cuts(
context, sequences_frame_start, sequences_frame_end
)
- if (
- sequences_frame_start == frame_left
- and sequences_frame_end == frame_right
- ):
+ if sequences_frame_start == frame_left and sequences_frame_end == frame_right:
continue
to_extend_left = [s for s in sequences if s.frame_final_start == sequences_frame_start]
@@ -80,15 +77,11 @@ class POWER_SEQUENCER_OT_expand_to_surrounding_cuts(bpy.types.Operator):
for s in to_extend_left:
s.frame_final_start = (
- frame_left
- if frame_left < sequences_frame_start
- else sequences_frame_start
+ frame_left if frame_left < sequences_frame_start else sequences_frame_start
)
for s in to_extend_right:
s.frame_final_end = (
- frame_right
- if frame_right > sequences_frame_end
- else sequences_frame_end
+ frame_right if frame_right > sequences_frame_end else sequences_frame_end
)
return {"FINISHED"}
diff --git a/power_sequencer/operators/fade_add.py b/power_sequencer/operators/fade_add.py
index 2053a77c..d72f034d 100644
--- a/power_sequencer/operators/fade_add.py
+++ b/power_sequencer/operators/fade_add.py
@@ -27,7 +27,7 @@ class POWER_SEQUENCER_OT_fade_add(bpy.types.Operator):
Fade options:
- In, Out, In and Out create a fade animation of the given duration from
- the start of the sequence, to the end of the sequence, or on both sides
+ the start of the sequence, to the end of the sequence, or on boths sides
- From playhead: the fade animation goes from the start of sequences under the playhead to the playhead
- To playhead: the fade animation goes from the playhead to the end of sequences under the playhead
diff --git a/power_sequencer/operators/fade_clear.py b/power_sequencer/operators/fade_clear.py
index d7abb4d7..ebccbe23 100644
--- a/power_sequencer/operators/fade_clear.py
+++ b/power_sequencer/operators/fade_clear.py
@@ -17,7 +17,6 @@
import bpy
from .utils.doc import doc_name, doc_idname, doc_brief, doc_description
-from .utils.global_settings import SequenceTypes
class POWER_SEQUENCER_OT_fade_clear(bpy.types.Operator):
@@ -51,14 +50,15 @@ class POWER_SEQUENCER_OT_fade_clear(bpy.types.Operator):
for sequence in context.selected_sequences:
animated_property = "volume" if hasattr(sequence, "volume") else "blend_alpha"
- for curve in fcurves:
- if not curve.data_path.endswith(animated_property):
- continue
- # Ensure the fcurve corresponds to the selected sequence
- if sequence == eval(
- "bpy.context.scene." + curve.data_path.replace("." + animated_property, "")
- ):
- fcurves.remove(curve)
+ data_path = sequence.path_from_id() + "." + animated_property
+ fcurve_map = {
+ curve.data_path: curve
+ for curve in fcurves
+ if curve.data_path.startswith("sequence_editor.sequences_all")
+ }
+ curve = fcurve_map.get(data_path)
+ if curve:
+ fcurves.remove(curve)
setattr(sequence, animated_property, 1.0)
return {"FINISHED"}
diff --git a/power_sequencer/operators/gap_remove.py b/power_sequencer/operators/gap_remove.py
index 8ef00b6b..88a3484a 100644
--- a/power_sequencer/operators/gap_remove.py
+++ b/power_sequencer/operators/gap_remove.py
@@ -17,8 +17,8 @@
import bpy
from operator import attrgetter
-from .utils.functions import slice_selection, sequencer_workaround_2_80_audio_bug
from .utils.doc import doc_name, doc_idname, doc_brief, doc_description
+from .utils.functions import slice_selection
class POWER_SEQUENCER_OT_gap_remove(bpy.types.Operator):
@@ -84,7 +84,6 @@ class POWER_SEQUENCER_OT_gap_remove(bpy.types.Operator):
)
self.gaps_remove(context, blocks_after_gap, gap_frame)
- sequencer_workaround_2_80_audio_bug(context)
return {"FINISHED"}
def find_gap_frame(self, context, frame, sorted_sequences):
diff --git a/power_sequencer/operators/grab.py b/power_sequencer/operators/grab.py
index af8109ff..dd198668 100644
--- a/power_sequencer/operators/grab.py
+++ b/power_sequencer/operators/grab.py
@@ -56,11 +56,11 @@ class POWER_SEQUENCER_OT_grab(bpy.types.Operator):
def execute(self, context):
if len(context.selected_sequences) == 0:
- return {'FINISHED'}
+ return {"FINISHED"}
strip = context.selected_sequences[0]
if len(context.selected_sequences) == 1 and strip.type in SequenceTypes.TRANSITION:
context.scene.sequence_editor.active_strip = strip
return bpy.ops.power_sequencer.crossfade_edit()
else:
- return bpy.ops.transform.seq_slide('INVOKE_DEFAULT')
+ return bpy.ops.transform.seq_slide("INVOKE_DEFAULT")
diff --git a/power_sequencer/operators/import_local_footage.py b/power_sequencer/operators/import_local_footage.py
index 1f13b15d..5ffee493 100644
--- a/power_sequencer/operators/import_local_footage.py
+++ b/power_sequencer/operators/import_local_footage.py
@@ -17,7 +17,6 @@
import json
import os
import re
-from operator import attrgetter
import bpy
@@ -91,7 +90,9 @@ class POWER_SEQUENCER_OT_import_local_footage(bpy.types.Operator):
self.directory = os.path.split(bpy.data.filepath)[0]
filepaths = self.find_local_footage_files()
- files_to_import = [os.path.join(self.directory, f) for f in self.find_new_files_to_import(filepaths)]
+ files_to_import = [
+ os.path.join(self.directory, f) for f in self.find_new_files_to_import(filepaths)
+ ]
print(files_to_import)
if not files_to_import:
self.report({"INFO"}, "No new files to import found")
@@ -162,7 +163,7 @@ class POWER_SEQUENCER_OT_import_local_footage(bpy.types.Operator):
Creates a new text data block that contains an empty json list, renames it to `name` and
returns it
"""
- re_text = re.compile(r"^Text.[0-9]{3}$")
+ re.compile(r"^Text.[0-9]{3}$")
bpy.ops.text.new()
diff --git a/power_sequencer/operators/marker_go_to_next.py b/power_sequencer/operators/marker_go_to_next.py
deleted file mode 100644
index 014091e5..00000000
--- a/power_sequencer/operators/marker_go_to_next.py
+++ /dev/null
@@ -1,74 +0,0 @@
-#
-# Copyright (C) 2016-2019 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-#
-# This file is part of Power Sequencer.
-#
-# Power Sequencer is free software: you can redistribute it and/or modify it under the terms of the
-# GNU General Public License as published by the Free Software Foundation, either version 3 of the
-# License, or (at your option) any later version.
-#
-# Power Sequencer is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
-# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along with Power Sequencer. If
-# not, see <https://www.gnu.org/licenses/>.
-#
-import bpy
-
-from .utils.functions import find_neighboring_markers
-from .utils.doc import doc_name, doc_idname, doc_brief, doc_description
-
-
-class POWER_SEQUENCER_OT_marker_go_to_next(bpy.types.Operator):
- """
- Moves the time cursor to the next marker
- """
-
- doc = {
- "name": doc_name(__qualname__),
- "demo": "",
- "description": doc_description(__doc__),
- "shortcuts": [],
- "keymap": "Sequencer",
- }
- bl_idname = doc_idname(__qualname__)
- bl_label = doc["name"]
- bl_description = doc_brief(doc["description"])
- bl_options = {"REGISTER", "UNDO"}
-
- target_marker: bpy.props.EnumProperty(
- items=[("left", "left", "left"), ("right", "right", "right")],
- name="Target marker",
- description="Move to the closest marker to the left or to the right of the cursor",
- default="left",
- )
-
- @classmethod
- def poll(cls, context):
- return context.scene
-
- def execute(self, context):
- if not context.scene.timeline_markers:
- self.report({"ERROR_INVALID_INPUT"}, "There are no markers. Operation cancelled.")
- return {"CANCELLED"}
-
- frame = context.scene.frame_current
- previous_marker, next_marker = find_neighboring_markers(context, frame)
-
- if (
- not previous_marker
- and self.target_marker == "left"
- or not next_marker
- and self.target_marker == "right"
- ):
- self.report({"INFO"}, "No more markers to jump to on the %s side." % self.target_marker)
- return {"CANCELLED"}
-
- previous_time = previous_marker.frame if previous_marker else None
- next_time = next_marker.frame if next_marker else None
-
- context.scene.frame_current = (
- previous_time if self.target_marker == "left" or not next_time else next_time
- )
- return {"FINISHED"}
diff --git a/power_sequencer/operators/markers_as_timecodes.py b/power_sequencer/operators/markers_as_timecodes.py
index 642d6118..6fdfd19d 100644
--- a/power_sequencer/operators/markers_as_timecodes.py
+++ b/power_sequencer/operators/markers_as_timecodes.py
@@ -16,7 +16,6 @@
#
import bpy
import datetime as dt
-import operator as op
from .utils.doc import doc_name, doc_idname, doc_brief, doc_description
@@ -48,8 +47,7 @@ class POWER_SEQUENCER_OT_copy_markers_as_timecodes(bpy.types.Operator):
self.report({"INFO"}, "No markers found")
return {"CANCELLED"}
- sorted_markers = sorted(context.scene.timeline_markers,
- key=lambda m: m.frame)
+ sorted_markers = sorted(context.scene.timeline_markers, key=lambda m: m.frame)
framerate = render.fps / render.fps_base
last_marker_seconds = sorted_markers[-1].frame / framerate
@@ -59,9 +57,9 @@ class POWER_SEQUENCER_OT_copy_markers_as_timecodes(bpy.types.Operator):
markers_as_timecodes = []
for marker in sorted_markers:
time = dt.datetime(year=1, month=1, day=1) + dt.timedelta(
- seconds=marker.frame / framerate)
- markers_as_timecodes.append(
- time.strftime(time_format) + " " + marker.name)
+ seconds=marker.frame / framerate
+ )
+ markers_as_timecodes.append(time.strftime(time_format) + " " + marker.name)
bpy.context.window_manager.clipboard = "\n".join(markers_as_timecodes)
return {"FINISHED"}
diff --git a/power_sequencer/operators/mouse_trim_instantly.py b/power_sequencer/operators/mouse_trim_instantly.py
index dd2bb1fd..aeba2325 100644
--- a/power_sequencer/operators/mouse_trim_instantly.py
+++ b/power_sequencer/operators/mouse_trim_instantly.py
@@ -21,7 +21,6 @@ from .utils.functions import find_strips_mouse
from .utils.functions import trim_strips
from .utils.functions import get_frame_range
from .utils.doc import doc_name, doc_idname, doc_brief, doc_description
-from .utils.functions import sequencer_workaround_2_80_audio_bug
class POWER_SEQUENCER_OT_mouse_trim_instantly(bpy.types.Operator):
@@ -79,35 +78,33 @@ class POWER_SEQUENCER_OT_mouse_trim_instantly(bpy.types.Operator):
default=True,
)
- to_select = []
-
@classmethod
def poll(cls, context):
return context.sequences
def invoke(self, context, event):
- to_select = []
+ to_trim = []
frame, channel = -1, -1
- x, y = context.region.view2d.region_to_view(
- x=event.mouse_region_x, y=event.mouse_region_y
- )
+ x, y = context.region.view2d.region_to_view(x=event.mouse_region_x, y=event.mouse_region_y)
frame, channel = round(x), floor(y)
mouse_clicked_strip = find_strips_mouse(context, frame, channel, self.select_linked)
- to_select.extend(mouse_clicked_strip)
- if self.select_mode == "CURSOR":
- to_select.extend([s for s in context.sequences if s.frame_final_start <= frame <= s.frame_final_end and not s.lock])
-
- frame_cut_closest = min(get_frame_range(context, to_select), key=lambda f: abs(frame - f))
+ to_trim += mouse_clicked_strip
+ if self.select_mode == "CURSOR" or (self.select_mode == "CONTEXT" and to_trim == []):
+ to_trim += [
+ s
+ for s in context.sequences
+ if s.frame_final_start <= frame <= s.frame_final_end and not s.lock
+ ]
+
+ frame_cut_closest = min(get_frame_range(context, to_trim), key=lambda f: abs(frame - f))
frame_start = min(frame, frame_cut_closest)
frame_end = max(frame, frame_cut_closest)
- trim_strips(context, frame_start, frame_end, self.select_mode, to_select)
+ trim_strips(context, frame_start, frame_end, to_trim=to_trim)
context.scene.frame_current = frame_start
if self.gap_remove and self.select_mode == "CURSOR":
bpy.ops.power_sequencer.gap_remove()
- # FIXME: Workaround Blender 2.80's audio bug, remove when fixed in Blender
- sequencer_workaround_2_80_audio_bug(context)
return {"FINISHED"}
diff --git a/power_sequencer/operators/mouse_trim_modal.py b/power_sequencer/operators/mouse_trim_modal.py
index 4e4bb407..b75985b7 100644
--- a/power_sequencer/operators/mouse_trim_modal.py
+++ b/power_sequencer/operators/mouse_trim_modal.py
@@ -17,7 +17,6 @@
import bpy
import bgl
import gpu
-from gpu_extras.batch import batch_for_shader
import math
from mathutils import Vector
@@ -26,7 +25,6 @@ from .utils.functions import (
trim_strips,
find_snap_candidate,
find_closest_surrounding_cuts,
- sequencer_workaround_2_80_audio_bug,
)
from .utils.draw import (
@@ -48,7 +46,7 @@ class POWER_SEQUENCER_OT_mouse_trim(bpy.types.Operator):
*brief* Cut or Trim strips quickly with the mouse cursor
- Click somewehre in the Sequencer to insert a cut, click and drag to trim
+ Click somehwere in the Sequencer to insert a cut, click and drag to trim
With this function you can quickly cut and remove a section of strips while keeping or
collapsing the remaining gap.
Press <kbd>Ctrl</kbd> to snap to cuts.
@@ -186,10 +184,6 @@ class POWER_SEQUENCER_OT_mouse_trim(bpy.types.Operator):
self.trim_apply(context, event)
self.draw_stop()
context.scene.use_audio_scrub = self.use_audio_scrub
-
- # FIXME: Workaround Blender 2.80's audio bug, remove when fixed in Blender
- sequencer_workaround_2_80_audio_bug(context)
-
return {"FINISHED"}
# Update trim
@@ -217,8 +211,8 @@ class POWER_SEQUENCER_OT_mouse_trim(bpy.types.Operator):
def draw_start(self, context, event):
"""Initializes the drawing handler, see draw()"""
- to_select, to_delete = self.find_strips_to_trim(context)
- target_strips = to_select + to_delete
+ to_trim, to_delete = self.find_strips_to_trim(context)
+ target_strips = to_trim + to_delete
draw_args = (self, context, self.trim_start, self.trim_end, target_strips, self.gap_remove)
self.draw_handler = bpy.types.SpaceSequenceEditor.draw_handler_add(
@@ -263,12 +257,12 @@ class POWER_SEQUENCER_OT_mouse_trim(bpy.types.Operator):
self.is_trimming = False
def cut(self, context):
- to_select = self.find_strips_to_cut(context)
+ to_cut = self.find_strips_to_cut(context)
bpy.ops.sequencer.select_all(action="DESELECT")
- for s in to_select:
+ for s in to_cut:
s.select = True
- if len(to_select) == 0:
+ if len(to_cut) == 0:
bpy.ops.power_sequencer.gap_remove()
else:
frame_current = context.scene.frame_current
@@ -298,10 +292,10 @@ class POWER_SEQUENCER_OT_mouse_trim(bpy.types.Operator):
return to_cut
def trim(self, context):
- to_select, to_delete = self.find_strips_to_trim(context)
- trim_strips(context, self.trim_start, self.trim_end, self.select_mode, to_select, to_delete)
+ to_trim, to_delete = self.find_strips_to_trim(context)
+ trim_strips(context, self.trim_start, self.trim_end, to_trim, to_delete)
if (self.gap_remove and self.select_mode == "CURSOR") or (
- self.select_mode == "CONTEXT" and to_select == [] and to_delete == []
+ self.select_mode == "CONTEXT" and to_trim == [] and to_delete == []
):
context.scene.frame_current = min(self.trim_start, self.trim_end)
bpy.ops.power_sequencer.gap_remove()
diff --git a/power_sequencer/operators/playback_speed_set.py b/power_sequencer/operators/playback_speed_set.py
index 2e21f7b9..2dab7412 100644
--- a/power_sequencer/operators/playback_speed_set.py
+++ b/power_sequencer/operators/playback_speed_set.py
@@ -29,15 +29,15 @@ class POWER_SEQUENCER_OT_playback_speed_set(bpy.types.Operator):
"demo": "",
"description": doc_description(__doc__),
"shortcuts": [
- ({"type": "NUMPAD_1", "ctrl": True, "value": "PRESS"}, {"speed": "NORMAL"}, "Speed to 1x"),
- ({"type": "NUMPAD_2", "ctrl": True, "value": "PRESS"}, {"speed": "FAST"}, "Speed to 1.33x"),
+ ({"type": "ONE", "ctrl": True, "value": "PRESS"}, {"speed": "NORMAL"}, "Speed to 1x"),
+ ({"type": "TWO", "ctrl": True, "value": "PRESS"}, {"speed": "FAST"}, "Speed to 1.33x"),
(
- {"type": "NUMPAD_3", "ctrl": True, "value": "PRESS"},
+ {"type": "THREE", "ctrl": True, "value": "PRESS"},
{"speed": "FASTER"},
"Speed to 1.66x",
),
- ({"type": "NUMPAD_4", "ctrl": True, "value": "PRESS"}, {"speed": "DOUBLE"}, "Speed to 2x"),
- ({"type": "NUMPAD_5", "ctrl": True, "value": "PRESS"}, {"speed": "TRIPLE"}, "Speed to 3x"),
+ ({"type": "FOUR", "ctrl": True, "value": "PRESS"}, {"speed": "DOUBLE"}, "Speed to 2x"),
+ ({"type": "FIVE", "ctrl": True, "value": "PRESS"}, {"speed": "TRIPLE"}, "Speed to 3x"),
],
"keymap": "Sequencer",
}
diff --git a/power_sequencer/operators/preview_to_selection.py b/power_sequencer/operators/preview_to_selection.py
index 20d168dd..f084e767 100644
--- a/power_sequencer/operators/preview_to_selection.py
+++ b/power_sequencer/operators/preview_to_selection.py
@@ -49,8 +49,11 @@ class POWER_SEQUENCER_OT_preview_to_selection(bpy.types.Operator):
return context.sequences
def execute(self, context):
- scene = context.scene
- sequences = context.selected_sequences if len(context.selected_sequences) >= 1 else context.sequences
+ sequences = (
+ context.selected_sequences
+ if len(context.selected_sequences) >= 1
+ else context.sequences
+ )
frame_start, frame_end = get_frame_range(context, sequences)
set_preview_range(context, frame_start, frame_end - 1)
return {"FINISHED"}
diff --git a/power_sequencer/operators/render_presets/youtube_1080.py b/power_sequencer/operators/render_presets/youtube_1080.py
index 1b7940e2..49d0a651 100644
--- a/power_sequencer/operators/render_presets/youtube_1080.py
+++ b/power_sequencer/operators/render_presets/youtube_1080.py
@@ -31,12 +31,11 @@ if __name__ == "__main__":
render.ffmpeg.constant_rate_factor = "PERC_LOSSLESS"
render.ffmpeg.ffmpeg_preset = "BEST"
- is_ntsc = render.fps != 25
- if is_ntsc:
- render.ffmpeg.gopsize = 18
- else:
- render.ffmpeg.gopsize = 15
- render.ffmpeg.use_max_b_frames = False
+ scene = bpy.context.scene
+ fps = scene.render.fps / scene.render.fps_base
+ render.ffmpeg.gopsize = round(fps / 2.0)
+ render.ffmpeg.use_max_b_frames = True
+ render.ffmpeg.max_b_frames = 2
render.ffmpeg.video_bitrate = 9000
render.ffmpeg.maxrate = 9000
@@ -44,3 +43,6 @@ if __name__ == "__main__":
render.ffmpeg.buffersize = 224 * 8
render.ffmpeg.packetsize = 2048
render.ffmpeg.muxrate = 10080000
+
+ render.ffmpeg.audio_codec = "AAC"
+ render.ffmpeg.audio_bitrate = 384
diff --git a/power_sequencer/operators/ripple_delete.py b/power_sequencer/operators/ripple_delete.py
index 6b88a8e9..a469148d 100644
--- a/power_sequencer/operators/ripple_delete.py
+++ b/power_sequencer/operators/ripple_delete.py
@@ -15,13 +15,14 @@
# not, see <https://www.gnu.org/licenses/>.
#
import bpy
-from operator import attrgetter
from .utils.doc import doc_brief, doc_description, doc_idname, doc_name
-from .utils.functions import get_frame_range
-from .utils.functions import get_mouse_frame_and_channel
-from .utils.global_settings import SequenceTypes
-from .utils.functions import slice_selection
+from .utils.functions import (
+ get_frame_range,
+ get_mouse_frame_and_channel,
+ slice_selection,
+ ripple_move,
+)
class POWER_SEQUENCER_OT_ripple_delete(bpy.types.Operator):
@@ -58,6 +59,7 @@ class POWER_SEQUENCER_OT_ripple_delete(bpy.types.Operator):
scene = context.scene
sequencer = bpy.ops.sequencer
selection = context.selected_sequences
+ selection_length = len(selection)
audio_scrub_active = context.scene.use_audio_scrub
context.scene.use_audio_scrub = False
@@ -67,44 +69,28 @@ class POWER_SEQUENCER_OT_ripple_delete(bpy.types.Operator):
is_single_channel = len(channels) == 1
if is_single_channel:
- first_strip = selection_blocks[0][0]
for block in selection_blocks:
- sequencer.select_all(action="DESELECT")
- block_strip_start = block[0]
- delete_start = block_strip_start.frame_final_start
+ delete_start = block[0].frame_final_start
delete_end = block[-1].frame_final_end
- ripple_length = delete_end - delete_start
- assert ripple_length > 0
-
- for s in block:
- s.select = True
- sequencer.delete()
-
- strips_in_channel = [
- s
- for s in bpy.context.sequences
- if s.channel == channels[0]
- and s.frame_final_start >= first_strip.frame_final_start
- ]
- strips_in_channel = sorted(strips_in_channel, key=attrgetter("frame_final_start"))
- to_ripple = [s for s in strips_in_channel if s.frame_final_start > delete_start]
- for s in to_ripple:
- s.frame_start -= ripple_length
+ ripple_duration = abs(delete_end - delete_start)
+ ripple_move(context, block, -ripple_duration, delete=True)
else:
+ cursor_frame = scene.frame_current
for block in selection_blocks:
sequencer.select_all(action="DESELECT")
for s in block:
s.select = True
- selection_start, _ = get_frame_range(context, block)
+ selection_start = get_frame_range(context, block)[0]
sequencer.delete()
scene.frame_current = selection_start
bpy.ops.power_sequencer.gap_remove()
+ scene.frame_current = cursor_frame
self.report(
{"INFO"},
- "Deleted " + str(len(selection)) + " sequence" + "s" if len(selection) > 1 else "",
+ "Deleted " + str(selection_length) + " sequence" + "s" if selection_length > 1 else "",
)
context.scene.use_audio_scrub = audio_scrub_active
diff --git a/power_sequencer/operators/scene_merge_from.py b/power_sequencer/operators/scene_merge_from.py
index cab3e353..792799e8 100644
--- a/power_sequencer/operators/scene_merge_from.py
+++ b/power_sequencer/operators/scene_merge_from.py
@@ -59,7 +59,7 @@ class POWER_SEQUENCER_OT_merge_from_scene_strip(bpy.types.Operator):
def execute(self, context):
strip = context.scene.sequence_editor.active_strip
if strip.type != "SCENE":
- return {'FINISHED'}
+ return {"FINISHED"}
strip_scene = strip.scene
start_scene = context.window.scene
diff --git a/power_sequencer/operators/scene_open_from_strip.py b/power_sequencer/operators/scene_open_from_strip.py
index 9534855f..8bcbef77 100644
--- a/power_sequencer/operators/scene_open_from_strip.py
+++ b/power_sequencer/operators/scene_open_from_strip.py
@@ -46,7 +46,7 @@ class POWER_SEQUENCER_OT_open_scene_strip(bpy.types.Operator):
def execute(self, context):
active_strip = context.scene.sequence_editor.active_strip
if active_strip.type != "SCENE":
- return {'FINISHED'}
+ return {"FINISHED"}
strip_scene = active_strip.scene
context.screen.scene = bpy.data.scenes[strip_scene.name]
diff --git a/power_sequencer/operators/scene_rename_with_strip.py b/power_sequencer/operators/scene_rename_with_strip.py
index f142f8bd..245909b5 100644
--- a/power_sequencer/operators/scene_rename_with_strip.py
+++ b/power_sequencer/operators/scene_rename_with_strip.py
@@ -15,7 +15,6 @@
# not, see <https://www.gnu.org/licenses/>.
#
import bpy
-import operator
from .utils.doc import doc_name, doc_idname, doc_brief, doc_description
diff --git a/power_sequencer/operators/snap_selection.py b/power_sequencer/operators/snap_selection.py
index 0c93b76e..db661d06 100644
--- a/power_sequencer/operators/snap_selection.py
+++ b/power_sequencer/operators/snap_selection.py
@@ -15,7 +15,7 @@
# not, see <https://www.gnu.org/licenses/>.
#
import bpy
-from .utils.functions import get_sequences_under_cursor
+from .utils.functions import get_sequences_under_cursor, apply_time_offset
from .utils.doc import doc_name, doc_idname, doc_brief, doc_description
@@ -48,11 +48,10 @@ class POWER_SEQUENCER_OT_snap_selection(bpy.types.Operator):
def execute(self, context):
sequences = (
context.selected_sequences
- if len(context.selected_sequences) > 0
+ if context.selected_sequences
else get_sequences_under_cursor(context)
)
- time_move = context.scene.frame_current - sequences[0].frame_final_start
- # bpy.ops.power_sequencer.select_related_strips()
- for s in sequences:
- s.frame_start += time_move
+ frame_first = min(sequences, key=lambda s: s.frame_final_start).frame_final_start
+ time_offset = context.scene.frame_current - frame_first
+ apply_time_offset(context, sequences, time_offset)
return {"FINISHED"}
diff --git a/power_sequencer/operators/space_sequences.py b/power_sequencer/operators/space_sequences.py
index 3f7bb3b0..40985afb 100644
--- a/power_sequencer/operators/space_sequences.py
+++ b/power_sequencer/operators/space_sequences.py
@@ -16,7 +16,7 @@
#
import bpy
-from .utils.functions import get_mouse_frame_and_channel, convert_duration_to_frames
+from .utils.functions import convert_duration_to_frames
from .utils.global_settings import SequenceTypes
from .utils.doc import doc_name, doc_idname, doc_brief, doc_description
diff --git a/power_sequencer/operators/speed_up_movie_strip.py b/power_sequencer/operators/speed_up_movie_strip.py
index 0c87b78b..27b66130 100644
--- a/power_sequencer/operators/speed_up_movie_strip.py
+++ b/power_sequencer/operators/speed_up_movie_strip.py
@@ -19,7 +19,6 @@ from math import ceil
from .utils.global_settings import SequenceTypes
from .utils.functions import slice_selection
-from .utils.functions import find_linked
from .utils.doc import doc_name, doc_idname, doc_brief, doc_description
@@ -36,21 +35,9 @@ class POWER_SEQUENCER_OT_speed_up_movie_strip(bpy.types.Operator):
"demo": "https://i.imgur.com/ZyEd0jD.gif",
"description": doc_description(__doc__),
"shortcuts": [
- (
- {"type": "NUMPAD_2", "value": "PRESS", "alt": True},
- {"speed_factor": 2.0},
- "Speed x2",
- ),
- (
- {"type": "NUMPAD_3", "value": "PRESS", "alt": True},
- {"speed_factor": 3.0},
- "Speed x3",
- ),
- (
- {"type": "NUMPAD_4", "value": "PRESS", "alt": True},
- {"speed_factor": 4.0},
- "Speed x4",
- ),
+ ({"type": "TWO", "value": "PRESS", "alt": True}, {"speed_factor": 2.0}, "Speed x2",),
+ ({"type": "THREE", "value": "PRESS", "alt": True}, {"speed_factor": 3.0}, "Speed x3",),
+ ({"type": "FOUR", "value": "PRESS", "alt": True}, {"speed_factor": 4.0}, "Speed x4",),
],
"keymap": "Sequencer",
}
@@ -101,16 +88,15 @@ class POWER_SEQUENCER_OT_speed_up_movie_strip(bpy.types.Operator):
return
sequence_editor = context.scene.sequence_editor
- bpy.ops.sequencer.select_all(action="DESELECT")
+ sequencer = bpy.ops.sequencer
+
+ sequencer.select_all(action="DESELECT")
for s in sequences:
s.select = True
- bpy.ops.sequencer.meta_make()
+ sequencer.meta_make()
meta_strip = sequence_editor.active_strip
- if len(meta_strip.sequences) == 1:
- meta_strip.sequences[0].frame_offset_start = 0
- meta_strip.sequences[0].frame_offset_end = 0
- bpy.ops.sequencer.effect_strip_add(type="SPEED")
+ sequencer.effect_strip_add(type="SPEED")
speed_effect = sequence_editor.active_strip
speed_effect.use_default_fade = False
speed_effect.speed_factor = self.speed_factor
@@ -121,7 +107,7 @@ class POWER_SEQUENCER_OT_speed_up_movie_strip(bpy.types.Operator):
sequence_editor.active_strip = meta_strip
speed_effect.select = True
meta_strip.select = True
- bpy.ops.sequencer.meta_make()
+ sequencer.meta_make()
sequence_editor.active_strip.name = (
meta_strip.sequences[0].name + " " + str(self.speed_factor) + "x"
)
diff --git a/power_sequencer/operators/synchronize_titles.py b/power_sequencer/operators/synchronize_titles.py
deleted file mode 100644
index 3df342f4..00000000
--- a/power_sequencer/operators/synchronize_titles.py
+++ /dev/null
@@ -1,112 +0,0 @@
-#
-# Copyright (C) 2016-2019 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
-#
-# This file is part of Power Sequencer.
-#
-# Power Sequencer is free software: you can redistribute it and/or modify it under the terms of the
-# GNU General Public License as published by the Free Software Foundation, either version 3 of the
-# License, or (at your option) any later version.
-#
-# Power Sequencer is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
-# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along with Power Sequencer. If
-# not, see <https://www.gnu.org/licenses/>.
-#
-import bpy
-
-from .utils.doc import doc_name, doc_idname, doc_brief, doc_description
-
-
-# TODO: rewrite to sync strips to corresponding identifiers instead
-# See https://github.com/GDquest/Blender-power-sequencer/issues/55
-class POWER_SEQUENCER_OT_synchronize_titles(bpy.types.Operator):
- """
- *brief* Snap the selected image or text strips to the corresponding title marker
-
-
- The marker and strip names have to start with TITLE-001
- """
-
- doc = {
- "name": doc_name(__qualname__),
- "demo": "",
- "description": doc_description(__doc__),
- "shortcuts": [],
- "keymap": "Sequencer",
- }
- bl_idname = doc_idname(__qualname__)
- bl_label = doc["name"]
- bl_description = doc_brief(doc["description"])
- bl_options = {"REGISTER", "UNDO"}
-
- TITLE_REGEX = r"^TITLE-?([0-9]+)-?"
-
- @classmethod
- def poll(cls, context):
- return context.scene.sequence_editor
-
- def execute(self, context):
- markers = context.scene.timeline_markers
- selection = context.selected_sequences
-
- if not markers and selection:
- if not markers:
- self.report({"INFO"}, "No markers, operation cancelled.")
- else:
- self.report({"INFO"}, "No sequences selected, operation cancelled.")
- return {"CANCELLED"}
-
- title_markers = self.find_markers(context, self.TITLE_REGEX)
- if not title_markers:
- self.report({"INFO"}, "No title markers found, operation cancelled.")
-
- matched = self.match_sequences_and_markers(selection, title_markers, self.TITLE_REGEX)
- for s, m in matched:
- s.frame_start = m.frame
- return {"FINISHED"}
-
- def match_sequences_and_markers(self, sequences, markers, regex):
- """Takes a list of sequences, of markers, and checks if they both
- match a regular expression.
- Returns a list of pairs of sequence and marker as tuples
-
- Args:
- - sequences, the list of sequences
- - markers, a list of markers
- - regex, the regular expression to match"""
- if not sequences and markers and regex:
- raise AttributeError("missing attributes")
-
- import re
- from .utils.global_settings import SequenceTypes
-
- sequences = (s for s in sequences if s.type not in SequenceTypes.EFFECT)
-
- return_list = []
- re_title = re.compile(regex)
- for s in sequences:
- found = re_title.match(s.name)
- title_id = int(found.group(1)) if found else None
- for m in markers:
- found = re_title.match(m.name)
- marker_id = int(found.group(1)) if found else None
- if marker_id == title_id:
- return_list.append((s, m))
- break
- return return_list
-
- def find_markers(self, context, regex):
- """Finds and returns all markers using REGEX
- Args:
- - regex, the re match pattern to use"""
- if not regex:
- raise AttributeError("regex parameter missing")
-
- import re
-
- regex = re.compile(regex)
- markers = context.scene.timeline_markers
- markers = (m for m in markers if regex.match(m.name))
- return markers
diff --git a/power_sequencer/operators/transitions_remove.py b/power_sequencer/operators/transitions_remove.py
index aabb2af5..4ebd2b44 100644
--- a/power_sequencer/operators/transitions_remove.py
+++ b/power_sequencer/operators/transitions_remove.py
@@ -44,7 +44,6 @@ class POWER_SEQUENCER_OT_transitions_remove(bpy.types.Operator):
def poll(cls, context):
return context.selected_sequences
-
def execute(self, context):
to_process = (
self.sequences_override if self.sequences_override else context.selected_sequences
diff --git a/power_sequencer/operators/trim_to_surrounding_cuts.py b/power_sequencer/operators/trim_to_surrounding_cuts.py
index 11ece536..0d4c012b 100644
--- a/power_sequencer/operators/trim_to_surrounding_cuts.py
+++ b/power_sequencer/operators/trim_to_surrounding_cuts.py
@@ -19,14 +19,10 @@ Find the two closest cuts, trims and deletes all strips above in the range but l
margin. Removes the newly formed gap.
"""
import bpy
-from math import floor
-from .utils.functions import convert_duration_to_frames
+from .utils.functions import convert_duration_to_frames, trim_strips
from .utils.doc import doc_name, doc_idname, doc_brief, doc_description
-from .utils.functions import (
- find_closest_surrounding_cuts_frames,
- sequencer_workaround_2_80_audio_bug,
-)
+from .utils.functions import find_closest_surrounding_cuts_frames
class POWER_SEQUENCER_OT_trim_to_surrounding_cuts(bpy.types.Operator):
@@ -38,6 +34,7 @@ class POWER_SEQUENCER_OT_trim_to_surrounding_cuts(bpy.types.Operator):
By default, the tool leaves a 0.2 seconds margin on either side of the trim.
"""
+
doc = {
"name": doc_name(__qualname__),
"demo": "",
@@ -76,14 +73,13 @@ class POWER_SEQUENCER_OT_trim_to_surrounding_cuts(bpy.types.Operator):
if not context.sequences:
return {"CANCELLED"}
- sequencer = bpy.ops.sequencer
-
- frame = context.region.view2d.region_to_view(x=event.mouse_region_x, y=event.mouse_region_y)[0]
+ frame = context.region.view2d.region_to_view(
+ x=event.mouse_region_x, y=event.mouse_region_y
+ )[0]
frame = round(frame)
- left_cut_frame, right_cut_frame = find_closest_surrounding_cuts_frames(context, frame)
margin_frame = convert_duration_to_frames(context, self.margin)
-
+ left_cut_frame, right_cut_frame = find_closest_surrounding_cuts_frames(context, frame)
if abs(left_cut_frame - right_cut_frame) <= margin_frame * 2:
self.report(
{"WARNING"},
@@ -94,26 +90,7 @@ class POWER_SEQUENCER_OT_trim_to_surrounding_cuts(bpy.types.Operator):
to_delete, to_trim = self.find_strips_in_range(context, left_cut_frame, right_cut_frame)
trim_start, trim_end = (left_cut_frame + margin_frame, right_cut_frame - margin_frame)
- for s in to_trim:
- # If the strip is larger than the range to trim cut it in three
- if s.frame_final_start < trim_start and s.frame_final_end > trim_end:
- sequencer.select_all(action="DESELECT")
- s.select = True
- sequencer.cut(frame=trim_start, type="SOFT", side="RIGHT")
- sequencer.cut(frame=trim_end, type="SOFT", side="LEFT")
- to_delete.append(context.selected_sequences[0])
- continue
-
- if s.frame_final_start < trim_end and s.frame_final_end > trim_end:
- s.frame_final_start = trim_end
- elif s.frame_final_end > trim_start and s.frame_final_start < trim_start:
- s.frame_final_end = trim_start
-
- # Delete all sequences that are between the cuts
- sequencer.select_all(action="DESELECT")
- for s in to_delete:
- s.select = True
- sequencer.delete()
+ trim_strips(context, trim_start, trim_end, to_trim, to_delete)
if self.gap_remove:
frame_to_remove_gap = right_cut_frame - 1 if frame == right_cut_frame else frame
@@ -122,7 +99,6 @@ class POWER_SEQUENCER_OT_trim_to_surrounding_cuts(bpy.types.Operator):
bpy.ops.power_sequencer.gap_remove()
context.scene.frame_current = trim_start
- sequencer_workaround_2_80_audio_bug(context)
return {"FINISHED"}
def find_strips_in_range(
diff --git a/power_sequencer/operators/utils/doc.py b/power_sequencer/operators/utils/doc.py
index 82a87581..0a3e0a83 100644
--- a/power_sequencer/operators/utils/doc.py
+++ b/power_sequencer/operators/utils/doc.py
@@ -18,7 +18,6 @@
Utilities to convert operator names and docstrings to human-readable text.
Used to generate names for Blender's operator search, and to generate Power Sequencer's documentation.
"""
-import re
upper_match = lambda m: m.string
diff --git a/power_sequencer/operators/utils/functions.py b/power_sequencer/operators/utils/functions.py
index 35fd900d..e2ea3672 100644
--- a/power_sequencer/operators/utils/functions.py
+++ b/power_sequencer/operators/utils/functions.py
@@ -14,10 +14,11 @@
# You should have received a copy of the GNU General Public License along with Power Sequencer. If
# not, see <https://www.gnu.org/licenses/>.
#
-import bpy
-import subprocess
-from math import sqrt, floor
+from math import floor, sqrt
from operator import attrgetter
+
+import bpy
+
from .global_settings import SequenceTypes
@@ -258,38 +259,50 @@ def slice_selection(context, sequences):
return broken_selection
-def trim_strips(
- context, start_frame, end_frame, select_mode, strips_to_trim=[], strips_to_delete=[]
-):
+def trim_strips(context, start_frame, end_frame, to_trim=[], to_delete=[]):
"""
Remove the footage and audio between start_frame and end_frame.
"""
trim_start = min(start_frame, end_frame)
trim_end = max(start_frame, end_frame)
- strips_to_trim = [s for s in strips_to_trim if s.type in SequenceTypes.CUTABLE]
+ to_trim = [s for s in to_trim if s.type in SequenceTypes.CUTABLE]
- for s in strips_to_trim:
- if s.frame_final_start < trim_start and s.frame_final_end > trim_end:
+ for s in to_trim:
+ # Cut strip longer than the trim range in three
+ is_strip_longer_than_trim_range = (
+ s.frame_final_start < trim_start and s.frame_final_end > trim_end
+ )
+ if is_strip_longer_than_trim_range:
bpy.ops.sequencer.select_all(action="DESELECT")
s.select = True
bpy.ops.sequencer.cut(frame=trim_start, type="SOFT", side="RIGHT")
bpy.ops.sequencer.cut(frame=trim_end, type="SOFT", side="LEFT")
- strips_to_delete.append(context.selected_sequences[0])
+ to_delete.append(context.selected_sequences[0])
continue
+
+ # Resize strips that overlap the trim range
elif s.frame_final_start < trim_end and s.frame_final_end > trim_end:
s.frame_final_start = trim_end
elif s.frame_final_end > trim_start and s.frame_final_start < trim_start:
s.frame_final_end = trim_start
- if strips_to_delete != []:
- bpy.ops.sequencer.select_all(action="DESELECT")
- for s in strips_to_delete:
- s.select = True
- bpy.ops.sequencer.delete()
+ delete_strips(to_delete)
return {"FINISHED"}
+def delete_strips(to_delete=[]):
+ """
+ Remove the footage and audio between start_frame and end_frame.
+ """
+ if to_delete == []:
+ return
+ bpy.ops.sequencer.select_all(action="DESELECT")
+ for s in to_delete:
+ s.select = True
+ bpy.ops.sequencer.delete()
+
+
def find_closest_surrounding_cuts(context, frame):
"""
Returns a tuple of (strip_before, strip_after), the two closest sequences around a gap.
@@ -333,11 +346,48 @@ def get_sequences_under_cursor(context):
return under_cursor
-def sequencer_workaround_2_80_audio_bug(context):
- for s in context.sequences:
- if s.lock:
- continue
+def ripple_move(context, sequences, duration_frames, delete=False):
+ """Moves sequences in the list and ripples the change to all sequences after them, in the corresponding channels
+ The `duration_frames` can be positive or negative.
+ If `delete` is True, deletes every sequence in `sequences`.
+ """
+ channels = {s.channel for s in sequences}
+ first_strip = min(sequences, key=lambda s: s.frame_final_start)
+ to_ripple = [
+ s
+ for s in context.sequences
+ if s.channel in channels and s.frame_final_start >= first_strip.frame_final_start
+ ]
+
+ if delete:
+ bpy.ops.sequencer.select_all(action="DESELECT")
+ for s in sequences:
+ s.select = True
+ bpy.ops.sequencer.delete()
+ else:
+ to_ripple = set(to_ripple + sequences)
+
+ # Use the built-in seq_slide operator to move strips, for best performances
+ initial_selection = context.selected_sequences
+ bpy.ops.sequencer.select_all(action="DESELECT")
+ for s in to_ripple:
+ s.select = True
+ bpy.ops.transform.seq_slide(value=(duration_frames, 0))
+ bpy.ops.sequencer.select_all(action="DESELECT")
+ for s in initial_selection:
+ s.select = True
+
+
+def apply_time_offset(context, sequences=[], offset=0):
+ """Offsets a list of sequences in time using bpy.ops.transform.seq_slide. Mutates and restores the
+ user's selection. Use this function to ensure maximum performances and avoid having to figure
+ out the logic to move strips in the right order.
+ """
+ selection = context.selected_sequences
+ bpy.ops.sequencer.select_all(action="DESELECT")
+ for s in sequences:
+ s.select = True
+ bpy.ops.transform.seq_slide(value=(offset, 0))
+ bpy.ops.sequencer.select_all(action="DESELECT")
+ for s in selection:
s.select = True
- bpy.ops.transform.seq_slide(value=(0, 0))
- s.select = False
- break
diff --git a/power_sequencer/operators/utils/global_settings.py b/power_sequencer/operators/utils/global_settings.py
index 88590783..03d94365 100644
--- a/power_sequencer/operators/utils/global_settings.py
+++ b/power_sequencer/operators/utils/global_settings.py
@@ -58,7 +58,7 @@ class SequenceTypes:
SOUND = ("SOUND",)
IMAGE = ("IMAGE",)
TRANSITIONABLE = (
- VIDEO + IMAGE + ("MULTICAM", "GAUSSIAN_BLUR", "TRANSFORM", "ADJUSTMENT", "SPEED")
+ VIDEO + IMAGE + ("MULTICAM", "GAUSSIAN_BLUR", "TRANSFORM", "ADJUSTMENT", "SPEED", "COLOR")
)
# Strips that can be cut. If most effect strips are linked to their inputs
# and shouldn't be cut, some can be edited directly
diff --git a/power_sequencer/operators/utils/info_progress_bar.py b/power_sequencer/operators/utils/info_progress_bar.py
index a3049bf5..fc23c3c1 100644
--- a/power_sequencer/operators/utils/info_progress_bar.py
+++ b/power_sequencer/operators/utils/info_progress_bar.py
@@ -15,7 +15,6 @@
# not, see <https://www.gnu.org/licenses/>.
#
import bpy
-import time
class InfoProgressBar:
diff --git a/power_sequencer/scripts/BPSProxy/bpsproxy/commands.py b/power_sequencer/scripts/BPSProxy/bpsproxy/commands.py
index a2f4388e..72c81ccf 100644
--- a/power_sequencer/scripts/BPSProxy/bpsproxy/commands.py
+++ b/power_sequencer/scripts/BPSProxy/bpsproxy/commands.py
@@ -70,7 +70,7 @@ def get_commands_image_1(cfg, clargs, **kwargs):
out: iter(tuple(str))
Iterator containing commands.
"""
- cmd = "ffmpeg -y -v quiet -stats -i '{path_i_1}' {common_all}"
+ cmd = "ffmpeg -hwaccel auto -y -v quiet -stats -i '{path_i_1}' {common_all}"
common = "-f apng -filter:v scale=iw*{size}:ih*{size} '{path_o_1}'"
common_all = map(lambda s: kwargs["path_o_1"].format(size=s), clargs.sizes)
common_all = map(
@@ -101,7 +101,7 @@ def get_commands_video_1(cfg, clargs, **kwargs):
out: iter(tuple(str))
Iterator containing commands.
"""
- cmd = "ffmpeg -y -v quiet -stats -i '{path_i_1}' {common_all}"
+ cmd = "ffmpeg -hwaccel auto -y -v quiet -stats -i '{path_i_1}' {common_all}"
common = (
"-pix_fmt yuv420p"
" -g 1"
@@ -186,5 +186,5 @@ def get_commands_vi(cfg, clargs, **kwargs):
An iterator with the 1st element as a tag (the `what` parameter) and the 2nd
element as the iterator of the actual commands.
"""
- ws = filter(lambda x: x != "all", cfg["extensions"])
+ ws = filter(lambda x: x is not "all", cfg["extensions"])
return chain.from_iterable(map(lambda w: get_commands(cfg, clargs, what=w, **kwargs), ws))
diff --git a/power_sequencer/scripts/BPSProxy/bpsproxy/config.py b/power_sequencer/scripts/BPSProxy/bpsproxy/config.py
index eada47d5..28554e85 100644
--- a/power_sequencer/scripts/BPSProxy/bpsproxy/config.py
+++ b/power_sequencer/scripts/BPSProxy/bpsproxy/config.py
@@ -29,7 +29,7 @@ CONFIG = {
},
"presets": {
"webm": "-c:v libvpx -crf 25 -speed 16 -threads {}".format(str(mp.cpu_count())),
- "mp4": "-c:v libx264 -crf 25 -preset faster",
+ "mp4": "-c:v libx264 -crf 25 -preset faster -tune fastdecode",
"nvenc": "-c:v h264_nvenc -qp 25 -preset fast",
},
"pre": {"work": "»", "done": "•", "skip": "~"},
diff --git a/power_sequencer/scripts/BPSProxy/bpsproxy/utils.py b/power_sequencer/scripts/BPSProxy/bpsproxy/utils.py
index a33c34a9..832a0beb 100644
--- a/power_sequencer/scripts/BPSProxy/bpsproxy/utils.py
+++ b/power_sequencer/scripts/BPSProxy/bpsproxy/utils.py
@@ -36,14 +36,14 @@ def checktools(tools):
msg = ["BPSProxy couldn't find external dependencies:"]
msg += [
"[{check}] {tool}: {path}".format(
- check="v" if path != "" else "X", tool=tool, path=path or "NOT FOUND"
+ check="v" if path is not "" else "X", tool=tool, path=path or "NOT FOUND"
)
for tool, path in check["tools"]
]
msg += [
(
"Check if you have them properly installed and available in the PATH"
- " environment variable."
+ " environemnt variable."
)
]
raise ToolError("\n".join(msg))
diff --git a/power_sequencer/scripts/BPSProxy/setup.py b/power_sequencer/scripts/BPSProxy/setup.py
index f1dc3f59..996ba3db 100644
--- a/power_sequencer/scripts/BPSProxy/setup.py
+++ b/power_sequencer/scripts/BPSProxy/setup.py
@@ -14,22 +14,23 @@
# You should have received a copy of the GNU General Public License along with Power Sequencer. If
# not, see <https://www.gnu.org/licenses/>.
#
+from setuptools import setup
+
def readme():
- with open("README.rst") as f:
+ with open("README.md") as f:
return f.read()
if __name__ == "__main__":
- from setuptools import setup
-
setup(
name="bpsproxy",
- version="0.1.3.post1",
+ version="0.2.0",
description="Blender Power Sequencer proxy generator tool",
long_description=readme(),
+ long_description_content_type="text/markdown",
classifiers=[
- "Development Status :: 4 - Beta",
+ "Development Status :: 5 - Production/Stable",
"Environment :: Console",
"Intended Audience :: End Users/Desktop",
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
@@ -43,7 +44,7 @@ if __name__ == "__main__":
"Topic :: Multimedia :: Video",
"Topic :: Utilities",
],
- url="https://gitlab.com/razcore/bpsproxy",
+ url="https://github.com/GDquest/BPSProxy",
keywords="blender proxy vse sequence editor productivity",
author="Răzvan C. Rădulescu",
author_email="razcore.art@gmail.com",
diff --git a/power_sequencer/scripts/BPSRender/bpsrender/commands.py b/power_sequencer/scripts/BPSRender/bpsrender/commands.py
index dc669806..910ff021 100644
--- a/power_sequencer/scripts/BPSRender/bpsrender/commands.py
+++ b/power_sequencer/scripts/BPSRender/bpsrender/commands.py
@@ -249,7 +249,7 @@ def get_commands_join(cfg, clargs, **kwargs):
"-c:a",
"aac",
"-b:a",
- "192k",
+ "320k",
"-y",
kwargs["render_audiovideo_path"],
)
diff --git a/power_sequencer/scripts/BPSRender/bpsrender/helpers.py b/power_sequencer/scripts/BPSRender/bpsrender/helpers.py
index df74d333..9ebcf2b0 100644
--- a/power_sequencer/scripts/BPSRender/bpsrender/helpers.py
+++ b/power_sequencer/scripts/BPSRender/bpsrender/helpers.py
@@ -41,14 +41,14 @@ def checktools(tools):
msg = ["BPSRender couldn't find external dependencies:"]
msg += [
"[{check}] {tool}: {path}".format(
- check="v" if path != "" else "X", tool=tool, path=path or "NOT FOUND"
+ check="v" if path is not "" else "X", tool=tool, path=path or "NOT FOUND"
)
for tool, path in check["tools"]
]
msg += [
(
"Check if you have them properly installed and available in the PATH"
- " environment variable."
+ " environemnt variable."
),
"Exiting...",
]
diff --git a/power_sequencer/scripts/BPSRender/bpsrender/setup.py b/power_sequencer/scripts/BPSRender/bpsrender/setup.py
index 9aa044c2..aba30d07 100644
--- a/power_sequencer/scripts/BPSRender/bpsrender/setup.py
+++ b/power_sequencer/scripts/BPSRender/bpsrender/setup.py
@@ -139,7 +139,7 @@ def setup_folders_hdd(cfg, clargs, **kwargs):
-------
out: (iter((str, iter(tuple))), dict)
1st element: see commands.py:get_commands_all
- 2nd element: the keyword arguments used by calls.py:call
+ 2nd elment: the keyword arguments used by calls.py:call
"""
# create folder structure if it doesn't exist already only if
# appropriate command line arguments are given
@@ -170,7 +170,7 @@ def setup(cfg, clargs):
-------
out: (iter((str, iter(tuple))), dict)
1st element: see commands.py:get_commands_all
- 2nd element: the keyword arguments used by calls.py:call
+ 2nd elment: the keyword arguments used by calls.py:call
"""
setups_f = (setup_bspy, setup_probe, setup_paths, setup_folders_hdd)
lg.basicConfig(level=LOGLEV[min(clargs.verbose, len(LOGLEV) - 1)])
diff --git a/power_sequencer/scripts/BPSRender/setup.py b/power_sequencer/scripts/BPSRender/setup.py
index 3d829d64..1f17b9ba 100644
--- a/power_sequencer/scripts/BPSRender/setup.py
+++ b/power_sequencer/scripts/BPSRender/setup.py
@@ -14,6 +14,8 @@
# You should have received a copy of the GNU General Public License along with Power Sequencer. If
# not, see <https://www.gnu.org/licenses/>.
#
+from setuptools import setup
+
def readme():
with open("README.rst") as f:
@@ -21,8 +23,6 @@ def readme():
if __name__ == "__main__":
- from setuptools import setup
-
setup(
name="bpsrender",
version="0.1.40.post1",
diff --git a/power_sequencer/tools/__init__.py b/power_sequencer/tools/__init__.py
new file mode 100644
index 00000000..4c0e75ca
--- /dev/null
+++ b/power_sequencer/tools/__init__.py
@@ -0,0 +1,33 @@
+#
+# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
+#
+# This file is part of Power Sequencer.
+#
+# Power Sequencer is free software: you can redistribute it and/or modify it under the terms of the
+# GNU General Public License as published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# Power Sequencer is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along with Power Sequencer. If
+# not, see <https://www.gnu.org/licenses/>.
+#
+import importlib
+import os
+
+
+def get_tool_classes():
+ """Returns the list of tools in the add-on"""
+ this_file = os.path.dirname(__file__)
+ module_files = [
+ f for f in os.listdir(this_file) if f.endswith(".py") and not f.startswith("__init__")
+ ]
+ module_paths = ["." + os.path.splitext(f)[0] for f in module_files]
+ classes = []
+ for path in module_paths:
+ module = importlib.import_module(path, package="blender_power_sequencer.tools")
+ tool_names = [entry for entry in dir(module) if entry.startswith("POWER_SEQUENCER_TOOL")]
+ classes.extend([getattr(module, name) for name in tool_names])
+ return classes
diff --git a/power_sequencer/tools/trim.py b/power_sequencer/tools/trim.py
new file mode 100644
index 00000000..e6a54437
--- /dev/null
+++ b/power_sequencer/tools/trim.py
@@ -0,0 +1,45 @@
+#
+# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
+#
+# This file is part of Power Sequencer.
+#
+# Power Sequencer is free software: you can redistribute it and/or modify it under the terms of the
+# GNU General Public License as published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# Power Sequencer is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along with Power Sequencer. If
+# not, see <https://www.gnu.org/licenses/>.
+#
+import bpy
+from bpy.types import WorkSpaceTool
+
+
+class POWER_SEQUENCER_TOOL_Trim(WorkSpaceTool):
+ bl_space_type = "SEQUENCE_EDITOR"
+ bl_context_mode = "SEQUENCER"
+
+ bl_idname = "power_sequencer.trim"
+ bl_label = "Trim"
+ bl_description = "Cut and trim strips with the mouse"
+ bl_icon = "ops.mesh.knife_tool"
+ bl_widget = None
+ bl_keymap = (
+ (
+ "power_sequencer.mouse_trim",
+ {"type": "LEFTMOUSE", "value": "PRESS"},
+ {"select_mode": "CONTEXT", "gap_remove": False},
+ ),
+ (
+ "power_sequencer.mouse_trim",
+ {"type": "LEFTMOUSE", "value": "PRESS", "shift": True},
+ {"select_mode": "CURSOR", "gap_remove": True},
+ ),
+ )
+
+ def draw_settings(context, layout, tool):
+ props = tool.operator_properties("power_sequencer.mouse_trim")
+ layout.prop(props, "mode")
diff --git a/power_sequencer/ui/menu_contextual.py b/power_sequencer/ui/menu_contextual.py
index 6ba0640a..c145beab 100644
--- a/power_sequencer/ui/menu_contextual.py
+++ b/power_sequencer/ui/menu_contextual.py
@@ -69,9 +69,7 @@ class POWER_SEQUENCER_MT_contextual(bpy.types.Menu):
layout.operator(
"power_sequencer.ripple_delete", icon="AUTOMERGE_ON", text="Ripple delete"
)
- layout.operator(
- "power_sequencer.snap_selection", icon="SNAP_ON", text="Snap selection"
- )
+ layout.operator("power_sequencer.snap_selection", icon="SNAP_ON", text="Snap selection")
layout.separator()
diff --git a/power_sequencer/ui/menu_toolbar.py b/power_sequencer/ui/menu_toolbar.py
index 744ed7b9..3510d157 100644..100755
--- a/power_sequencer/ui/menu_toolbar.py
+++ b/power_sequencer/ui/menu_toolbar.py
@@ -52,7 +52,7 @@ class POWER_SEQUENCER_MT_playback(bpy.types.Menu):
class POWER_SEQUENCER_MT_strips(bpy.types.Menu):
- bl_label = "Strips"
+ bl_label = "Strip"
def draw(self, context):
layout = self.layout
@@ -72,7 +72,7 @@ class POWER_SEQUENCER_MT_strips(bpy.types.Menu):
class POWER_SEQUENCER_MT_transitions(bpy.types.Menu):
- bl_label = "Transitions"
+ bl_label = "Transition"
def draw(self, context):
layout = self.layout
@@ -132,7 +132,7 @@ class POWER_SEQUENCER_MT_edit(bpy.types.Menu):
class POWER_SEQUENCER_MT_markers(bpy.types.Menu):
- bl_label = "Markers"
+ bl_label = "Marker"
def draw(self, context):
layout = self.layout
@@ -142,7 +142,6 @@ class POWER_SEQUENCER_MT_markers(bpy.types.Menu):
layout.separator()
- layout.operator("power_sequencer.marker_go_to_next")
layout.operator("power_sequencer.copy_markers_as_timecodes")
layout.operator("power_sequencer.marker_snap_to_cursor")
layout.operator("power_sequencer.set_preview_between_markers")
diff --git a/power_sequencer/utils/register_shortcuts.py b/power_sequencer/utils/register_shortcuts.py
index 074fb1c3..5ecfdb30 100644
--- a/power_sequencer/utils/register_shortcuts.py
+++ b/power_sequencer/utils/register_shortcuts.py
@@ -35,14 +35,11 @@ def set_keymap_property(properties, property_name, value):
print("Warning: %r" % e)
-def register_shortcuts():
+def register_shortcuts(operator_classes):
def keymapgetter(operator):
return operator[1]["keymap"]
- data = dir(operators)
- data = filter(lambda operator: operator[0].isupper(), data)
- data = map(lambda operator: op.attrgetter(operator), data)
- data = map(lambda operator: operator(operators), data)
+ data = operator_classes
data = map(lambda operator: op.attrgetter("bl_idname", "doc")(operator), data)
data = {k: v for k, v in data if v != {}}
data.update(operators.doc)