From a30e16606b78c2bf25d36d4014259b681ae9b0d6 Mon Sep 17 00:00:00 2001 From: "florentin.luce" Date: Fri, 24 May 2024 09:44:19 +0200 Subject: [PATCH 1/6] Split auto (UI + Core) - 1st version --- auto_splitter.py | 36 ++++++++++++++++++++++++++ constants.py | 4 ++- file_utils.py | 4 +-- operators/sequencer.py | 59 +++++++++++++++++++++++++++++++++++++++++- ui/panels.py | 1 + 5 files changed, 100 insertions(+), 4 deletions(-) create mode 100644 auto_splitter.py diff --git a/auto_splitter.py b/auto_splitter.py new file mode 100644 index 0000000..84da28b --- /dev/null +++ b/auto_splitter.py @@ -0,0 +1,36 @@ +import re +import subprocess + +from vse_toolbox.constants import AUTO_SPLITTER_LOG +from vse_toolbox import file_utils + + +class AutoSplitter(object): + + def __init__(self, paths, fps=None): + + self.paths = paths + self.fps = fps + + self.log_path = AUTO_SPLITTER_LOG + + def launch_analysis(self, threshold=0.6): + + ffmpeg_cmd = f"ffmpeg -i {str(self.paths[0])} -filter:v \"select='gt(scene,{threshold})',showinfo\" -f null - 2> {str(self.log_path)}" + print(ffmpeg_cmd) + + subprocess.call(ffmpeg_cmd, shell=True) + + def get_split_times(self, as_frame=True): + log = file_utils.read_file(self.log_path) + + timecodes = re.findall(r'pts_time:([\d.]+)', log) + + if as_frame: + # convert timecode to frame number + if not self.fps: + self.fps = float(re.findall(r'([\d]+) fps', log)[0]) + + return [round(float(time) * self.fps) for time in timecodes] + + return timecodes diff --git a/constants.py b/constants.py index fb7d94e..7180420 100644 --- a/constants.py +++ b/constants.py @@ -36,4 +36,6 @@ ASSET_PREVIEWS = bpy.utils.previews.new() CASTING_BUFFER = CONFIG_DIR / 'casting.json' -SPREADSHEET = [] \ No newline at end of file +SPREADSHEET = [] + +AUTO_SPLITTER_LOG = CONFIG_DIR / 'auto_splitter.log' diff --git a/file_utils.py b/file_utils.py index 2e70c5a..b59f18e 100644 --- a/file_utils.py +++ b/file_utils.py @@ -89,9 +89,9 @@ def norm_name(string, separator='_', format=str.lower, padding=0): return string def read_file(path): - '''Read a file with an extension in (json, yaml, yml, txt)''' + '''Read a file with an extension in (json, yaml, yml, txt, log)''' - exts = ('.json', '.yaml', '.yml', '.txt') + exts = ('.json', '.yaml', '.yml', '.txt', '.log') if not path: print('Try to read empty file') diff --git a/operators/sequencer.py b/operators/sequencer.py index 7052703..b49f568 100644 --- a/operators/sequencer.py +++ b/operators/sequencer.py @@ -2,12 +2,13 @@ from os.path import expandvars, abspath from pathlib import Path import bpy from bpy.types import Operator -from bpy.props import (BoolProperty, StringProperty, EnumProperty) +from bpy.props import (BoolProperty, StringProperty, EnumProperty, FloatProperty) from vse_toolbox.sequencer_utils import (get_strips, rename_strips, set_channels, get_channel_index, new_text_strip, get_strip_at, get_channel_name) from vse_toolbox.bl_utils import get_scene_settings, get_strip_settings +from vse_toolbox.auto_splitter import AutoSplitter from vse_toolbox.constants import REVIEW_TEMPLATE_BLEND from shutil import copy2 @@ -172,6 +173,61 @@ class VSETB_OT_set_sequencer(Operator): return {"FINISHED"} +class VSETB_OT_auto_split(Operator): + """Launch subprocess with ffmpeg and python to find and create each + shots strips from video source""" + + bl_idname = "vse_toolbox.auto_split" + bl_label = "Auto Split" + bl_description = "Generate shots strips" + bl_options = {"REGISTER", "UNDO"} + + threshold: FloatProperty(name="Threshold", default=0.6, min=0, max=1) + selected_only: BoolProperty(name="Selected Only", default=True) + + def invoke(self, context, event): + return context.window_manager.invoke_props_dialog(self) + + def draw(self, context): + layout = self.layout + + col = layout.column() + col.use_property_split = True + col.use_property_decorate = False + + col.prop(self, 'threshold') + col.prop(self, 'selected_only') + + def execute(self, context): + scn = context.scene + first_frame = int(scn.frame_start) + + strips = get_strips('Movie') + if self.selected_only: + strips = context.selected_sequences + + sources = list(set([bpy.path.abspath(strip.filepath) for strip in strips if strip.type == 'MOVIE'])) + + splitter = AutoSplitter(sources) + splitter.launch_analysis(self.threshold) + + split_frames = [0] + split_frames += splitter.get_split_times(as_frame=True) + + for i, frame in enumerate(split_frames): + strip = scn.sequence_editor.sequences.new_effect( + f'tmp_shot_{str(i).zfill(3)}', + 'COLOR', + get_channel_index('Shots'), + frame_start=frame + first_frame, + frame_end=split_frames[i+1] + first_frame if i < len(split_frames) else strips[0].frame_final_end + ) + strip.blend_alpha = 0 + strip.color = (0.5, 0.5, 0.5) + + return {'FINISHED'} + + class VSETB_OT_set_stamps(Operator): bl_idname = "vse_toolbox.set_stamps" bl_label = "Set Stamps" @@ -625,6 +681,7 @@ def unregister_keymaps(): classes = ( VSETB_OT_rename, VSETB_OT_set_sequencer, + VSETB_OT_auto_split, VSETB_OT_set_stamps, VSETB_OT_show_waveform, VSETB_OT_previous_shot, diff --git a/ui/panels.py b/ui/panels.py index cf3c13d..c4e3172 100644 --- a/ui/panels.py +++ b/ui/panels.py @@ -179,6 +179,7 @@ class VSETB_PT_sequencer(VSETB_main, Panel): col = layout.column() col.operator('vse_toolbox.set_sequencer', text='Set-Up Sequencer', icon='SEQ_SEQUENCER') + col.operator('vse_toolbox.auto_split', text='Auto Split Shots') col.operator('vse_toolbox.strips_rename', text=f'Rename {channel}', icon='SORTALPHA') col.operator('vse_toolbox.set_stamps', text='Set Stamps', icon='COLOR') col.operator("vse_toolbox.collect_files", text='Collect Files', icon='PACKAGE') From b1bde01b575283d4b7a487af6a4511f4b353e2da Mon Sep 17 00:00:00 2001 From: "florentin.luce" Date: Fri, 24 May 2024 11:12:13 +0200 Subject: [PATCH 2/6] one movie by splitter --- auto_splitter.py | 6 +++--- operators/sequencer.py | 35 +++++++++++++++++++---------------- 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/auto_splitter.py b/auto_splitter.py index 84da28b..63c5bc7 100644 --- a/auto_splitter.py +++ b/auto_splitter.py @@ -7,16 +7,16 @@ from vse_toolbox import file_utils class AutoSplitter(object): - def __init__(self, paths, fps=None): + def __init__(self, path, fps=None): - self.paths = paths + self.path = path self.fps = fps self.log_path = AUTO_SPLITTER_LOG def launch_analysis(self, threshold=0.6): - ffmpeg_cmd = f"ffmpeg -i {str(self.paths[0])} -filter:v \"select='gt(scene,{threshold})',showinfo\" -f null - 2> {str(self.log_path)}" + ffmpeg_cmd = f"ffmpeg -i {str(self.path)} -filter:v \"select='gt(scene,{threshold})',showinfo\" -f null - 2> {str(self.log_path)}" print(ffmpeg_cmd) subprocess.call(ffmpeg_cmd, shell=True) diff --git a/operators/sequencer.py b/operators/sequencer.py index b49f568..b582d52 100644 --- a/operators/sequencer.py +++ b/operators/sequencer.py @@ -200,30 +200,33 @@ class VSETB_OT_auto_split(Operator): def execute(self, context): scn = context.scene - first_frame = int(scn.frame_start) strips = get_strips('Movie') if self.selected_only: strips = context.selected_sequences - sources = list(set([bpy.path.abspath(strip.filepath) for strip in strips if strip.type == 'MOVIE'])) + for strip in strips: - splitter = AutoSplitter(sources) - splitter.launch_analysis(self.threshold) + if strip.type != 'MOVIE': + continue - split_frames = [0] - split_frames += splitter.get_split_times(as_frame=True) + splitter = AutoSplitter(bpy.path.abspath(strip.filepath)) + splitter.launch_analysis(self.threshold) - for i, frame in enumerate(split_frames): - strip = scn.sequence_editor.sequences.new_effect( - f'tmp_shot_{str(i).zfill(3)}', - 'COLOR', - get_channel_index('Shots'), - frame_start=frame + first_frame, - frame_end=split_frames[i+1] + first_frame if i < len(split_frames) else strips[0].frame_final_end - ) - strip.blend_alpha = 0 - strip.color = (0.5, 0.5, 0.5) + split_frames = [0] + split_frames += splitter.get_split_times(as_frame=True) + + for i, frame in enumerate(split_frames): + + shot_strip = scn.sequence_editor.sequences.new_effect( + f'tmp_shot_{str(i).zfill(3)}', + 'COLOR', + get_channel_index('Shots'), + frame_start=frame + strip.frame_final_start, + frame_end=split_frames[i+1] + strip.frame_final_start if i+1 < len(split_frames) else strip.frame_final_end + ) + shot_strip.blend_alpha = 0 + shot_strip.color = (0.5, 0.5, 0.5) return {'FINISHED'} From 940cd1100f68c52d1b86bd67bc01890d1fd5fa8c Mon Sep 17 00:00:00 2001 From: "florentin.luce" Date: Mon, 27 May 2024 14:40:23 +0200 Subject: [PATCH 3/6] use modal and display shots created one after other --- auto_splitter.py | 106 +++++++++++++++++++++++++++++++++-------- constants.py | 2 - operators/sequencer.py | 48 ++++++++++++------- sequencer_utils.py | 17 ++++++- 4 files changed, 133 insertions(+), 40 deletions(-) diff --git a/auto_splitter.py b/auto_splitter.py index 63c5bc7..c19badd 100644 --- a/auto_splitter.py +++ b/auto_splitter.py @@ -1,36 +1,102 @@ +import os import re import subprocess -from vse_toolbox.constants import AUTO_SPLITTER_LOG -from vse_toolbox import file_utils +import bpy + +from vse_toolbox import bl_utils -class AutoSplitter(object): +def launch_split(movie_strip, threshold, frame_start=None, frame_end=None): + """Launch ffmpeg command to detect changing frames from a movie strip. - def __init__(self, path, fps=None): + Args: + movie_strip (bpy.types.Sequence): blender sequence strip to detect changes. + threshold (float): value of the detection factor (from 0 to 1). + frame_start (int, optional): first frame to detect. + Defaults to None. + frame_end (int, optional): last frame to detect. + Defaults to None. - self.path = path - self.fps = fps + Returns: + str: ffmpeg command log. + """ - self.log_path = AUTO_SPLITTER_LOG + path = bl_utils.abspath(movie_strip.filepath) + fps = bpy.context.scene.render.fps - def launch_analysis(self, threshold=0.6): + if frame_start is None: + frame_start = movie_strip.frame_final_start + if frame_end is None: + frame_end = movie_strip.frame_final_end - ffmpeg_cmd = f"ffmpeg -i {str(self.path)} -filter:v \"select='gt(scene,{threshold})',showinfo\" -f null - 2> {str(self.log_path)}" - print(ffmpeg_cmd) + frame_start -= movie_strip.frame_final_start + frame_end -= movie_strip.frame_final_start - subprocess.call(ffmpeg_cmd, shell=True) + # Launch ffmpeg command to split + ffmpeg_cmd = get_command(str(path), threshold, frame_start, frame_end, fps) - def get_split_times(self, as_frame=True): - log = file_utils.read_file(self.log_path) + print(ffmpeg_cmd) + process = subprocess.Popen( + ffmpeg_cmd, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + universal_newlines=True) - timecodes = re.findall(r'pts_time:([\d.]+)', log) + return process - if as_frame: - # convert timecode to frame number - if not self.fps: - self.fps = float(re.findall(r'([\d]+) fps', log)[0]) - return [round(float(time) * self.fps) for time in timecodes] +def get_command(path, threshold, frame_start, frame_end, fps): + """Generate the ffmpeg command which detect change from a movie. - return timecodes + Args: + path (_type_): path to detect changes. + threshold (_type_): value of the detection factor (from 0 to 1). + frame_start (_type_): first frame to detect. + frame_end (_type_): last frame to detect. + fps (_type_): framerate of the movie. + + Returns: + list: ffmpeg command as list for subprocess module. + """ + + start_time = frame_start/fps + end_time = frame_end/fps + + return [ + 'ffmpeg', + '-i', + str(path), + '-vf', + f"trim=start={start_time}:end={end_time}, select='gt(scene, {threshold})',showinfo", + '-f', + 'null', + '-' + ] + + +def get_split_time(log, as_frame=True, fps=None): + """Parse ffmpeg command lines to detect the timecode + + Args: + log (str): log to parse. + as_frame (bool, optional): if wanted the timecode as frame number. + Defaults to True. + fps (_type_, optional): framerate of the movie (mandatory if as_frame used). + Defaults to None. + + Returns: + _type_: _description_ + """ + timecodes = re.findall(r'pts_time:([\d.]+)', log) + + if not timecodes: + return + + timecode = timecodes[0] + + if as_frame: + # convert timecode to frame number + return round(float(timecode) * fps) + + return timecode diff --git a/constants.py b/constants.py index 7180420..4865f8c 100644 --- a/constants.py +++ b/constants.py @@ -37,5 +37,3 @@ ASSET_PREVIEWS = bpy.utils.previews.new() CASTING_BUFFER = CONFIG_DIR / 'casting.json' SPREADSHEET = [] - -AUTO_SPLITTER_LOG = CONFIG_DIR / 'auto_splitter.log' diff --git a/operators/sequencer.py b/operators/sequencer.py index b582d52..745931b 100644 --- a/operators/sequencer.py +++ b/operators/sequencer.py @@ -2,14 +2,14 @@ from os.path import expandvars, abspath from pathlib import Path import bpy from bpy.types import Operator -from bpy.props import (BoolProperty, StringProperty, EnumProperty, FloatProperty) +from bpy.props import (BoolProperty, StringProperty, FloatProperty) from vse_toolbox.sequencer_utils import (get_strips, rename_strips, set_channels, get_channel_index, new_text_strip, get_strip_at, get_channel_name) - + +from vse_toolbox import auto_splitter from vse_toolbox.bl_utils import get_scene_settings, get_strip_settings -from vse_toolbox.auto_splitter import AutoSplitter -from vse_toolbox.constants import REVIEW_TEMPLATE_BLEND +from vse_toolbox.sequencer_utils import create_shot_strip from shutil import copy2 @@ -199,8 +199,10 @@ class VSETB_OT_auto_split(Operator): col.prop(self, 'selected_only') def execute(self, context): - scn = context.scene + context.window_manager.modal_handler_add(self) + return {'PASS_THROUGH'} + def modal(self, context, event): strips = get_strips('Movie') if self.selected_only: strips = context.selected_sequences @@ -210,23 +212,35 @@ class VSETB_OT_auto_split(Operator): if strip.type != 'MOVIE': continue - splitter = AutoSplitter(bpy.path.abspath(strip.filepath)) - splitter.launch_analysis(self.threshold) + process = auto_splitter.launch_split(strip, self.threshold) - split_frames = [0] - split_frames += splitter.get_split_times(as_frame=True) + i = 1 + frame_start = 0 + for line in process.stdout: + frame_end = auto_splitter.get_split_time(line, fps=24) - for i, frame in enumerate(split_frames): + if not frame_end: + continue - shot_strip = scn.sequence_editor.sequences.new_effect( + create_shot_strip( f'tmp_shot_{str(i).zfill(3)}', - 'COLOR', - get_channel_index('Shots'), - frame_start=frame + strip.frame_final_start, - frame_end=split_frames[i+1] + strip.frame_final_start if i+1 < len(split_frames) else strip.frame_final_end + start=frame_start+strip.frame_final_start, + end=frame_end+strip.frame_final_start + ) + + i += 1 + frame_start = frame_end + + bpy.ops.wm.redraw_timer(type='DRAW_WIN_SWAP', iterations=1) + + process.wait() + + # last strip: + create_shot_strip( + f'tmp_shot_{str(i).zfill(3)}', + start=frame_start+strip.frame_final_start, + end=strip.frame_final_end ) - shot_strip.blend_alpha = 0 - shot_strip.color = (0.5, 0.5, 0.5) return {'FINISHED'} diff --git a/sequencer_utils.py b/sequencer_utils.py index 5cbd720..87147ca 100644 --- a/sequencer_utils.py +++ b/sequencer_utils.py @@ -582,4 +582,19 @@ def update_text_strips(scene): strip['text_pattern'] = strip.text if 'text_pattern' in strip.keys(): - strip.text = strip['text_pattern'].format_map(MissingKey(**format_data)) \ No newline at end of file + strip.text = strip['text_pattern'].format_map(MissingKey(**format_data)) + + +def create_shot_strip(name, start, end): + + shot_strip = bpy.context.scene.sequence_editor.sequences.new_effect( + name, + 'COLOR', + get_channel_index('Shots'), + frame_start=start, + frame_end=end + ) + shot_strip.blend_alpha = 0 + shot_strip.color = (0.5, 0.5, 0.5) + + return shot_strip From 61f6ec3896e2da40ba8ad5522bc825e01e573f04 Mon Sep 17 00:00:00 2001 From: "florentin.luce" Date: Tue, 28 May 2024 14:49:28 +0200 Subject: [PATCH 4/6] improve usage --- auto_splitter.py | 10 +++--- operators/sequencer.py | 69 +++++++++++++++++++++++++++++++----------- 2 files changed, 57 insertions(+), 22 deletions(-) diff --git a/auto_splitter.py b/auto_splitter.py index c19badd..e6b76e7 100644 --- a/auto_splitter.py +++ b/auto_splitter.py @@ -26,12 +26,14 @@ def launch_split(movie_strip, threshold, frame_start=None, frame_end=None): fps = bpy.context.scene.render.fps if frame_start is None: - frame_start = movie_strip.frame_final_start + frame_start = 0 if frame_end is None: - frame_end = movie_strip.frame_final_end + frame_end = movie_strip.frame_duration - frame_start -= movie_strip.frame_final_start - frame_end -= movie_strip.frame_final_start + frame_start = frame_start - movie_strip.frame_start + + #frame_start += movie_strip.frame_offset_start + #frame_end -= movie_strip.frame_offset_end # Launch ffmpeg command to split ffmpeg_cmd = get_command(str(path), threshold, frame_start, frame_end, fps) diff --git a/operators/sequencer.py b/operators/sequencer.py index 745931b..5c85669 100644 --- a/operators/sequencer.py +++ b/operators/sequencer.py @@ -2,14 +2,15 @@ from os.path import expandvars, abspath from pathlib import Path import bpy from bpy.types import Operator -from bpy.props import (BoolProperty, StringProperty, FloatProperty) +from bpy.props import (BoolProperty, StringProperty, FloatProperty, + IntProperty, EnumProperty) from vse_toolbox.sequencer_utils import (get_strips, rename_strips, set_channels, - get_channel_index, new_text_strip, get_strip_at, get_channel_name) - + get_channel_index, new_text_strip, get_strip_at, get_channel_name, + create_shot_strip) + from vse_toolbox import auto_splitter from vse_toolbox.bl_utils import get_scene_settings, get_strip_settings -from vse_toolbox.sequencer_utils import create_shot_strip from shutil import copy2 @@ -183,9 +184,21 @@ class VSETB_OT_auto_split(Operator): bl_options = {"REGISTER", "UNDO"} threshold: FloatProperty(name="Threshold", default=0.6, min=0, max=1) - selected_only: BoolProperty(name="Selected Only", default=True) + frame_first: IntProperty(name='Start Split') + frame_last: IntProperty(name='End Split') + movie_channel_name: EnumProperty( + items=lambda self, ctx: ((c.name, c.name, '') for c in ctx.scene.sequence_editor.channels), + name='Movie Channel') def invoke(self, context, event): + + self.frame_first = context.scene.frame_start + self.frame_last = context.scene.frame_end + + if context.selected_sequences: + self.frame_first = min([s.frame_final_start for s in context.selected_sequences]) + self.frame_last = max([s.frame_final_end for s in context.selected_sequences]) + return context.window_manager.invoke_props_dialog(self) def draw(self, context): @@ -196,6 +209,12 @@ class VSETB_OT_auto_split(Operator): col.use_property_decorate = False col.prop(self, 'threshold') + col.prop(self, 'movie_channel_name') + + split_col = col.column(align=True) + split_col.prop(self, 'frame_first', text='Frame Split First') + split_col.prop(self, 'frame_last', text='Last') + col.prop(self, 'selected_only') def execute(self, context): @@ -203,29 +222,42 @@ class VSETB_OT_auto_split(Operator): return {'PASS_THROUGH'} def modal(self, context, event): - strips = get_strips('Movie') - if self.selected_only: - strips = context.selected_sequences + + strips = get_strips(channel=self.movie_channel_name) for strip in strips: if strip.type != 'MOVIE': continue - process = auto_splitter.launch_split(strip, self.threshold) + # Skip strip outside the frame range to create shot from. + if strip.frame_final_start >= self.frame_last or strip.frame_final_end <= self.frame_first: + continue + + process = auto_splitter.launch_split(strip, self.threshold, frame_start=self.frame_first, frame_end=self.frame_last) i = 1 - frame_start = 0 + frame_start = self.frame_first for line in process.stdout: + + # Get frame split from the movie timeline (not from blender strips timeline) frame_end = auto_splitter.get_split_time(line, fps=24) if not frame_end: continue + # Convert movie frame to strips frame + if frame_start+int(strip.frame_start) < self.frame_first: + frame_start = self.frame_first + + frame_end += int(strip.frame_final_start) + if frame_end > self.frame_last: + frame_end = self.frame_last + create_shot_strip( f'tmp_shot_{str(i).zfill(3)}', - start=frame_start+strip.frame_final_start, - end=frame_end+strip.frame_final_start + start=frame_start, + end=frame_end ) i += 1 @@ -235,12 +267,13 @@ class VSETB_OT_auto_split(Operator): process.wait() - # last strip: - create_shot_strip( - f'tmp_shot_{str(i).zfill(3)}', - start=frame_start+strip.frame_final_start, - end=strip.frame_final_end - ) + # Last strip: + if frame_start < self.frame_last: + create_shot_strip( + f'tmp_shot_{str(i).zfill(3)}', + start=frame_start, + end=self.frame_last + ) return {'FINISHED'} From 15bd62be2ffcd4c6167c2ba470d306a732606cea Mon Sep 17 00:00:00 2001 From: "florentin.luce" Date: Tue, 28 May 2024 14:58:49 +0200 Subject: [PATCH 5/6] fix bug --- operators/sequencer.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/operators/sequencer.py b/operators/sequencer.py index 5c85669..8d4cbd7 100644 --- a/operators/sequencer.py +++ b/operators/sequencer.py @@ -225,6 +225,8 @@ class VSETB_OT_auto_split(Operator): strips = get_strips(channel=self.movie_channel_name) + i = 1 + frame_start = self.frame_first for strip in strips: if strip.type != 'MOVIE': @@ -236,8 +238,6 @@ class VSETB_OT_auto_split(Operator): process = auto_splitter.launch_split(strip, self.threshold, frame_start=self.frame_first, frame_end=self.frame_last) - i = 1 - frame_start = self.frame_first for line in process.stdout: # Get frame split from the movie timeline (not from blender strips timeline) @@ -267,13 +267,13 @@ class VSETB_OT_auto_split(Operator): process.wait() - # Last strip: - if frame_start < self.frame_last: - create_shot_strip( - f'tmp_shot_{str(i).zfill(3)}', - start=frame_start, - end=self.frame_last - ) + # Last strip: + if frame_start < self.frame_last: + create_shot_strip( + f'tmp_shot_{str(i).zfill(3)}', + start=frame_start, + end=self.frame_last + ) return {'FINISHED'} From fe5e6728ad97ba1538a7649b1afd8500b9e6c332 Mon Sep 17 00:00:00 2001 From: "florentin.luce" Date: Tue, 28 May 2024 15:05:19 +0200 Subject: [PATCH 6/6] remove legacy prop --- operators/sequencer.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/operators/sequencer.py b/operators/sequencer.py index 8d4cbd7..e304399 100644 --- a/operators/sequencer.py +++ b/operators/sequencer.py @@ -215,8 +215,6 @@ class VSETB_OT_auto_split(Operator): split_col.prop(self, 'frame_first', text='Frame Split First') split_col.prop(self, 'frame_last', text='Last') - col.prop(self, 'selected_only') - def execute(self, context): context.window_manager.modal_handler_add(self) return {'PASS_THROUGH'}