Split auto (UI + Core) - 1st version

pull/6/head
florentin.luce 2024-05-24 09:44:19 +02:00
parent b68b43e3e1
commit a30e16606b
5 changed files with 100 additions and 4 deletions

36
auto_splitter.py Normal file
View File

@ -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

View File

@ -36,4 +36,6 @@ ASSET_PREVIEWS = bpy.utils.previews.new()
CASTING_BUFFER = CONFIG_DIR / 'casting.json' CASTING_BUFFER = CONFIG_DIR / 'casting.json'
SPREADSHEET = [] SPREADSHEET = []
AUTO_SPLITTER_LOG = CONFIG_DIR / 'auto_splitter.log'

View File

@ -89,9 +89,9 @@ def norm_name(string, separator='_', format=str.lower, padding=0):
return string return string
def read_file(path): 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: if not path:
print('Try to read empty file') print('Try to read empty file')

View File

@ -2,12 +2,13 @@ from os.path import expandvars, abspath
from pathlib import Path from pathlib import Path
import bpy import bpy
from bpy.types import Operator 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, 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)
from vse_toolbox.bl_utils import get_scene_settings, get_strip_settings 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.constants import REVIEW_TEMPLATE_BLEND
from shutil import copy2 from shutil import copy2
@ -172,6 +173,61 @@ class VSETB_OT_set_sequencer(Operator):
return {"FINISHED"} 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): class VSETB_OT_set_stamps(Operator):
bl_idname = "vse_toolbox.set_stamps" bl_idname = "vse_toolbox.set_stamps"
bl_label = "Set Stamps" bl_label = "Set Stamps"
@ -625,6 +681,7 @@ def unregister_keymaps():
classes = ( classes = (
VSETB_OT_rename, VSETB_OT_rename,
VSETB_OT_set_sequencer, VSETB_OT_set_sequencer,
VSETB_OT_auto_split,
VSETB_OT_set_stamps, VSETB_OT_set_stamps,
VSETB_OT_show_waveform, VSETB_OT_show_waveform,
VSETB_OT_previous_shot, VSETB_OT_previous_shot,

View File

@ -179,6 +179,7 @@ class VSETB_PT_sequencer(VSETB_main, Panel):
col = layout.column() col = layout.column()
col.operator('vse_toolbox.set_sequencer', text='Set-Up Sequencer', icon='SEQ_SEQUENCER') 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.strips_rename', text=f'Rename {channel}', icon='SORTALPHA')
col.operator('vse_toolbox.set_stamps', text='Set Stamps', icon='COLOR') col.operator('vse_toolbox.set_stamps', text='Set Stamps', icon='COLOR')
col.operator("vse_toolbox.collect_files", text='Collect Files', icon='PACKAGE') col.operator("vse_toolbox.collect_files", text='Collect Files', icon='PACKAGE')