From 5c35bae0e3f2db5ccf147da8149607623e9427a5 Mon Sep 17 00:00:00 2001 From: Christophe SEUX Date: Sat, 22 Apr 2023 21:12:21 +0200 Subject: [PATCH] waveform --- operators/operators.py | 92 ++++++++++++++++++++++------------------- panels.py | 8 +++- properties.py | 19 +++++++++ sequencer_utils.py | 93 +++++++++++++++++++++++++++++++++++++----- 4 files changed, 159 insertions(+), 53 deletions(-) diff --git a/operators/operators.py b/operators/operators.py index 8e1b559..6290086 100644 --- a/operators/operators.py +++ b/operators/operators.py @@ -83,28 +83,12 @@ class VSETB_OT_tracker_connect(Operator): return {"CANCELLED"} -def get_custom_name_items(self, context): - settings = get_scene_settings() - project = settings.active_project - return [(m.field_name, m.name, '') for m in project.metadata_types if m.entity_type=='ASSET'] - class VSETB_OT_export_spreadsheet(Operator): bl_idname = "vse_toolbox.export_spreadsheet" bl_label = "Export Spreadsheet" bl_description = "Export Shot data in a table as a csv or an xlsl" bl_options = {"REGISTER", "UNDO"} - format : EnumProperty(items=[(i, i, '') for i in ('CSV', 'XLSX')]) - separator : StringProperty(default='\\n') - delimiter : StringProperty(default=';') - export_path : StringProperty(default='//export') - use_custom_name : BoolProperty(default=False) - custom_name : EnumProperty(items=get_custom_name_items, - description='Use a custom name for asset using a metadata value') - - open_folder : BoolProperty(default=False) - show_settings : BoolProperty(default=False) - @classmethod def poll(cls, context): settings = get_scene_settings() @@ -120,6 +104,7 @@ class VSETB_OT_export_spreadsheet(Operator): scn = context.scene settings = get_scene_settings() project = settings.active_project + options = project.spreadsheet_options layout = self.layout @@ -135,30 +120,31 @@ class VSETB_OT_export_spreadsheet(Operator): row = col.row(align=True, heading='Custom Name') #row.use_property_split = True - row.prop(self, 'use_custom_name', text='') + row.prop(options, 'use_custom_name', text='') sub = row.row(align=True) - sub.enabled = self.use_custom_name - sub.prop(self, 'custom_name', text='') + sub.enabled = options.use_custom_name + sub.prop(options, 'custom_name', text='') col.separator() row = col.row(align=False) - row.prop(self, "format", expand=True, text='Format') - row.prop(self, 'show_settings', text='', icon='PREFERENCES') - if self.show_settings: - col.prop(self, "separator", expand=True, text='Separator') - if self.format == 'CSV': - col.prop(self, "delimiter", expand=True, text='Delimiter') + row.prop(options, "format", expand=True, text='Format') + row.prop(options, 'show_settings', text='', icon='PREFERENCES') + if options.show_settings: + col.prop(options, "separator", expand=True, text='Separator') + if options.format == 'CSV': + col.prop(options, "delimiter", expand=True, text='Delimiter') col.separator() - col.prop(self, 'open_folder', text='Open Folder') - col.prop(self, 'export_path', text='Export Path') + col.prop(options, 'open_folder', text='Open Folder') + col.prop(options, 'export_path', text='Export Path') def execute(self, context): #self.report({'ERROR'}, f'Export not implemented yet.') prefs = get_addon_prefs() settings = get_scene_settings() project = settings.active_project + options = project.spreadsheet_options episode = settings.active_episode cells = [cell for cell in project.spreadsheet if cell.enabled] @@ -167,8 +153,8 @@ class VSETB_OT_export_spreadsheet(Operator): # Header rows.append([cell.export_name for cell in cells]) - separator = self.separator.replace('\\n', '\n').replace('\\t', '\t').replace('\\r', '\r') - delimiter = self.delimiter.replace('\\n', '\n').replace('\\t', '\t').replace('\\r', '\r') + separator = options.separator.replace('\\n', '\n').replace('\\t', '\t').replace('\\r', '\r') + delimiter = options.delimiter.replace('\\n', '\n').replace('\\t', '\t').replace('\\r', '\r') for strip in get_strips('Shots'): row = [] @@ -182,11 +168,11 @@ class VSETB_OT_export_spreadsheet(Operator): if not asset.asset_type == cell.name: continue - if self.use_custom_name: - if asset.get('metadata', {}).get(self.custom_name): - asset_castings.append(asset['metadata'][self.custom_name]) + if options.use_custom_name: + if asset.get('metadata', {}).get(options.custom_name): + asset_castings.append(asset['metadata'][options.custom_name]) else: - self.report({'ERROR'}, f'The asset {asset.tracker_name} has no data {self.custom_name}') + self.report({'ERROR'}, f'The asset {asset.tracker_name} has no data {options.custom_name}') else: asset_castings.append(asset.tracker_name) @@ -206,7 +192,7 @@ class VSETB_OT_export_spreadsheet(Operator): #print(rows) - export_path = Path(os.path.abspath(bpy.path.abspath(self.export_path))) + export_path = Path(os.path.abspath(bpy.path.abspath(options.export_path))) export_name = export_path.name if export_path.suffix or export_name.endswith('{ext}'): @@ -221,7 +207,7 @@ class VSETB_OT_export_spreadsheet(Operator): date = datetime.now().strftime('%Y_%m_%d') project_name = project.name.replace(' ', '_').lower() episode_name = episode.name.replace(' ', '_').lower() if episode else 'episode' - ext = self.format.lower() + ext = options.format.lower() export_name = export_name.format(date=date, project=project_name, episode=episode_name, tracker=settings.tracker_name.lower(), ext=ext) @@ -231,14 +217,14 @@ class VSETB_OT_export_spreadsheet(Operator): #2023_04_11_kitsu_boris_ep01_shots export_path.parent.mkdir(parents=True, exist_ok=True) - if self.format == 'CSV': + if options.format == 'CSV': print('Writing .csv file to', export_path) with open(str(export_path), 'w', newline='\n', encoding='utf-8') as f: - writer = csv.writer(f, delimiter=self.delimiter) + writer = csv.writer(f, delimiter=options.delimiter) for row in rows: writer.writerow(row) - elif self.format == 'XLSX': + elif options.format == 'XLSX': try: import openpyxl except ModuleNotFoundError(): @@ -259,7 +245,7 @@ class VSETB_OT_export_spreadsheet(Operator): # Save the file workbook.save(str(export_path)) - if self.open_folder: + if options.open_folder: open_file(export_path, select=True) @@ -745,8 +731,8 @@ class VSETB_OT_rename(Operator): bl_description = "Rename Strips" bl_options = {"REGISTER", "UNDO"} - template : StringProperty(name="Strip Template Name", default="") - increment : IntProperty(name="Name Increment", default=0) + template : StringProperty(name="Strip Name", default="") + increment : IntProperty(name="Increment", default=0) channel_name : StringProperty(name="Channel Name", default="") #selected_only : BoolProperty(name="Selected Only", default=False) start_number : IntProperty(name="Start Number", default=0, min=0) @@ -838,6 +824,27 @@ class VSETB_OT_render(Operator): return {"FINISHED"} +class VSETB_OT_show_waveform(Operator): + bl_idname = "vse_toolbox.show_waveform" + bl_label = "Show Waveform" + bl_description = "Show Waveform of all audio strips" + bl_options = {"REGISTER", "UNDO"} + + enabled : BoolProperty(default=True) + + @classmethod + def poll(cls, context): + return True + + def execute(self, context): + scn = context.scene + + for strip in get_strips(channel='Audio'): + strip.show_waveform = self.enabled + + return {"FINISHED"} + + class VSETB_OT_set_sequencer(Operator): bl_idname = "vse_toolbox.set_sequencer" bl_label = "Set Sequencer" @@ -1193,7 +1200,8 @@ classes = ( VSETB_OT_set_sequencer, VSETB_OT_tracker_connect, VSETB_OT_set_stamps, - VSETB_OT_upload_to_tracker + VSETB_OT_upload_to_tracker, + VSETB_OT_show_waveform ) def register(): diff --git a/panels.py b/panels.py index 9c20baa..1f9f30c 100644 --- a/panels.py +++ b/panels.py @@ -7,7 +7,7 @@ from bpy.types import Panel from vse_toolbox.bl_utils import (get_addon_prefs, get_scene_settings, get_strip_settings) from vse_toolbox.constants import ASSET_PREVIEWS -from vse_toolbox.sequencer_utils import (set_active_strip, get_channel_name) +from vse_toolbox.sequencer_utils import (set_active_strip, get_channel_name, get_strips) class VSETB_main: @@ -74,6 +74,12 @@ class VSETB_PT_sequencer(VSETB_main, Panel): def draw_header_preset(self, context): settings = get_scene_settings() + + audio_strips = get_strips('Audio') + + depress = any(s.show_waveform for s in audio_strips) + self.layout.operator('vse_toolbox.show_waveform', text="", icon="IPO_ELASTIC", depress=depress).enabled = not depress + ico = ("RESTRICT_SELECT_OFF" if settings.auto_select_strip else "RESTRICT_SELECT_ON") self.layout.prop(settings, "auto_select_strip", text="", icon=ico) diff --git a/properties.py b/properties.py index 4cec2ef..9eb456b 100644 --- a/properties.py +++ b/properties.py @@ -155,6 +155,23 @@ class Episode(PropertyGroup): settings = get_scene_settings() return self.get(settings.project_name) + +def get_custom_name_items(self, context): + settings = get_scene_settings() + project = settings.active_project + return [(m.field_name, m.name, '') for m in project.metadata_types if m.entity_type=='ASSET'] + +class SpreadsheetOptions(PropertyGroup): + format : EnumProperty(items=[(i, i, '') for i in ('CSV', 'XLSX')]) + separator : StringProperty(default='\\n') + delimiter : StringProperty(default=';') + export_path : StringProperty(default='//export') + use_custom_name : BoolProperty(default=False) + custom_name : EnumProperty(items=get_custom_name_items, + description='Use a custom name for asset using a metadata value') + + open_folder : BoolProperty(default=False) + show_settings : BoolProperty(default=False) class Project(PropertyGroup): id : StringProperty(default='') @@ -188,6 +205,7 @@ class Project(PropertyGroup): task_types : CollectionProperty(type=TaskType) task_statuses : CollectionProperty(type=TaskStatus) + spreadsheet_options : PointerProperty(type=SpreadsheetOptions) spreadsheet : CollectionProperty(type=SpreadsheetCell) spreadsheet_index : IntProperty(name='Spreadsheet Index', default=0) @@ -396,6 +414,7 @@ classes=( Metadata, MetadataType, TaskType, + SpreadsheetOptions, Project, VSETB_UL_spreadsheet, VSETB_UL_casting, diff --git a/sequencer_utils.py b/sequencer_utils.py index 024c5ae..be95ebb 100644 --- a/sequencer_utils.py +++ b/sequencer_utils.py @@ -9,7 +9,9 @@ from bpy.app.handlers import persistent from vse_toolbox.bl_utils import get_scene_settings, get_strip_settings from vse_toolbox.constants import SOUND_SUFFIXES -import multiprocessing +#import multiprocessing +#from multiprocessing.pool import ThreadPool +import subprocess def new_text_strip(name='Text', channel=0, start=0, end=50, text='Text', font_size=48, @@ -131,18 +133,69 @@ def get_strip_render_path(strip, template): render_path = template.format(strip_name=strip.name, ext=suffix[1:]) return Path(os.path.abspath(bpy.path.abspath(render_path))) -def render_strips(strips, template): - scn = bpy.context.scene - scene_start = scn.frame_start - scene_end = scn.frame_end - render_path = scn.render.filepath +''' +# def render_strip_background(blender_path, filepath, start, end, output): +# cmd = [ +# blender_path, '-b', '--factory-startup', str(tmp_path), '-a', +# '-s', str(start), '-e', str(end), +# '-o', str(output) +# ] - # pool = multiprocessing.Pool(4) - # p.map(func, range(1, 100)) +# print(cmd) +# process = subprocess.call(cmd) + +def render_strips(strips, template): + from functools import partial + scn = bpy.context.scene + # scene_start = scn.frame_start + # scene_end = scn.frame_end + # render_path = scn.render.filepath + + tmp_name = Path(bpy.data.filepath).name if bpy.data.filepath else 'Untitled.blend' + tmp_path = Path(bpy.app.tempdir, tmp_name) + + bpy.ops.wm.save_as_mainfile(filepath=str(tmp_path), copy=True) + + + script_code = dedent(f""" + import bpy + + for + + """) + + script_path = Path(bpy.app.tempdir) / 'bundle_library.py' + script_path.write_text(script_code) + + cmd = [bpy.app.binary_path, tmp_path, '--python', ] + + # nb_threads = min(multiprocessing.cpu_count()-2, 8) + # print(nb_threads) + + # pool = multiprocessing.Pool(nb_threads) + + #arguments = [(bpy.app.binary_path, str(tmp_path), s.frame_final_start, s.frame_final_end-1, str(get_strip_render_path(s, template))) for s in strips] + #print(arguments) + #pool.starmap(render_strip_background, arguments) # def render_strip_background(index): # cmd = [bpy.app.binary_path, etc] - # process = subprocess.Popen(substr + " --index {}".format(index), shell=True, stdout=subprocess.PIPE) + # process = subprocess.Popen(cmd) + + # pool = ThreadPool(nb_threads) + + # for strip in strips: + # start = strip.frame_final_start + # end = strip.frame_final_end-1 + # output = str(get_strip_render_path(strip, template)) + + # cmd = [ + # bpy.app.binary_path, '-b', str(tmp_path), + # '-s', str(start), '-e', str(end), '-a', + # '-o', str(output) + # ] + + for strip in strips: @@ -161,6 +214,25 @@ def render_strips(strips, template): bpy.ops.render.opengl(animation=True, sequencer=True) + scn.frame_start = scene_start + scn.frame_end = scene_end + scn.render.filepath = render_path +''' + +def render_strips(strips, template): + scn = bpy.context.scene + scene_start = scn.frame_start + scene_end = scn.frame_end + render_path = scn.render.filepath + + for strip in strips: + scn.frame_start = strip.frame_final_start + scn.frame_end = strip.frame_final_end - 1 + scn.render.filepath = str(get_strip_render_path(strip, template)) + + print(f'Render Strip to {scn.render.filepath}') + bpy.ops.render.opengl(animation=True, sequencer=True) + scn.frame_start = scene_start scn.frame_end = scene_end scn.render.filepath = render_path @@ -278,7 +350,8 @@ def import_sound(filepath): if bpy.data.is_saved: strip.sound.filepath = bpy.path.relpath(str(filepath)) - strip.show_waveform = True + strip.show_waveform = True if strip.frame_final_duration < 10000 else False + return strip def clean_sequencer(edit=False, movie=False, sound=False):