pull/5/head
Christophe SEUX 2023-04-22 21:12:21 +02:00
parent d0230672f4
commit 5c35bae0e3
4 changed files with 159 additions and 53 deletions

View File

@ -83,28 +83,12 @@ class VSETB_OT_tracker_connect(Operator):
return {"CANCELLED"} 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): class VSETB_OT_export_spreadsheet(Operator):
bl_idname = "vse_toolbox.export_spreadsheet" bl_idname = "vse_toolbox.export_spreadsheet"
bl_label = "Export Spreadsheet" bl_label = "Export Spreadsheet"
bl_description = "Export Shot data in a table as a csv or an xlsl" bl_description = "Export Shot data in a table as a csv or an xlsl"
bl_options = {"REGISTER", "UNDO"} 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 @classmethod
def poll(cls, context): def poll(cls, context):
settings = get_scene_settings() settings = get_scene_settings()
@ -120,6 +104,7 @@ class VSETB_OT_export_spreadsheet(Operator):
scn = context.scene scn = context.scene
settings = get_scene_settings() settings = get_scene_settings()
project = settings.active_project project = settings.active_project
options = project.spreadsheet_options
layout = self.layout layout = self.layout
@ -135,30 +120,31 @@ class VSETB_OT_export_spreadsheet(Operator):
row = col.row(align=True, heading='Custom Name') row = col.row(align=True, heading='Custom Name')
#row.use_property_split = True #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 = row.row(align=True)
sub.enabled = self.use_custom_name sub.enabled = options.use_custom_name
sub.prop(self, 'custom_name', text='') sub.prop(options, 'custom_name', text='')
col.separator() col.separator()
row = col.row(align=False) row = col.row(align=False)
row.prop(self, "format", expand=True, text='Format') row.prop(options, "format", expand=True, text='Format')
row.prop(self, 'show_settings', text='', icon='PREFERENCES') row.prop(options, 'show_settings', text='', icon='PREFERENCES')
if self.show_settings: if options.show_settings:
col.prop(self, "separator", expand=True, text='Separator') col.prop(options, "separator", expand=True, text='Separator')
if self.format == 'CSV': if options.format == 'CSV':
col.prop(self, "delimiter", expand=True, text='Delimiter') col.prop(options, "delimiter", expand=True, text='Delimiter')
col.separator() col.separator()
col.prop(self, 'open_folder', text='Open Folder') col.prop(options, 'open_folder', text='Open Folder')
col.prop(self, 'export_path', text='Export Path') col.prop(options, 'export_path', text='Export Path')
def execute(self, context): def execute(self, context):
#self.report({'ERROR'}, f'Export not implemented yet.') #self.report({'ERROR'}, f'Export not implemented yet.')
prefs = get_addon_prefs() prefs = get_addon_prefs()
settings = get_scene_settings() settings = get_scene_settings()
project = settings.active_project project = settings.active_project
options = project.spreadsheet_options
episode = settings.active_episode episode = settings.active_episode
cells = [cell for cell in project.spreadsheet if cell.enabled] cells = [cell for cell in project.spreadsheet if cell.enabled]
@ -167,8 +153,8 @@ class VSETB_OT_export_spreadsheet(Operator):
# Header # Header
rows.append([cell.export_name for cell in cells]) rows.append([cell.export_name for cell in cells])
separator = self.separator.replace('\\n', '\n').replace('\\t', '\t').replace('\\r', '\r') separator = options.separator.replace('\\n', '\n').replace('\\t', '\t').replace('\\r', '\r')
delimiter = self.delimiter.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'): for strip in get_strips('Shots'):
row = [] row = []
@ -182,11 +168,11 @@ class VSETB_OT_export_spreadsheet(Operator):
if not asset.asset_type == cell.name: if not asset.asset_type == cell.name:
continue continue
if self.use_custom_name: if options.use_custom_name:
if asset.get('metadata', {}).get(self.custom_name): if asset.get('metadata', {}).get(options.custom_name):
asset_castings.append(asset['metadata'][self.custom_name]) asset_castings.append(asset['metadata'][options.custom_name])
else: 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: else:
asset_castings.append(asset.tracker_name) asset_castings.append(asset.tracker_name)
@ -206,7 +192,7 @@ class VSETB_OT_export_spreadsheet(Operator):
#print(rows) #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 export_name = export_path.name
if export_path.suffix or export_name.endswith('{ext}'): 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') date = datetime.now().strftime('%Y_%m_%d')
project_name = project.name.replace(' ', '_').lower() project_name = project.name.replace(' ', '_').lower()
episode_name = episode.name.replace(' ', '_').lower() if episode else 'episode' 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, export_name = export_name.format(date=date, project=project_name,
episode=episode_name, tracker=settings.tracker_name.lower(), ext=ext) 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 #2023_04_11_kitsu_boris_ep01_shots
export_path.parent.mkdir(parents=True, exist_ok=True) export_path.parent.mkdir(parents=True, exist_ok=True)
if self.format == 'CSV': if options.format == 'CSV':
print('Writing .csv file to', export_path) print('Writing .csv file to', export_path)
with open(str(export_path), 'w', newline='\n', encoding='utf-8') as f: 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: for row in rows:
writer.writerow(row) writer.writerow(row)
elif self.format == 'XLSX': elif options.format == 'XLSX':
try: try:
import openpyxl import openpyxl
except ModuleNotFoundError(): except ModuleNotFoundError():
@ -259,7 +245,7 @@ class VSETB_OT_export_spreadsheet(Operator):
# Save the file # Save the file
workbook.save(str(export_path)) workbook.save(str(export_path))
if self.open_folder: if options.open_folder:
open_file(export_path, select=True) open_file(export_path, select=True)
@ -745,8 +731,8 @@ class VSETB_OT_rename(Operator):
bl_description = "Rename Strips" bl_description = "Rename Strips"
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
template : StringProperty(name="Strip Template Name", default="") template : StringProperty(name="Strip Name", default="")
increment : IntProperty(name="Name Increment", default=0) increment : IntProperty(name="Increment", default=0)
channel_name : StringProperty(name="Channel Name", default="") channel_name : StringProperty(name="Channel Name", default="")
#selected_only : BoolProperty(name="Selected Only", default=False) #selected_only : BoolProperty(name="Selected Only", default=False)
start_number : IntProperty(name="Start Number", default=0, min=0) start_number : IntProperty(name="Start Number", default=0, min=0)
@ -838,6 +824,27 @@ class VSETB_OT_render(Operator):
return {"FINISHED"} 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): class VSETB_OT_set_sequencer(Operator):
bl_idname = "vse_toolbox.set_sequencer" bl_idname = "vse_toolbox.set_sequencer"
bl_label = "Set Sequencer" bl_label = "Set Sequencer"
@ -1193,7 +1200,8 @@ classes = (
VSETB_OT_set_sequencer, VSETB_OT_set_sequencer,
VSETB_OT_tracker_connect, VSETB_OT_tracker_connect,
VSETB_OT_set_stamps, VSETB_OT_set_stamps,
VSETB_OT_upload_to_tracker VSETB_OT_upload_to_tracker,
VSETB_OT_show_waveform
) )
def register(): def register():

View File

@ -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.bl_utils import (get_addon_prefs, get_scene_settings, get_strip_settings)
from vse_toolbox.constants import ASSET_PREVIEWS 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: class VSETB_main:
@ -74,6 +74,12 @@ class VSETB_PT_sequencer(VSETB_main, Panel):
def draw_header_preset(self, context): def draw_header_preset(self, context):
settings = get_scene_settings() 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") ico = ("RESTRICT_SELECT_OFF" if settings.auto_select_strip else "RESTRICT_SELECT_ON")
self.layout.prop(settings, "auto_select_strip", text="", icon=ico) self.layout.prop(settings, "auto_select_strip", text="", icon=ico)

View File

@ -155,6 +155,23 @@ class Episode(PropertyGroup):
settings = get_scene_settings() settings = get_scene_settings()
return self.get(settings.project_name) 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): class Project(PropertyGroup):
id : StringProperty(default='') id : StringProperty(default='')
@ -188,6 +205,7 @@ class Project(PropertyGroup):
task_types : CollectionProperty(type=TaskType) task_types : CollectionProperty(type=TaskType)
task_statuses : CollectionProperty(type=TaskStatus) task_statuses : CollectionProperty(type=TaskStatus)
spreadsheet_options : PointerProperty(type=SpreadsheetOptions)
spreadsheet : CollectionProperty(type=SpreadsheetCell) spreadsheet : CollectionProperty(type=SpreadsheetCell)
spreadsheet_index : IntProperty(name='Spreadsheet Index', default=0) spreadsheet_index : IntProperty(name='Spreadsheet Index', default=0)
@ -396,6 +414,7 @@ classes=(
Metadata, Metadata,
MetadataType, MetadataType,
TaskType, TaskType,
SpreadsheetOptions,
Project, Project,
VSETB_UL_spreadsheet, VSETB_UL_spreadsheet,
VSETB_UL_casting, VSETB_UL_casting,

View File

@ -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.bl_utils import get_scene_settings, get_strip_settings
from vse_toolbox.constants import SOUND_SUFFIXES 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, 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:]) render_path = template.format(strip_name=strip.name, ext=suffix[1:])
return Path(os.path.abspath(bpy.path.abspath(render_path))) return Path(os.path.abspath(bpy.path.abspath(render_path)))
def render_strips(strips, template): '''
scn = bpy.context.scene # def render_strip_background(blender_path, filepath, start, end, output):
scene_start = scn.frame_start # cmd = [
scene_end = scn.frame_end # blender_path, '-b', '--factory-startup', str(tmp_path), '-a',
render_path = scn.render.filepath # '-s', str(start), '-e', str(end),
# '-o', str(output)
# ]
# pool = multiprocessing.Pool(4) # print(cmd)
# p.map(func, range(1, 100)) # 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): # def render_strip_background(index):
# cmd = [bpy.app.binary_path, etc] # 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: for strip in strips:
@ -161,6 +214,25 @@ def render_strips(strips, template):
bpy.ops.render.opengl(animation=True, sequencer=True) 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_start = scene_start
scn.frame_end = scene_end scn.frame_end = scene_end
scn.render.filepath = render_path scn.render.filepath = render_path
@ -278,7 +350,8 @@ def import_sound(filepath):
if bpy.data.is_saved: if bpy.data.is_saved:
strip.sound.filepath = bpy.path.relpath(str(filepath)) 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 return strip
def clean_sequencer(edit=False, movie=False, sound=False): def clean_sequencer(edit=False, movie=False, sound=False):