add generic templates
parent
2960bab63d
commit
0055e0f39e
|
@ -164,19 +164,24 @@ def parse(template, string):
|
|||
if result := re.match(reg, string):
|
||||
return result.groupdict()
|
||||
|
||||
def find_last(template, **kargs):
|
||||
#print(find_last, template, kargs)
|
||||
pattern = Path(template).as_posix()
|
||||
pattern = expandvars(pattern)
|
||||
def expand(template, **kargs):
|
||||
template = Path(template).as_posix()
|
||||
template = expandvars(template)
|
||||
|
||||
for key, value in kargs.items():
|
||||
pattern = re.sub(r'\{%s\}'%key, value, pattern)
|
||||
template = re.sub(r'\{%s\}'%key, value, template)
|
||||
|
||||
return template
|
||||
|
||||
def find_last(template, **kargs):
|
||||
|
||||
pattern = expand(template, **kargs)
|
||||
|
||||
pattern = re.sub(r'{\w+:(\d{2})d}', lambda x : '?' * int(x.groups()[0]), pattern)
|
||||
pattern = re.sub(r'{.*?}', '*', pattern)
|
||||
|
||||
print(pattern)
|
||||
|
||||
filepaths = glob.glob(pattern)
|
||||
if filepaths:
|
||||
return Path(max(filepaths))
|
||||
return Path(max(filepaths))
|
||||
else:
|
||||
print(f'No preview found for template {pattern}')
|
|
@ -5,6 +5,7 @@ import os
|
|||
|
||||
import bpy
|
||||
from bpy.types import Operator
|
||||
from bpy.props import IntProperty
|
||||
|
||||
import vse_toolbox
|
||||
|
||||
|
@ -37,6 +38,21 @@ class VSETB_OT_load_settings(Operator):
|
|||
bl_label = 'Load Settings'
|
||||
bl_description = 'Load VSE ToolBox settings from config file'
|
||||
|
||||
def set_config(self, data, values):
|
||||
for k, v in values.items():
|
||||
if not hasattr(data, k):
|
||||
print(f'{repr(data)} has no attribute {k}')
|
||||
continue
|
||||
|
||||
if isinstance(v, list):
|
||||
data[k] = v
|
||||
return
|
||||
|
||||
try:
|
||||
setattr(data, k, v)
|
||||
except Exception:
|
||||
print(f'Could not set property {k} with value {v} to {repr(data)}')
|
||||
|
||||
def execute(self, context):
|
||||
prefs = get_addon_prefs()
|
||||
settings = get_scene_settings()
|
||||
|
@ -48,6 +64,10 @@ class VSETB_OT_load_settings(Operator):
|
|||
|
||||
if not addon_config:
|
||||
return {'CANCELLED'}
|
||||
|
||||
# Environ set
|
||||
for k, v in addon_config.get('environ', {}).items():
|
||||
os.environ[k] = str(v)
|
||||
|
||||
addon_config['trackers'] = addon_config.get('trackers')
|
||||
trackers_config = addon_config.pop('trackers')
|
||||
|
@ -61,11 +81,10 @@ class VSETB_OT_load_settings(Operator):
|
|||
for k, v in tracker_config.items():
|
||||
setattr(tracker, k, v)
|
||||
|
||||
addon_config['spreadsheet_export'] = addon_config.get('spreadsheet_export', {})
|
||||
spreadsheet_export_config = addon_config.pop('spreadsheet_export')
|
||||
|
||||
addon_config['spreadsheet_import'] = addon_config.get('spreadsheet_import', {})
|
||||
spreadsheet_import_config = addon_config.pop('spreadsheet_import')
|
||||
spreadsheet_export_config = addon_config.pop('spreadsheet_export', {})
|
||||
spreadsheet_import_config = addon_config.pop('spreadsheet_import', {})
|
||||
upload_to_tracker_config = addon_config.pop('upload_to_tracker', {})
|
||||
import_shots_config = addon_config.pop('import_shots', {})
|
||||
|
||||
project_name = addon_config.get('project_name')
|
||||
if project_name not in settings.projects:
|
||||
|
@ -77,12 +96,7 @@ class VSETB_OT_load_settings(Operator):
|
|||
|
||||
# Project Properties
|
||||
project = settings.active_project
|
||||
for k, v in addon_config.items():
|
||||
try:
|
||||
setattr(project, k, v)
|
||||
except Exception:
|
||||
print(f'Could not set property {k} with value {v} to project {settings.project_name}')
|
||||
|
||||
self.set_config(project, addon_config)
|
||||
|
||||
export_cells = project.spreadsheet_export.cells
|
||||
for k, v in spreadsheet_export_config.items():
|
||||
|
@ -148,13 +162,53 @@ class VSETB_OT_load_settings(Operator):
|
|||
except Exception:
|
||||
print(f'Could not set option {k} with value {v} to spreadsheet')
|
||||
|
||||
self.report({"INFO"}, 'Settings loaded with sucess')
|
||||
# Upload to Tracker
|
||||
self.set_config(project.upload_to_tracker, upload_to_tracker_config)
|
||||
|
||||
# Import Shots
|
||||
self.set_config(project.import_shots, import_shots_config)
|
||||
|
||||
self.report({"INFO"}, 'Settings loaded with success')
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
class VSETB_OT_add_template(Operator):
|
||||
bl_idname = "vse_toolbox.add_template"
|
||||
bl_label = "Add Template"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def execute(self, context):
|
||||
scn = context.scene
|
||||
settings = get_scene_settings()
|
||||
project = settings.active_project
|
||||
|
||||
project.templates.add()
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
class VSETB_OT_remove_template(Operator):
|
||||
bl_idname = "vse_toolbox.remove_template"
|
||||
bl_label = "Remove Template"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
index : IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
scn = context.scene
|
||||
settings = get_scene_settings()
|
||||
project = settings.active_project
|
||||
|
||||
project.templates.remove(self.index)
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
classes = (
|
||||
VSETB_OT_reload_addon,
|
||||
VSETB_OT_load_settings
|
||||
VSETB_OT_load_settings,
|
||||
VSETB_OT_add_template,
|
||||
VSETB_OT_remove_template
|
||||
)
|
||||
|
||||
def register():
|
||||
|
|
|
@ -8,9 +8,10 @@ import bpy
|
|||
from bpy.types import Operator
|
||||
from bpy.props import BoolProperty
|
||||
|
||||
from vse_toolbox.sequencer_utils import (get_strips, render_strip, render_sound, render_scene)
|
||||
from vse_toolbox.sequencer_utils import (get_strips, render_strip, render_sound,
|
||||
render_scene, get_render_attributes)
|
||||
from vse_toolbox.bl_utils import (get_scene_settings, background_render)
|
||||
from vse_toolbox.file_utils import install_module
|
||||
from vse_toolbox.file_utils import install_module, expand
|
||||
|
||||
|
||||
|
||||
|
@ -116,53 +117,67 @@ class VSETB_OT_render(Operator):
|
|||
settings = get_scene_settings()
|
||||
project = settings.active_project
|
||||
|
||||
project_templates = {t.name: t.value for t in project.templates}
|
||||
format_data = {**settings.format_data, **project.format_data}
|
||||
|
||||
single_file = False
|
||||
sequence_strips = []
|
||||
shot_strips = []
|
||||
|
||||
render_attrs = get_render_attributes()
|
||||
|
||||
start_time = time.perf_counter()
|
||||
if project.render_single_file:
|
||||
single_file = True
|
||||
if project.render_video:
|
||||
video_template = os.path.expandvars(project.render_video_template)
|
||||
video_template = expand(project.render_video_template, **project_templates)
|
||||
video_path = video_template.format(**format_data)
|
||||
render_scene(video_path)
|
||||
render_scene(video_path, attributes=render_attrs)
|
||||
#background_render(output=video_path)
|
||||
|
||||
if project.render_audio:
|
||||
audio_template = os.path.expandvars(project.render_audio_template)
|
||||
audio_template = expand(project.render_audio_template, **project_templates)
|
||||
audio_path = audio_template.format(**format_data)
|
||||
bpy.ops.sound.mixdown(filepath=audio_path)
|
||||
|
||||
if project.render_sequence:
|
||||
print('Render Sequences...')
|
||||
for strip in get_strips(channel='Sequences', selected_only=project.render_selected_only):
|
||||
sequence_strips = get_strips(channel='Sequences', selected_only=project.render_selected_only)
|
||||
for strip in sequence_strips:
|
||||
#print(strip.name)
|
||||
strip_settings = strip.vsetb_strip_settings
|
||||
strip_data = {**format_data, **strip_settings.format_data}
|
||||
|
||||
if project.render_sequence and project.render_video_per_sequence:
|
||||
video_sequence_template = os.path.expandvars(project.render_video_sequence_template)
|
||||
video_sequence_template = expand(project.render_video_sequence_template, **project_templates)
|
||||
sequence_render_path = video_sequence_template.format(**strip_data)
|
||||
render_strip(strip, sequence_render_path)
|
||||
render_strip(strip, sequence_render_path, attributes=render_attrs)
|
||||
|
||||
if project.render_shot and project.render_audio_per_sequence:
|
||||
audio_sequence_template = os.path.expandvars(project.render_audio_sequence_template)
|
||||
audio_sequence_template = expand(project.render_audio_sequence_template, **project_templates)
|
||||
audio_render_path = audio_sequence_template.format(**strip_data)
|
||||
render_sound(strip, audio_render_path)
|
||||
|
||||
if project.render_shot:
|
||||
for strip in get_strips(channel='Shots', selected_only=project.render_selected_only):
|
||||
shot_strips = get_strips(channel='Shots', selected_only=project.render_selected_only)
|
||||
for strip in shot_strips:
|
||||
strip_settings = strip.vsetb_strip_settings
|
||||
strip_data = {**format_data, **strip_settings.format_data}
|
||||
|
||||
if project.render_video_per_strip:
|
||||
video_strip_template = os.path.expandvars(project.render_video_strip_template)
|
||||
video_strip_template = expand(project.render_video_strip_template, **project_templates)
|
||||
strip_render_path = video_strip_template.format(**strip_data)
|
||||
render_strip(strip, strip_render_path)
|
||||
render_strip(strip, strip_render_path, attributes=render_attrs)
|
||||
|
||||
if project.render_audio_per_strip:
|
||||
audio_strip_template = os.path.expandvars(project.render_audio_strip_template)
|
||||
audio_strip_template = expand(project.render_audio_strip_template, **project_templates)
|
||||
audio_render_path = audio_strip_template.format(**strip_data)
|
||||
render_sound(strip, audio_render_path)
|
||||
|
||||
if not single_file and not sequence_strips and not shot_strips:
|
||||
self.report({"ERROR"}, f'No strips rendered, select sequence or shot strips')
|
||||
return {"CANCELLED"}
|
||||
|
||||
self.report({"INFO"}, f'Strips rendered in {time.perf_counter()-start_time} seconds')
|
||||
|
||||
return {"FINISHED"}
|
||||
|
|
|
@ -7,7 +7,7 @@ from tempfile import gettempdir
|
|||
|
||||
import bpy
|
||||
from bpy.types import Operator, UIList
|
||||
from bpy.props import (CollectionProperty, BoolProperty, EnumProperty, StringProperty)
|
||||
from bpy.props import (CollectionProperty, BoolProperty, EnumProperty, StringProperty, IntProperty)
|
||||
|
||||
from vse_toolbox.constants import (EDITS, EDIT_SUFFIXES, MOVIES, MOVIE_SUFFIXES,
|
||||
SOUNDS, SOUND_SUFFIXES)
|
||||
|
@ -16,8 +16,8 @@ from vse_toolbox.sequencer_utils import (clean_sequencer, import_edit, import_mo
|
|||
import_sound, get_strips, get_channel_index, get_empty_channel, scale_clip_to_fit)
|
||||
|
||||
from vse_toolbox.bl_utils import (get_scene_settings, get_addon_prefs, get_scene_settings)
|
||||
from vse_toolbox.file_utils import install_module, parse, find_last
|
||||
#from vse_toolbox.template import Template
|
||||
from vse_toolbox.file_utils import install_module, parse, find_last, expand
|
||||
|
||||
|
||||
class VSETB_OT_auto_select_files(Operator):
|
||||
bl_idname = "vse_toolbox.auto_select_files"
|
||||
|
@ -254,6 +254,38 @@ class VSETB_OT_unselect_task(Operator):
|
|||
return {'FINISHED'}
|
||||
|
||||
|
||||
class VSETB_OT_add_import_template(Operator):
|
||||
bl_idname = "vse_toolbox.add_import_template"
|
||||
bl_label = "Add Template"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def execute(self, context):
|
||||
scn = context.scene
|
||||
settings = get_scene_settings()
|
||||
project = settings.active_project
|
||||
|
||||
project.import_shots.video_templates.add()
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
class VSETB_OT_remove_import_template(Operator):
|
||||
bl_idname = "vse_toolbox.remove_import_template"
|
||||
bl_label = "Remove Template"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
index : IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
scn = context.scene
|
||||
settings = get_scene_settings()
|
||||
project = settings.active_project
|
||||
|
||||
project.import_shots.video_templates.remove(self.index)
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
def get_sequence_items(self, context):
|
||||
settings = get_scene_settings()
|
||||
project = settings.active_project
|
||||
|
@ -351,6 +383,9 @@ class VSETB_OT_import_shots(Operator):
|
|||
if not last_comment:
|
||||
return
|
||||
|
||||
last_preview = last_comment['previews'][0]
|
||||
|
||||
ext = last_preview['extension']
|
||||
shot_name = shot['name']
|
||||
sequence_name = f"{shot['sequence_name']}_"
|
||||
if shot_name.startswith(sequence_name):
|
||||
|
@ -358,10 +393,9 @@ class VSETB_OT_import_shots(Operator):
|
|||
|
||||
preview_dir = self.get_preview_dir()
|
||||
|
||||
filepath = Path(preview_dir, f'{sequence_name}{shot_name}_{task_type.name}')
|
||||
filepath = Path(preview_dir, f'{sequence_name}{shot_name}_{task_type.name}.{ext}')
|
||||
filepath.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
last_preview = last_comment['previews'][0]
|
||||
tracker.download_preview_file(last_preview, str(filepath))
|
||||
|
||||
return filepath
|
||||
|
@ -378,17 +412,20 @@ class VSETB_OT_import_shots(Operator):
|
|||
settings = get_scene_settings()
|
||||
project = settings.active_project
|
||||
import_shots = project.import_shots
|
||||
project_templates = {t.name: t.value for t in project.templates}
|
||||
|
||||
pattern = Path(
|
||||
import_shots.shot_folder_template,
|
||||
import_shots.import_video_template)
|
||||
for template in [import_shots.video_template] + list(import_shots.video_templates.keys()):
|
||||
|
||||
template = expand(template, **project_templates)
|
||||
|
||||
# Normalize name in snake_case for now
|
||||
sequence = sequence.name.lower().replace(' ', '_')
|
||||
shot = parse(project.shot_template, shot_name)['shot']
|
||||
task = task_type.name.lower().replace(' ', '_')
|
||||
format_data = project.format_data
|
||||
format_data.update(parse(project.sequence_template, sequence.name))
|
||||
format_data.update(parse(project.shot_template, shot_name))
|
||||
# Normalize name in snake_case for now
|
||||
format_data.update(task=task_type.name.lower().replace(' ', '_'))
|
||||
|
||||
return find_last(pattern, sequence=sequence, shot=shot, task=task)
|
||||
if last_preview := find_last(template, **format_data):
|
||||
return last_preview
|
||||
|
||||
def execute(self, context):
|
||||
scn = context.scene
|
||||
|
@ -405,6 +442,9 @@ class VSETB_OT_import_shots(Operator):
|
|||
|
||||
conformed = False
|
||||
|
||||
scn.sequence_editor_clear()
|
||||
scn.sequence_editor_create()
|
||||
|
||||
self.set_sequencer_channels([t.name for t in task_types])
|
||||
frame_index = 1
|
||||
for sequence in sequences:
|
||||
|
@ -412,6 +452,7 @@ class VSETB_OT_import_shots(Operator):
|
|||
sequence_start = frame_index
|
||||
for shot_data in shots_data:
|
||||
frames = int(shot_data['nb_frames'])
|
||||
frame_end = frame_index + frames
|
||||
|
||||
strip = scn.sequence_editor.sequences.new_effect(
|
||||
name=shot_data['name'],
|
||||
|
@ -425,25 +466,23 @@ class VSETB_OT_import_shots(Operator):
|
|||
|
||||
for task_type in task_types:
|
||||
if import_shots.import_source == 'DISK':
|
||||
preview = self.find_shot_preview(sequence, shot_data['name'], task_type)
|
||||
#print(videos)
|
||||
#return {"FINISHED"}
|
||||
|
||||
preview = self.find_shot_preview(sequence, shot_data['name'], task_type)
|
||||
else:
|
||||
preview = self.download_preview(task_type, shot_data)
|
||||
if not preview:
|
||||
print(f'No preview found for shot {shot_data["name"]}')
|
||||
|
||||
if not preview:
|
||||
print(f'No preview found for shot {shot_data["name"]}')
|
||||
continue
|
||||
|
||||
print(f'Loading Preview from {preview}')
|
||||
|
||||
# Load Video
|
||||
channel_index = get_channel_index(f'{task_type.name} Video')
|
||||
video_clip = import_movie(preview)
|
||||
video_clip.frame_start = frame_index
|
||||
video_clip = import_movie(preview, frame_start=frame_index, frame_end=frame_end)
|
||||
video_clip.channel = channel_index
|
||||
|
||||
if video_clip.frame_offset_end:
|
||||
video_clip.color_tag = 'COLOR_01'
|
||||
|
||||
if not conformed:
|
||||
self.conform_render(video_clip)
|
||||
conformed = True
|
||||
|
@ -453,8 +492,10 @@ class VSETB_OT_import_shots(Operator):
|
|||
# Load Audio
|
||||
channel_index = get_channel_index(f'{task_type.name} Audio')
|
||||
audio_clip = import_sound(preview, frame_start=frame_index,
|
||||
frame_end=video_clip.frame_final_end)
|
||||
frame_end=frame_end)
|
||||
audio_clip.channel = channel_index
|
||||
if video_clip.frame_offset_end:
|
||||
audio_clip.color_tag = 'COLOR_01'
|
||||
|
||||
frame_index += frames
|
||||
|
||||
|
@ -471,6 +512,8 @@ class VSETB_OT_import_shots(Operator):
|
|||
scn.frame_start = 1
|
||||
scn.frame_end = frame_index -1
|
||||
|
||||
#bpy.ops.vse_toolbox.set_stamps()
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
def draw(self, context):
|
||||
|
@ -489,8 +532,16 @@ class VSETB_OT_import_shots(Operator):
|
|||
|
||||
if import_shots.import_source == 'DISK':
|
||||
#col.prop(import_shots, "sequence_dir_template", text='Sequence Dir')
|
||||
col.prop(import_shots, "shot_folder_template", text='Shot Folder')
|
||||
col.prop(import_shots, "import_video_template", text='Video')
|
||||
#col.prop(import_shots, "shot_folder_template", text='Shot Folder')
|
||||
row = col.row(align=True)
|
||||
row.prop(import_shots, "video_template", text='')
|
||||
row.operator("vse_toolbox.add_import_template", text='', icon='ADD')
|
||||
|
||||
for i, template in enumerate(import_shots.video_templates):
|
||||
row = col.row(align=True)
|
||||
row.prop(template, "name", text='')
|
||||
row.operator("vse_toolbox.remove_import_template", text='', icon='REMOVE').index=i
|
||||
|
||||
col.separator()
|
||||
else:
|
||||
col.prop(import_shots, "previews_folder", text='Previews Folder')
|
||||
|
@ -579,7 +630,7 @@ class VSETB_OT_import_shots(Operator):
|
|||
# self.report({"ERROR"}, "Save your Blender file first")
|
||||
# return {"CANCELLED"}
|
||||
|
||||
return context.window_manager.invoke_props_dialog(self, width=400)
|
||||
return context.window_manager.invoke_props_dialog(self, width=350)
|
||||
|
||||
def check(self, context):
|
||||
return True
|
||||
|
@ -590,6 +641,8 @@ classes = (
|
|||
VSETB_OT_unselect_sequence,
|
||||
VSETB_OT_unselect_task,
|
||||
VSETB_OT_select_task,
|
||||
VSETB_OT_add_import_template,
|
||||
VSETB_OT_remove_import_template,
|
||||
VSETB_UL_import_task,
|
||||
VSETB_OT_auto_select_files,
|
||||
VSETB_OT_import_files,
|
||||
|
|
|
@ -155,7 +155,7 @@ class VSETB_OT_set_sequencer(Operator):
|
|||
|
||||
scn.view_settings.view_transform = 'Standard'
|
||||
scn.render.image_settings.file_format = 'FFMPEG'
|
||||
scn.render.ffmpeg.gopsize = 5
|
||||
scn.render.ffmpeg.gopsize = 8
|
||||
scn.render.ffmpeg.constant_rate_factor = 'HIGH'
|
||||
scn.render.ffmpeg.format = 'QUICKTIME'
|
||||
scn.render.ffmpeg.audio_codec = 'AAC'
|
||||
|
@ -304,8 +304,18 @@ class VSETB_OT_open_shot_folder(Operator):
|
|||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
settings = get_scene_settings()
|
||||
project = settings.active_project
|
||||
if not project.templates.get('shot_dir'):
|
||||
cls.poll_message_set('No shot_dir template setted')
|
||||
return
|
||||
|
||||
strip = context.active_sequence_strip
|
||||
return strip and get_channel_name(strip) == 'Shots'
|
||||
if not strip or get_channel_name(strip) != 'Shots':
|
||||
cls.poll_message_set('No shot strip active')
|
||||
return
|
||||
|
||||
return True
|
||||
|
||||
def execute(self, context):
|
||||
strip = context.active_sequence_strip
|
||||
|
@ -316,7 +326,7 @@ class VSETB_OT_open_shot_folder(Operator):
|
|||
|
||||
format_data = {**settings.format_data, **project.format_data, **strip_settings.format_data}
|
||||
|
||||
shot_folder_template = expandvars(project.import_shots.shot_folder_template)
|
||||
shot_folder_template = expandvars(project.templates['shot_dir'].value)
|
||||
shot_folder_path = shot_folder_template.format(**format_data)
|
||||
bpy.ops.wm.path_open(filepath=shot_folder_path)
|
||||
|
||||
|
|
|
@ -12,10 +12,11 @@ from bpy.props import (BoolProperty, EnumProperty, StringProperty)
|
|||
|
||||
from vse_toolbox.constants import (ASSET_PREVIEWS, PREVIEWS_DIR, ASSET_ITEMS)
|
||||
|
||||
from vse_toolbox.sequencer_utils import (get_strips, get_strip_render_path, get_strip_sequence_name)
|
||||
from vse_toolbox.sequencer_utils import (get_strips, get_strip_render_path, get_strip_sequence_name,
|
||||
render_strip, get_render_attributes)
|
||||
|
||||
from vse_toolbox.bl_utils import (get_addon_prefs, get_scene_settings)
|
||||
from vse_toolbox.file_utils import (norm_name,)
|
||||
from vse_toolbox.file_utils import (norm_name, expand)
|
||||
from bpy.app.handlers import persistent
|
||||
|
||||
|
||||
|
@ -36,7 +37,7 @@ class VSETB_OT_tracker_connect(Operator):
|
|||
settings = get_scene_settings()
|
||||
try:
|
||||
prefs.tracker.connect()
|
||||
self.report({'INFO'}, f'Sucessfully login to {settings.tracker_name.title()}')
|
||||
self.report({'INFO'}, f'successfully login to {settings.tracker_name.title()}')
|
||||
return {"FINISHED"}
|
||||
except Exception as e:
|
||||
print('e: ', e)
|
||||
|
@ -121,6 +122,10 @@ class VSETB_OT_load_projects(Operator):
|
|||
(r,g,b), a = map(lambda component: component / 255, bytes.fromhex(color_str[-6:])), 1.0
|
||||
return (r,g,b,a)
|
||||
|
||||
def invoke(self, context, event):
|
||||
self.ctrl = event.ctrl
|
||||
return self.execute(context)
|
||||
|
||||
def execute(self, context):
|
||||
settings = get_scene_settings()
|
||||
prefs = get_addon_prefs()
|
||||
|
@ -213,14 +218,16 @@ class VSETB_OT_load_projects(Operator):
|
|||
if project.name not in project_names:
|
||||
settings.projects.remove(list(settings.projects).index(project))
|
||||
|
||||
bpy.ops.vse_toolbox.load_settings()
|
||||
if self.ctrl or not settings.get('projects_loaded'):
|
||||
bpy.ops.vse_toolbox.load_settings()
|
||||
|
||||
if prev_project_name != '/' and prev_project_name in settings.projects:
|
||||
settings.project_name = prev_project_name
|
||||
|
||||
#if settings.active_project:
|
||||
# settings.active_project.set_strip_metadata()
|
||||
|
||||
|
||||
settings['projects_loaded'] = True
|
||||
|
||||
self.report({"INFO"}, 'Successfully Load Tracker Projects')
|
||||
|
||||
|
@ -251,19 +258,16 @@ class VSETB_OT_new_episode(Operator):
|
|||
tracker = prefs.tracker
|
||||
|
||||
episode_name = settings.episode_template.format(index=int(self.episode_name))
|
||||
|
||||
#print(self.episode_name)
|
||||
#print('episode_name: ', episode_name)
|
||||
|
||||
episode = tracker.get_episode(episode_name)
|
||||
if episode:
|
||||
self.report({'ERROR'}, f'Episode {episode_name} already exists')
|
||||
return {"CANCELLED"}
|
||||
|
||||
tracker.new_episode(episode_name)
|
||||
# tracker.get_episodes
|
||||
tracker.update_project()
|
||||
|
||||
self.report({'INFO'}, f'Episode {episode_name} successfully created')
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
|
@ -302,8 +306,6 @@ class VSETB_OT_upload_to_tracker(Operator):
|
|||
col.use_property_split = False
|
||||
col.prop(upload_to_tracker, 'render_strip_template', text='')
|
||||
col.use_property_split = True
|
||||
#else:
|
||||
# col.label(text=f'Source: {self.project.render_video_strip_template}')
|
||||
|
||||
col.separator()
|
||||
|
||||
|
@ -329,21 +331,27 @@ class VSETB_OT_upload_to_tracker(Operator):
|
|||
#self.report({'ERROR'}, f'Export not implemented yet.')
|
||||
prefs = get_addon_prefs()
|
||||
settings = get_scene_settings()
|
||||
tracker = prefs.tracker
|
||||
|
||||
upload_to_tracker = self.project.upload_to_tracker
|
||||
render_attrs = get_render_attributes()
|
||||
project_templates = {t.name: t.value for t in self.project.templates}
|
||||
|
||||
episode = None
|
||||
if settings.active_episode:
|
||||
episode = settings.active_episode.id
|
||||
|
||||
format_data = {**settings.format_data, **self.project.format_data}
|
||||
|
||||
tracker = prefs.tracker
|
||||
|
||||
|
||||
status = upload_to_tracker.status
|
||||
if status == 'CURRENT':
|
||||
status = None
|
||||
|
||||
shot_strips = get_strips(channel='Shots', selected_only=True)
|
||||
context.window_manager.progress_begin(0, len(shot_strips))
|
||||
|
||||
for strip in get_strips(channel='Shots', selected_only=True):
|
||||
for i, strip in enumerate(shot_strips):
|
||||
context.window_manager.progress_update(i)
|
||||
strip_settings = strip.vsetb_strip_settings
|
||||
sequence_name = get_strip_sequence_name(strip)
|
||||
shot_name = strip.name
|
||||
|
@ -369,12 +377,17 @@ class VSETB_OT_upload_to_tracker(Operator):
|
|||
|
||||
strip_data = {**format_data, **strip_settings.format_data}
|
||||
if upload_to_tracker.render_strips:
|
||||
preview_template = expandvars(upload_to_tracker.render_strip_template)
|
||||
preview_template = expand(upload_to_tracker.render_strip_template, **project_templates)
|
||||
strip_data['ext'] = 'mov'
|
||||
else:
|
||||
preview_template = expandvars(self.project.render_video_strip_template)
|
||||
|
||||
preview = preview_template.format(**strip_data)
|
||||
preview = Path(os.path.abspath(bpy.path.abspath(preview)))
|
||||
|
||||
if upload_to_tracker.render_strips:
|
||||
render_strip(strip, preview, attributes=render_attrs)
|
||||
|
||||
#print(preview)
|
||||
if not preview.exists():
|
||||
print(f'The preview {preview} not exists')
|
||||
|
@ -387,7 +400,7 @@ class VSETB_OT_upload_to_tracker(Operator):
|
|||
preview = None
|
||||
|
||||
comment_data = None
|
||||
if status or comment or preview:
|
||||
if status or upload_to_tracker.comment or preview:
|
||||
comment_data = tracker.new_comment(task, comment=upload_to_tracker.comment, status=status)
|
||||
if preview:
|
||||
print('Upload preview from', preview)
|
||||
|
@ -423,6 +436,8 @@ class VSETB_OT_upload_to_tracker(Operator):
|
|||
if task.comment and tracker_task.get('last_comment') != task.comment:
|
||||
tracker.new_comment(tracker_task, comment=task.comment)
|
||||
|
||||
context.window_manager.progress_end()
|
||||
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
|
@ -448,6 +463,7 @@ class VSETB_OT_open_shot_on_tracker(Operator):
|
|||
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@persistent
|
||||
def set_asset_items(scene=None):
|
||||
ASSET_ITEMS.clear()
|
||||
|
|
|
@ -78,7 +78,7 @@ class Kitsu(Tracker):
|
|||
print(f'Info: Log in to kitsu as {login}')
|
||||
res = gazu.log_in(login, password)
|
||||
LOGIN = login
|
||||
print(f'Info: Sucessfully login to Kitsu as {res["user"]["full_name"]}')
|
||||
print(f'Info: successfully login to Kitsu as {res["user"]["full_name"]}')
|
||||
return res['user']
|
||||
except Exception as e:
|
||||
print(f'Error: {traceback.format_exc()}')
|
||||
|
|
|
@ -16,6 +16,19 @@ import subprocess
|
|||
from collections import defaultdict
|
||||
|
||||
|
||||
def get_render_attributes():
|
||||
scn = bpy.context.scene
|
||||
return [
|
||||
(scn.view_settings, "view_transform", 'Standard'),
|
||||
(scn.render.image_settings, "file_format", 'FFMPEG'),
|
||||
(scn.render.ffmpeg, "gopsize", 8),
|
||||
(scn.render.ffmpeg, "constant_rate_factor", 'HIGH'),
|
||||
(scn.render.ffmpeg, "format", 'QUICKTIME'),
|
||||
(scn.render.ffmpeg, "audio_codec", 'MP3'),
|
||||
(scn.render.ffmpeg, "audio_mixrate", 44100),
|
||||
(scn.render.ffmpeg, "audio_bitrate", 128)
|
||||
]
|
||||
|
||||
def frame_to_timecode(frame, fps):
|
||||
total_seconds = frame / fps
|
||||
|
||||
|
@ -109,20 +122,23 @@ def rename_strips(strips, template, increment=10, start_number=0, padding=3, by_
|
|||
strip_number = 0
|
||||
|
||||
for strip in strips:
|
||||
channel = get_channel_name(strip)
|
||||
sequence_name = get_strip_sequence_name(strip)
|
||||
sequence_data = parse(project.sequence_template, sequence_name)
|
||||
format_data = {}
|
||||
if channel == 'Shots':
|
||||
format_data = parse(project.sequence_template, sequence_name)
|
||||
else:
|
||||
format_data['sequence'] = str(strip_number*increment + start_number).zfill(padding)
|
||||
|
||||
if (by_sequence and prev_sequence_name and
|
||||
sequence_name and sequence_name != prev_sequence_name):
|
||||
strip_number = 0
|
||||
|
||||
format_data = dict(
|
||||
format_data.update(
|
||||
sequence_strip=sequence_name,
|
||||
episode=episode_name,
|
||||
shot=str(strip_number*increment + start_number).zfill(padding))
|
||||
|
||||
format_data.update(sequence_data)
|
||||
|
||||
name = template.format(**format_data)
|
||||
|
||||
existing_strip = scn.sequence_editor.sequences_all.get(name)
|
||||
|
@ -270,26 +286,32 @@ def render_sound(strip, output):
|
|||
scn.frame_end = scene_end
|
||||
scn.frame_current = scene_current
|
||||
|
||||
def render_scene(output):
|
||||
output = os.path.abspath(bpy.path.abspath(output))
|
||||
def render_scene(output, attributes=None):
|
||||
output = os.path.abspath(bpy.path.abspath(str(output)))
|
||||
|
||||
scn = bpy.context.scene
|
||||
render_path = scn.render.filepath
|
||||
|
||||
scn.render.filepath = output
|
||||
if attributes is None:
|
||||
attributes = []
|
||||
|
||||
attributes += [
|
||||
(scn.render, "filepath", output),
|
||||
]
|
||||
|
||||
print(f'Render Strip to {scn.render.filepath}')
|
||||
Path(output).parent.mkdir(exist_ok=True, parents=True)
|
||||
bpy.ops.render.opengl(animation=True, sequencer=True)
|
||||
|
||||
scn.render.filepath = render_path
|
||||
|
||||
def render_strip(strip, output):
|
||||
output = os.path.abspath(bpy.path.abspath(output))
|
||||
def render_strip(strip, output, attributes=None):
|
||||
output = os.path.abspath(bpy.path.abspath(str(output)))
|
||||
|
||||
scn = bpy.context.scene
|
||||
|
||||
attributes = [
|
||||
if attributes is None:
|
||||
attributes = []
|
||||
|
||||
attributes += [
|
||||
(scn, "frame_start", strip.frame_final_start),
|
||||
(scn, "frame_end", strip.frame_final_end - 1),
|
||||
(scn, "frame_current"),
|
||||
|
@ -316,7 +338,6 @@ def import_edit(filepath, adapter="cmx_3600", channel='Shots', match_by='name'):
|
|||
for s in strips:
|
||||
s.channel = empty_channel
|
||||
|
||||
|
||||
edl = Path(filepath)
|
||||
try:
|
||||
timeline = opentimelineio.adapters.read_from_file(
|
||||
|
@ -397,11 +418,14 @@ def import_edit(filepath, adapter="cmx_3600", channel='Shots', match_by='name'):
|
|||
scn.frame_end = frame_end-1
|
||||
return timeline
|
||||
|
||||
def import_movie(filepath):
|
||||
def import_movie(filepath, frame_start=None, frame_end=None):
|
||||
scn = bpy.context.scene
|
||||
|
||||
if frame_start is None:
|
||||
frame_start = scn.frame_start
|
||||
|
||||
res_x = scn.render.resolution_x
|
||||
res_y = scn.render.resolution_y
|
||||
res_y = scn.render.resolution_y
|
||||
|
||||
if bpy.data.is_saved:
|
||||
relpath = Path(bpy.path.relpath(str(filepath)))
|
||||
|
@ -412,7 +436,7 @@ def import_movie(filepath):
|
|||
name=Path(filepath).stem,
|
||||
filepath=str(filepath),
|
||||
channel=get_channel_index('Movie'),
|
||||
frame_start=scn.frame_start
|
||||
frame_start=frame_start
|
||||
)
|
||||
|
||||
elem = strip.strip_elem_from_frame(scn.frame_current)
|
||||
|
@ -423,7 +447,8 @@ def import_movie(filepath):
|
|||
if src_height != res_y:
|
||||
strip.transform.scale_y = (res_y / src_height)
|
||||
|
||||
|
||||
if frame_end is not None:
|
||||
strip.frame_final_end = frame_end
|
||||
|
||||
return strip
|
||||
|
||||
|
|
36
ui/panels.py
36
ui/panels.py
|
@ -39,20 +39,52 @@ class VSETB_PT_main(VSETB_main, Panel):
|
|||
# row.operator('vse_toolbox.load_projects', icon='FILE_REFRESH', text='', emboss=False)
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
wm = context.window_manager
|
||||
scn = context.scene
|
||||
settings = get_scene_settings()
|
||||
prefs = get_addon_prefs()
|
||||
|
||||
row = self.layout.row(align=False)
|
||||
row = layout.row(align=True)
|
||||
|
||||
row.operator('vse_toolbox.load_projects', icon='FILE_REFRESH', text='', emboss=False)
|
||||
row.separator(factor=0.5)
|
||||
row.prop(settings, 'project_name', text='')
|
||||
|
||||
project = settings.active_project
|
||||
|
||||
if project and project.type == 'TVSHOW':
|
||||
row.separator(factor=0.5)
|
||||
row.prop(project, 'episode_name', text='')
|
||||
|
||||
row.separator(factor=0.5)
|
||||
row.prop(project, "show_settings", icon="PREFERENCES", text='')
|
||||
if project.show_settings:
|
||||
box = layout.box()
|
||||
split = box.split(factor=0.3)
|
||||
name_col = split.column()
|
||||
template_col = split.column()
|
||||
|
||||
row = name_col.row(align=True) # Padding
|
||||
row.separator(factor=0.5)
|
||||
row.label(text="Name")
|
||||
|
||||
row = template_col.row(align=True) # Padding
|
||||
row.separator(factor=0.5)
|
||||
row.label(text="Template")
|
||||
|
||||
row.operator("vse_toolbox.add_template", text="", icon='ADD', emboss=False)
|
||||
|
||||
for i, template in enumerate(project.templates):
|
||||
row = name_col.row()
|
||||
row.prop(template, "name", text="")
|
||||
|
||||
row = template_col.row(align=True)
|
||||
subrow = row.row()
|
||||
subrow.prop(template, "value", text="")
|
||||
row.separator(factor=0.25)
|
||||
row.operator("vse_toolbox.remove_template", text="", icon='REMOVE', emboss=False).index = i
|
||||
|
||||
|
||||
# settings = get_scene_settings()
|
||||
# prefs = get_addon_prefs()
|
||||
|
@ -173,7 +205,7 @@ class VSETB_PT_settings(VSETB_main, Panel):
|
|||
class VSETB_PT_imports(VSETB_main, Panel):
|
||||
bl_label = "Imports"
|
||||
#bl_parent_id = "VSETB_PT_main"
|
||||
bl_options = {'DEFAULT_CLOSED'}
|
||||
#bl_options = {'DEFAULT_CLOSED'}
|
||||
|
||||
def draw_header_preset(self, context):
|
||||
self.layout.operator('vse_toolbox.import_files', icon='IMPORT', text='', emboss=False)
|
||||
|
|
|
@ -63,6 +63,7 @@ def load_trackers():
|
|||
print(f'Could not register Tracker {name}')
|
||||
print(e)
|
||||
|
||||
|
||||
def load_prefs():
|
||||
prefs = get_addon_prefs()
|
||||
prefs_config_file = prefs.config_path
|
||||
|
@ -72,6 +73,9 @@ def load_prefs():
|
|||
|
||||
prefs_datas = read_file(os.path.expandvars(prefs_config_file))
|
||||
|
||||
if not prefs_datas:
|
||||
return
|
||||
|
||||
for tracker_data in prefs_datas['trackers']:
|
||||
tracker_name = norm_str(tracker_data['name'])
|
||||
if not hasattr(prefs.trackers, tracker_name):
|
||||
|
|
|
@ -159,6 +159,10 @@ class Episode(PropertyGroup):
|
|||
return self.get(settings.project_name)
|
||||
|
||||
|
||||
class Template(PropertyGroup):
|
||||
value : StringProperty()
|
||||
|
||||
|
||||
class SpreadsheetExportCell(PropertyGroup):
|
||||
export_name : StringProperty()
|
||||
enabled : BoolProperty(default=True)
|
||||
|
@ -241,19 +245,21 @@ def get_task_type_items(self, context):
|
|||
class ImportShots(PropertyGroup):
|
||||
import_source: EnumProperty(items=[(i, i.title(), '') for i in ('DISK', 'TRACKER')])
|
||||
import_task: EnumProperty(items=[(i, i.title().replace('_', ' '), '')
|
||||
for i in ('LAST', 'FROM_LIST', 'ALL')], default='FROM_LIST')
|
||||
for i in ('FROM_LIST',)], default='FROM_LIST') #('LAST', 'FROM_LIST', 'ALL')
|
||||
|
||||
#sequence_dir_template: StringProperty(
|
||||
# name="Sequence Template", default="$PROJECT_ROOT/sequences/{sequence}")
|
||||
|
||||
shot_folder_template: StringProperty(
|
||||
name="Shot Template", default="$PROJECT_ROOT/sequences/sq{sequence}/sh{shot}")
|
||||
#shot_folder_template: StringProperty(
|
||||
# name="Shot Template", default="$PROJECT_ROOT/sequences/sq{sequence}/sh{shot}")
|
||||
|
||||
import_video_template : StringProperty(
|
||||
name="Video Path", default="./{task}/render/{version}.{ext}")
|
||||
video_template : StringProperty(
|
||||
name="Video Path", default="//sources/{sequence}_{shot}_{task}.{ext}")
|
||||
|
||||
video_templates : CollectionProperty(type=PropertyGroup)
|
||||
|
||||
import_sequence: EnumProperty(items=[(i, i.title().replace('_', ' '), '')
|
||||
for i in ('SELECTED_STRIPS', 'FROM_LIST', 'ALL')], default='FROM_LIST')
|
||||
for i in ('FROM_LIST',)], default='FROM_LIST') #('SELECTED_STRIPS', 'FROM_LIST', 'ALL')
|
||||
|
||||
previews_folder: StringProperty(
|
||||
name="Previews Folder", default="//sources", subtype='DIR_PATH')
|
||||
|
@ -262,7 +268,7 @@ class ImportShots(PropertyGroup):
|
|||
class UploadToTracker(PropertyGroup):
|
||||
render_strips: BoolProperty(default=False)
|
||||
render_strip_template : StringProperty(
|
||||
name="Movie Path", default="//render/{project_basename}.{ext}")
|
||||
name="Movie Path", default="//render/{strip}.{ext}")
|
||||
|
||||
task : EnumProperty(items=get_task_type_items)
|
||||
status : EnumProperty(items=get_task_status_items)
|
||||
|
@ -297,6 +303,9 @@ def on_episode_updated(self, context):
|
|||
class Project(PropertyGroup):
|
||||
id : StringProperty(default='')
|
||||
|
||||
show_settings : BoolProperty()
|
||||
templates : CollectionProperty(type=Template)
|
||||
|
||||
shot_start_number : IntProperty(name="Shot Start Number", default=10, min=0)
|
||||
shot_padding : IntProperty(name="Shot Padding", default=4, min=0, max=10)
|
||||
|
||||
|
@ -316,28 +325,28 @@ class Project(PropertyGroup):
|
|||
name="Shot Increment", default=10, min=0, step=10)
|
||||
|
||||
sequence_template : StringProperty(
|
||||
name="Sequence Name", default="sq{sequence}")
|
||||
name="Sequence Name", default="{sequence}")
|
||||
|
||||
episode_template : StringProperty(
|
||||
name="Episode Name", default="ep{episode}")
|
||||
name="Episode Name", default="{episode}")
|
||||
|
||||
shot_template : StringProperty(
|
||||
name="Shot Name", default="sq{sequence}_sh{shot}")
|
||||
name="Shot Name", default="{sequence}_{shot}")
|
||||
|
||||
render_video_template : StringProperty(
|
||||
name="Movie Path", default="//render/{project_basename}.{ext}")
|
||||
name="Movie Path", default="//render/{project}.mov")
|
||||
|
||||
render_audio_template : StringProperty(
|
||||
name="Audio Path", default="//render/{project_basename}.wav")
|
||||
name="Audio Path", default="//render/{project}.wav")
|
||||
|
||||
render_video_strip_template : StringProperty(
|
||||
name="Strip Path", default="//render/shots/{strip}.{ext}")
|
||||
name="Strip Path", default="//render/shots/{strip}.mov")
|
||||
|
||||
render_audio_strip_template: StringProperty(
|
||||
name="Sound Path", default="//render/sounds/{strip}.wav")
|
||||
|
||||
render_video_sequence_template : StringProperty(
|
||||
name="Sequence Path", default="//render/sequences/{sequence}.{ext}")
|
||||
name="Sequence Path", default="//render/sequences/{sequence}.mov")
|
||||
|
||||
render_audio_sequence_template: StringProperty(
|
||||
name="Sound Path", default="//render/sounds/{sequence}.wav")
|
||||
|
@ -359,7 +368,7 @@ class Project(PropertyGroup):
|
|||
#render_sound_format: EnumProperty(items=[(e, e, '') for e in ('mp3', 'wav')])
|
||||
|
||||
export_edl_template : StringProperty(
|
||||
name="Edl Path", default="//render/{project_basename}.edl")
|
||||
name="Edl Path", default="//render/{project}.edl")
|
||||
|
||||
episode_name : EnumProperty(items=get_episodes_items, update=on_episode_updated)
|
||||
episodes : CollectionProperty(type=Episode)
|
||||
|
@ -394,6 +403,9 @@ class Project(PropertyGroup):
|
|||
data['episode'] = norm_str(self.episode_name)
|
||||
data['project_basename'] = f"{data['project']}_{data['episode']}"
|
||||
|
||||
#for template in self.templates:
|
||||
# data[template.name] = template.value
|
||||
|
||||
return data
|
||||
|
||||
def get_cell_types(self):
|
||||
|
@ -745,6 +757,7 @@ classes = (
|
|||
AssetType,
|
||||
TaskStatus,
|
||||
Episode,
|
||||
Template,
|
||||
Metadata,
|
||||
MetadataType,
|
||||
Sequence,
|
||||
|
|
Loading…
Reference in New Issue