refacto
parent
7f1e2cc0b2
commit
9f11a18c3a
|
@ -1,2 +1,3 @@
|
|||
*pyc
|
||||
__pycache__
|
||||
__pycache__
|
||||
.vscode
|
14
__init__.py
14
__init__.py
|
@ -17,25 +17,21 @@ import sys
|
|||
from pathlib import Path
|
||||
sys.modules['vse_toolbox'] = sys.modules.pop(Path(__file__).parent.name)
|
||||
|
||||
from vse_toolbox import panels
|
||||
from vse_toolbox import preferences
|
||||
from vse_toolbox import properties
|
||||
from vse_toolbox import ui
|
||||
from vse_toolbox import operators
|
||||
from vse_toolbox.constants import ASSET_PREVIEWS
|
||||
from vse_toolbox.sequencer_utils import set_active_strip, update_text_strips
|
||||
|
||||
|
||||
modules = (
|
||||
properties,
|
||||
preferences,
|
||||
operators,
|
||||
panels,
|
||||
ui,
|
||||
)
|
||||
|
||||
if 'bpy' in locals():
|
||||
import importlib
|
||||
for module in modules:
|
||||
importlib.reload(module)
|
||||
for mod in modules:
|
||||
importlib.reload(mod)
|
||||
|
||||
import bpy
|
||||
|
||||
|
@ -49,8 +45,8 @@ def register():
|
|||
bpy.app.handlers.frame_change_post.append(set_active_strip)
|
||||
bpy.app.handlers.frame_change_post.append(update_text_strips)
|
||||
|
||||
def unregister():
|
||||
|
||||
def unregister():
|
||||
try:
|
||||
bpy.utils.previews.remove(ASSET_PREVIEWS)
|
||||
except Exception as e:
|
||||
|
|
|
@ -1,16 +1,30 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
from vse_toolbox.operators import operators
|
||||
from vse_toolbox.operators import (addon, casting, imports, render, sequencer,
|
||||
spreadsheet, tracker)
|
||||
|
||||
modules = (
|
||||
addon,
|
||||
casting,
|
||||
imports,
|
||||
render,
|
||||
sequencer,
|
||||
spreadsheet,
|
||||
tracker
|
||||
)
|
||||
|
||||
if 'bpy' in locals():
|
||||
import importlib
|
||||
|
||||
importlib.reload(operators)
|
||||
for mod in modules:
|
||||
importlib.reload(mod)
|
||||
|
||||
import bpy
|
||||
|
||||
def register():
|
||||
operators.register()
|
||||
for mod in modules:
|
||||
mod.register()
|
||||
|
||||
def unregister():
|
||||
operators.unregister()
|
||||
for mod in modules:
|
||||
mod.unregister()
|
|
@ -0,0 +1,108 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
import importlib
|
||||
import os
|
||||
|
||||
import bpy
|
||||
from bpy.types import Operator
|
||||
|
||||
import vse_toolbox
|
||||
|
||||
|
||||
from vse_toolbox.bl_utils import (get_addon_prefs, get_scene_settings)
|
||||
from vse_toolbox.file_utils import (read_file, )
|
||||
|
||||
|
||||
|
||||
class VSETB_OT_reload_addon(Operator):
|
||||
bl_idname = "vse_toolbox.reload_addon"
|
||||
bl_options = {"UNDO"}
|
||||
bl_label = 'Reload VSE ToolBox Addon'
|
||||
bl_description = 'Reload The VSE ToolBox Addon'
|
||||
|
||||
def execute(self, context):
|
||||
|
||||
print('Execute reload')
|
||||
|
||||
vse_toolbox.unregister()
|
||||
importlib.reload(vse_toolbox)
|
||||
vse_toolbox.register()
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
class VSETB_OT_load_settings(Operator):
|
||||
bl_idname = "vse_toolbox.load_settings"
|
||||
bl_options = {"UNDO"}
|
||||
bl_label = 'Load Settings'
|
||||
bl_description = 'Load VSE ToolBox settings from config file'
|
||||
|
||||
def execute(self, context):
|
||||
prefs = get_addon_prefs()
|
||||
settings = get_scene_settings()
|
||||
project = settings.active_project
|
||||
|
||||
if not prefs.config_path:
|
||||
return
|
||||
|
||||
addon_config = read_file(os.path.expandvars(prefs.config_path))
|
||||
|
||||
addon_config['trackers'] = addon_config.get('trackers')
|
||||
trackers = addon_config.pop('trackers')
|
||||
|
||||
addon_config['spreadsheet'] = addon_config.get('spreadsheet')
|
||||
spreadsheet = addon_config.pop('spreadsheet')
|
||||
|
||||
project_name = addon_config.get('project_name')
|
||||
if project_name:
|
||||
settings.project_name = project_name
|
||||
|
||||
# Project Properties
|
||||
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}')
|
||||
|
||||
if spreadsheet.get('cells'):
|
||||
#project.spreadsheet.clear()
|
||||
|
||||
for i, cell_data in enumerate(spreadsheet['cells']):
|
||||
if not 'name' in cell_data:
|
||||
print(f'cell_data {cell_data} need to have a attribute name')
|
||||
continue
|
||||
|
||||
cell = project.spreadsheet.get(cell_data['name'])
|
||||
|
||||
if not cell:
|
||||
print(f"cell {cell_data['name']} not in spreadsheet")
|
||||
continue
|
||||
|
||||
project.spreadsheet.move(list(project.spreadsheet).index(cell), i)
|
||||
|
||||
for prop_name in ('export_name', 'enabled'):
|
||||
if prop_name in cell_data:
|
||||
setattr(cell, prop_name, cell_data[prop_name])
|
||||
|
||||
if spreadsheet.get('options'):
|
||||
for k, v in spreadsheet['options'].items():
|
||||
try:
|
||||
setattr(project.spreadsheet_options, k, v)
|
||||
except Exception:
|
||||
print(f'Could not set option {k} with value {v} to spreadsheet')
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
classes = (
|
||||
VSETB_OT_reload_addon,
|
||||
VSETB_OT_load_settings
|
||||
)
|
||||
|
||||
def register():
|
||||
for cls in classes:
|
||||
bpy.utils.register_class(cls)
|
||||
|
||||
def unregister():
|
||||
for cls in reversed(classes):
|
||||
bpy.utils.unregister_class(cls)
|
|
@ -0,0 +1,327 @@
|
|||
|
||||
import json
|
||||
|
||||
import bpy
|
||||
from bpy.types import PropertyGroup, Operator
|
||||
from bpy.props import (CollectionProperty, EnumProperty, StringProperty)
|
||||
|
||||
from vse_toolbox.constants import CASTING_BUFFER
|
||||
from vse_toolbox.sequencer_utils import get_strips
|
||||
from vse_toolbox.bl_utils import (get_addon_prefs, get_scene_settings, get_strip_settings)
|
||||
|
||||
|
||||
class VSETB_OT_casting_replace(Operator):
|
||||
bl_idname = "vse_toolbox.casting_replace"
|
||||
bl_label = "Replace Asset"
|
||||
bl_description = "Replace Asset of selected strips"
|
||||
|
||||
old_asset : StringProperty()
|
||||
new_asset : StringProperty()
|
||||
|
||||
assets : CollectionProperty(type=PropertyGroup)
|
||||
|
||||
def execute(self, context):
|
||||
prefs = get_addon_prefs()
|
||||
settings = get_scene_settings()
|
||||
|
||||
new_asset = next(a for a in settings.active_project.assets if a.tracker_name == self.new_asset)
|
||||
|
||||
for strip in get_strips('Shots', selected_only=True):
|
||||
strip_settings = strip.vsetb_strip_settings
|
||||
for asset_casting in strip_settings.casting:
|
||||
if asset_casting.asset.tracker_name == self.old_asset:
|
||||
|
||||
print(f'Replace casting on {strip.name}')
|
||||
|
||||
asset_casting.name = new_asset.name
|
||||
asset_casting.id = new_asset.id
|
||||
asset_casting['_name'] = new_asset.label
|
||||
|
||||
strip_settings.casting.update()
|
||||
|
||||
self.assets.clear()
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
def invoke(self, context, event):
|
||||
settings = get_scene_settings()
|
||||
project = settings.active_project
|
||||
|
||||
self.assets.clear()
|
||||
for asset in project.assets:
|
||||
item = self.assets.add()
|
||||
item.name = asset.tracker_name
|
||||
|
||||
strip = context.active_sequence_strip
|
||||
asset_casting_index = strip.vsetb_strip_settings.casting_index
|
||||
active_asset = strip.vsetb_strip_settings.casting[asset_casting_index].asset
|
||||
|
||||
self.old_asset = active_asset.tracker_name
|
||||
self.new_asset = ''
|
||||
|
||||
return context.window_manager.invoke_props_dialog(self)
|
||||
#
|
||||
|
||||
def draw(self, context):
|
||||
scn = context.scene
|
||||
settings = get_scene_settings()
|
||||
|
||||
project = settings.active_project
|
||||
|
||||
layout = self.layout
|
||||
col = layout.column()
|
||||
col.use_property_split = True
|
||||
col.prop_search(self, 'old_asset', self, 'assets', text='Old Asset', icon='ASSET_MANAGER')
|
||||
col.prop_search(self, 'new_asset', self, 'assets', text='New Asset', icon='ASSET_MANAGER')
|
||||
|
||||
|
||||
def get_asset_items(self, context):
|
||||
settings = get_scene_settings()
|
||||
project = settings.active_project
|
||||
|
||||
return [(a.id, a.label, '', i) for i, a in enumerate(project.assets)]
|
||||
|
||||
class VSETB_OT_casting_add(Operator):
|
||||
bl_idname = "vse_toolbox.casting_add"
|
||||
bl_label = "Casting Add"
|
||||
bl_description = "Add Asset to Castin"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_property = "asset_name"
|
||||
|
||||
asset_name : EnumProperty(name='', items=get_asset_items)
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
active_strip = context.scene.sequence_editor.active_strip
|
||||
if active_strip:
|
||||
return True
|
||||
|
||||
def invoke(self, context, event):
|
||||
context.window_manager.invoke_search_popup(self)
|
||||
return {'FINISHED'}
|
||||
|
||||
def execute(self, context):
|
||||
scn = context.scene
|
||||
active_strip = scn.sequence_editor.active_strip
|
||||
|
||||
settings = get_scene_settings()
|
||||
strip_settings = get_strip_settings()
|
||||
|
||||
project = settings.active_project
|
||||
|
||||
if strip_settings.casting.get(self.asset_name):
|
||||
asset = project.assets[self.asset_name]
|
||||
self.report({'WARNING'}, f"Asset {asset.label} already casted.")
|
||||
return {"CANCELLED"}
|
||||
|
||||
item = strip_settings.casting.add()
|
||||
asset = project.assets[self.asset_name]
|
||||
|
||||
item.name = asset.name
|
||||
item.id = project.assets[self.asset_name].id
|
||||
item['_name'] = asset.label
|
||||
|
||||
strip_settings.casting.update()
|
||||
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class VSETB_OT_casting_remove(Operator):
|
||||
bl_idname = "vse_toolbox.casting_remove"
|
||||
bl_label = "Remove Item from Casting"
|
||||
bl_description = "Remove Item from Casting"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
active_strip = context.scene.sequence_editor.active_strip
|
||||
if active_strip:
|
||||
return True
|
||||
|
||||
def invoke(self, context, event):
|
||||
scn = context.scene
|
||||
|
||||
strip_settings = get_strip_settings()
|
||||
idx = strip_settings.casting_index
|
||||
|
||||
try:
|
||||
item = strip_settings.casting[idx]
|
||||
except IndexError:
|
||||
pass
|
||||
else:
|
||||
item = strip_settings.casting[strip_settings.casting_index]
|
||||
label = item.asset.label if item.asset.label else 'Empty'
|
||||
info = f"Item {label} removed from casting"
|
||||
strip_settings.casting.remove(idx)
|
||||
|
||||
if strip_settings.casting_index == 0:
|
||||
strip_settings.casting_index = 0
|
||||
else:
|
||||
strip_settings.casting_index -= 1
|
||||
|
||||
self.report({'INFO'}, info)
|
||||
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class VSETB_OT_casting_move(Operator):
|
||||
bl_idname = "vse_toolbox.casting_move"
|
||||
bl_label = "Move Casting items"
|
||||
bl_description = "Move Casting items"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
direction: EnumProperty(
|
||||
items=(
|
||||
('UP', "Up", ""),
|
||||
('DOWN', "Down", ""),
|
||||
)
|
||||
)
|
||||
|
||||
asset_name : StringProperty()
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
active_strip = context.scene.sequence_editor.active_strip
|
||||
if active_strip:
|
||||
return True
|
||||
|
||||
def execute(self, context):
|
||||
scn = context.scene
|
||||
|
||||
strip_settings = get_strip_settings()
|
||||
idx = strip_settings.casting_index
|
||||
|
||||
try:
|
||||
item = strip_settings.casting[idx]
|
||||
except IndexError:
|
||||
pass
|
||||
else:
|
||||
if self.direction == 'DOWN' and idx < len(strip_settings.casting) - 1:
|
||||
item_next = strip_settings.casting[idx+1].name
|
||||
strip_settings.casting.move(idx, idx+1)
|
||||
strip_settings.casting_index += 1
|
||||
|
||||
elif self.direction == 'UP' and idx >= 1:
|
||||
item_prev = strip_settings.casting[idx-1].name
|
||||
strip_settings.casting.move(idx, idx-1)
|
||||
strip_settings.casting_index -= 1
|
||||
|
||||
info = f"Item {item.asset.label} moved to position {(item.asset.label, strip_settings.casting_index + 1)}"
|
||||
self.report({'INFO'}, info)
|
||||
|
||||
return {"FINISHED"}
|
||||
|
||||
class VSETB_OT_copy_casting(Operator):
|
||||
bl_idname = "vse_toolbox.copy_casting"
|
||||
bl_label = "Copy Casting"
|
||||
bl_description = "Copy Casting from active strip"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
active_strip = context.scene.sequence_editor.active_strip
|
||||
strip_settings = get_strip_settings()
|
||||
|
||||
if active_strip and strip_settings.casting:
|
||||
return True
|
||||
|
||||
def execute(self, context):
|
||||
scn = context.scene
|
||||
|
||||
active_strip = scn.sequence_editor.active_strip
|
||||
strip_settings = get_strip_settings()
|
||||
|
||||
datas = [c.to_dict() for c in strip_settings.casting]
|
||||
CASTING_BUFFER.write_text(json.dumps(datas), encoding='utf-8')
|
||||
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class VSETB_OT_paste_casting(Operator):
|
||||
bl_idname = "vse_toolbox.paste_casting"
|
||||
bl_label = "Paste Casting"
|
||||
bl_description = "Paste Casting to active strip"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
active_strip = context.scene.sequence_editor.active_strip
|
||||
if active_strip:
|
||||
return True
|
||||
|
||||
def execute(self, context):
|
||||
scn = context.scene
|
||||
strip_settings = get_strip_settings()
|
||||
|
||||
if not CASTING_BUFFER.exists():
|
||||
self.report({'ERROR'}, f'No Casting to copy.')
|
||||
return {"CANCELLED"}
|
||||
|
||||
casting_datas = json.loads(CASTING_BUFFER.read_text())
|
||||
|
||||
|
||||
|
||||
for strip in context.selected_sequences:
|
||||
strip_settings = strip.vsetb_strip_settings
|
||||
for casting_data in casting_datas:
|
||||
|
||||
item = strip.vsetb_strip_settings.casting.add()
|
||||
|
||||
item.name = casting_data['name']
|
||||
item.id = casting_data['id']
|
||||
item['_name'] = casting_data['_name']
|
||||
|
||||
strip_settings.casting.update()
|
||||
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class VSETB_OT_copy_metadata(Operator):
|
||||
bl_idname = "vse_toolbox.copy_metadata"
|
||||
bl_label = "Copy metadata to selected"
|
||||
bl_description = "Copy Metadata to selected strips"
|
||||
|
||||
metadata : StringProperty()
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return context.selected_sequences and context.active_sequence_strip
|
||||
|
||||
def execute(self, context):
|
||||
prefs = get_addon_prefs()
|
||||
settings = get_scene_settings()
|
||||
project = settings.active_project
|
||||
|
||||
metadata = next((m.field_name for m in project.metadata_types if m.name == self.metadata), None)
|
||||
|
||||
if not metadata:
|
||||
self.report({'ERROR'}, f'No Metadata named {self.metadata}')
|
||||
|
||||
active_strip = context.active_sequence_strip
|
||||
metadata_value = getattr(active_strip.vsetb_strip_settings.metadata, metadata)
|
||||
|
||||
for strip in context.selected_sequences:
|
||||
if strip == context.active_sequence_strip:
|
||||
continue
|
||||
|
||||
setattr(strip.vsetb_strip_settings.metadata, metadata, metadata_value)
|
||||
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
classes = (
|
||||
VSETB_OT_casting_add,
|
||||
VSETB_OT_casting_remove,
|
||||
VSETB_OT_casting_move,
|
||||
VSETB_OT_copy_casting,
|
||||
VSETB_OT_paste_casting,
|
||||
VSETB_OT_casting_replace,
|
||||
VSETB_OT_copy_metadata
|
||||
)
|
||||
|
||||
def register():
|
||||
for cls in classes:
|
||||
bpy.utils.register_class(cls)
|
||||
|
||||
def unregister():
|
||||
for cls in reversed(classes):
|
||||
bpy.utils.unregister_class(cls)
|
|
@ -0,0 +1,184 @@
|
|||
|
||||
from pathlib import Path
|
||||
|
||||
import bpy
|
||||
from bpy.types import Operator
|
||||
from bpy.props import (CollectionProperty, BoolProperty, EnumProperty, StringProperty)
|
||||
|
||||
from vse_toolbox.constants import (EDITS, EDIT_SUFFIXES, MOVIES, MOVIE_SUFFIXES,
|
||||
SOUNDS, SOUND_SUFFIXES)
|
||||
|
||||
from vse_toolbox.sequencer_utils import (clean_sequencer, import_edit, import_movie, import_sound)
|
||||
|
||||
from vse_toolbox.bl_utils import get_scene_settings
|
||||
from vse_toolbox.file_utils import install_module
|
||||
|
||||
|
||||
class VSETB_OT_auto_select_files(Operator):
|
||||
bl_idname = "import.auto_select_files"
|
||||
bl_label = "Auto Select"
|
||||
bl_description = "Auto Select Files"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return True
|
||||
|
||||
def get_items(self, items=[]):
|
||||
if not items:
|
||||
return [('NONE', 'None', '', 0)]
|
||||
return [(e, e, '', i) for i, e in enumerate(sorted(items))]
|
||||
|
||||
def execute(self, context):
|
||||
params = context.space_data.params
|
||||
directory = Path(params.directory.decode())
|
||||
|
||||
EDITS.clear()
|
||||
MOVIES.clear()
|
||||
SOUNDS.clear()
|
||||
|
||||
edits = []
|
||||
movies = []
|
||||
sounds = []
|
||||
|
||||
for file_entry in directory.glob('*'):
|
||||
if file_entry.is_dir():
|
||||
continue
|
||||
|
||||
if file_entry.suffix in EDIT_SUFFIXES:
|
||||
edits.append(file_entry.name)
|
||||
elif file_entry.suffix in MOVIE_SUFFIXES:
|
||||
movies.append(file_entry.name)
|
||||
elif file_entry.suffix in SOUND_SUFFIXES:
|
||||
sounds.append(file_entry.name)
|
||||
|
||||
EDITS.extend(self.get_items(items=edits))
|
||||
MOVIES.extend(self.get_items(items=movies))
|
||||
SOUNDS.extend(self.get_items(items=sounds))
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
class VSETB_OT_import_files(Operator):
|
||||
bl_idname = "vse_toolbox.import_files"
|
||||
bl_label = "Import"
|
||||
bl_description = "Import Edit"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
directory : StringProperty(subtype='DIR_PATH')
|
||||
filepath: StringProperty(
|
||||
name="File Path",
|
||||
description="Filepath used for importing the file",
|
||||
maxlen=1024,
|
||||
subtype='FILE_PATH',
|
||||
)
|
||||
files : CollectionProperty(type=bpy.types.OperatorFileListElement)
|
||||
clean_sequencer : BoolProperty(
|
||||
name="Clean Sequencer",
|
||||
default=False,
|
||||
description="Clean all existing strips in sequencer",
|
||||
)
|
||||
|
||||
import_edit : BoolProperty(name='', default=True)
|
||||
edit: EnumProperty(name='', items=lambda s, c: EDITS)
|
||||
|
||||
import_movie : BoolProperty(name='', default=False)
|
||||
movie: EnumProperty(name='', items=lambda s, c: MOVIES)
|
||||
|
||||
import_sound : BoolProperty(name='', default=False)
|
||||
sound: EnumProperty(name='', items=lambda s, c: SOUNDS)
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return True
|
||||
|
||||
def draw(self, context):
|
||||
scn = context.scene
|
||||
settings = get_scene_settings()
|
||||
|
||||
layout = self.layout
|
||||
layout.use_property_split = True
|
||||
layout.use_property_decorate = False
|
||||
|
||||
col = layout.column(align=True)
|
||||
col.operator('import.auto_select_files', text='Auto Select')
|
||||
|
||||
row = self.layout.row(heading="Import Edit", align=True)
|
||||
row.prop(self, 'import_edit')
|
||||
sub = row.row(align=True)
|
||||
sub.active = self.import_edit
|
||||
sub.prop(self, 'edit')
|
||||
|
||||
row = self.layout.row(heading="Import Movie", align=True)
|
||||
row.prop(self, 'import_movie')
|
||||
sub = row.row()
|
||||
sub.active = self.import_movie
|
||||
sub.prop(self, 'movie')
|
||||
|
||||
row = self.layout.row(heading="Import Sound", align=True)
|
||||
row.prop(self, 'import_sound')
|
||||
sub = row.row()
|
||||
sub.active = self.import_sound
|
||||
sub.prop(self, 'sound')
|
||||
|
||||
col = layout.column()
|
||||
col.separator()
|
||||
col.prop(self, 'clean_sequencer')
|
||||
|
||||
def invoke(self, context, event):
|
||||
context.window_manager.fileselect_add(self)
|
||||
return {'RUNNING_MODAL'}
|
||||
|
||||
def execute(self, context):
|
||||
otio = install_module('opentimelineio')
|
||||
|
||||
edit_filepath = Path(self.directory, self.edit)
|
||||
if not edit_filepath.exists():
|
||||
self.import_edit = False
|
||||
|
||||
movie_filepath = Path(self.directory, self.movie)
|
||||
if not movie_filepath.exists():
|
||||
self.import_movie = False
|
||||
|
||||
sound_filepath = Path(self.directory, self.sound)
|
||||
if not sound_filepath.exists():
|
||||
self.import_sound = False
|
||||
|
||||
if self.clean_sequencer:
|
||||
clean_sequencer(
|
||||
edit=self.import_edit,
|
||||
movie=self.import_movie,
|
||||
sound=self.import_sound,
|
||||
)
|
||||
|
||||
if self.import_edit:
|
||||
print(f'[>.] Loading Edit from: {str(edit_filepath)}')
|
||||
import_edit(edit_filepath, adapter="cmx_3600")
|
||||
|
||||
if self.import_movie:
|
||||
print(f'[>.] Loading Movie from: {str(movie_filepath)}')
|
||||
import_movie(movie_filepath)
|
||||
|
||||
if self.import_sound:
|
||||
print(f'[>.] Loading Audio from: {str(sound_filepath)}')
|
||||
import_sound(sound_filepath)
|
||||
elif not self.import_sound and self.import_movie:
|
||||
print(f'[>.] Loading Audio from Movie: {str(movie_filepath)}')
|
||||
import_sound(movie_filepath)
|
||||
|
||||
context.scene.sequence_editor.sequences.update()
|
||||
|
||||
return {"FINISHED"}
|
||||
|
||||
classes = (
|
||||
VSETB_OT_auto_select_files,
|
||||
VSETB_OT_import_files,
|
||||
)
|
||||
|
||||
def register():
|
||||
for cls in classes:
|
||||
bpy.utils.register_class(cls)
|
||||
|
||||
def unregister():
|
||||
for cls in reversed(classes):
|
||||
bpy.utils.unregister_class(cls)
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,70 @@
|
|||
|
||||
import time
|
||||
|
||||
import bpy
|
||||
from bpy.types import Operator
|
||||
|
||||
from vse_toolbox.sequencer_utils import (get_strips, render_strips)
|
||||
from vse_toolbox.bl_utils import get_scene_settings
|
||||
|
||||
|
||||
|
||||
class VSETB_OT_render(Operator):
|
||||
bl_idname = "vse_toolbox.strips_render"
|
||||
bl_label = "Render Shots Strips"
|
||||
bl_description = "Render Shots Strips"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
#selected_only : BoolProperty(name="Selected Only", default=False)
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
settings = get_scene_settings()
|
||||
return settings.active_project
|
||||
|
||||
def invoke(self, context, event):
|
||||
scn = context.scene
|
||||
settings = get_scene_settings()
|
||||
|
||||
return context.window_manager.invoke_props_dialog(self)
|
||||
|
||||
def draw(self, context):
|
||||
scn = context.scene
|
||||
settings = get_scene_settings()
|
||||
|
||||
layout = self.layout
|
||||
col = layout.column()
|
||||
col.use_property_split = True
|
||||
col.use_property_decorate = False
|
||||
#col.prop(settings, 'channel', text='Channel')
|
||||
#col.prop(self, 'selected_only')
|
||||
|
||||
col.prop(settings.active_project, "render_template")
|
||||
|
||||
def execute(self, context):
|
||||
scn = context.scene
|
||||
settings = get_scene_settings()
|
||||
strips = get_strips(channel='Shots', selected_only=True)
|
||||
|
||||
start_time = time.perf_counter()
|
||||
render_strips(strips, settings.active_project.render_template)
|
||||
|
||||
self.report({"INFO"}, f'Strips rendered in {time.perf_counter()-start_time} seconds')
|
||||
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
classes = (
|
||||
VSETB_OT_render,
|
||||
)
|
||||
|
||||
def register():
|
||||
for cls in classes:
|
||||
bpy.utils.register_class(cls)
|
||||
|
||||
def unregister():
|
||||
for cls in reversed(classes):
|
||||
bpy.utils.unregister_class(cls)
|
|
@ -0,0 +1,216 @@
|
|||
|
||||
import bpy
|
||||
from bpy.types import Operator
|
||||
from bpy.props import (BoolProperty, StringProperty)
|
||||
|
||||
from vse_toolbox.sequencer_utils import (get_strips, rename_strips, set_channels,
|
||||
get_channel_index, new_text_strip)
|
||||
|
||||
from vse_toolbox.bl_utils import get_scene_settings
|
||||
|
||||
|
||||
class VSETB_OT_rename(Operator):
|
||||
bl_idname = "vse_toolbox.strips_rename"
|
||||
bl_label = "Rename Strips"
|
||||
bl_description = "Rename Strips"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
#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)
|
||||
#by_sequence : BoolProperty(
|
||||
# name="Reset By Sequence",
|
||||
# description="Reset Start Number for each sequence",
|
||||
# default=False
|
||||
#)
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
settings = get_scene_settings()
|
||||
return settings.active_project
|
||||
|
||||
def invoke(self, context, event):
|
||||
scn = context.scene
|
||||
settings = get_scene_settings()
|
||||
|
||||
return context.window_manager.invoke_props_dialog(self)
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
scn = context.scene
|
||||
settings = get_scene_settings()
|
||||
project = settings.active_project
|
||||
|
||||
col = layout.column()
|
||||
col.use_property_split = True
|
||||
col.use_property_decorate = False
|
||||
|
||||
if self.channel_name == 'Shots':
|
||||
col.prop(project, 'shot_template', text='Shot Name')
|
||||
col.prop(project, 'shot_start_number', text='Start Number')
|
||||
col.prop(project, 'shot_increment', text='Increment')
|
||||
col.prop(project, 'reset_by_sequence')
|
||||
elif self.channel_name == 'Sequences':
|
||||
col.prop(project, 'sequence_template' ,text='Sequence Name')
|
||||
col.prop(project, 'sequence_start_number', text='Start Number')
|
||||
col.prop(project, 'sequence_increment', text='Increment')
|
||||
|
||||
col.prop(self, 'selected_only')
|
||||
|
||||
def execute(self, context):
|
||||
scn = context.scene
|
||||
settings = get_scene_settings()
|
||||
project = settings.active_project
|
||||
|
||||
strips = get_strips(channel=self.channel_name, selected_only=self.selected_only)
|
||||
if self.channel_name == 'Shots':
|
||||
rename_strips(strips,
|
||||
template=project.shot_template,
|
||||
increment=project.shot_increment, start_number=project.shot_start_number,
|
||||
by_sequence=project.reset_by_sequence
|
||||
)
|
||||
|
||||
if self.channel_name == 'Sequences':
|
||||
rename_strips(strips,
|
||||
template=project.sequence_template,
|
||||
increment=project.sequence_increment, start_number=project.sequence_start_number
|
||||
)
|
||||
|
||||
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"
|
||||
bl_description = "Set resolution, frame end and channel names"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return True
|
||||
|
||||
def execute(self, context):
|
||||
scn = context.scene
|
||||
|
||||
set_channels()
|
||||
movie = get_strips(channel='Movie')
|
||||
if movie:
|
||||
movie = movie[0]
|
||||
movie.transform.scale_x = movie.transform.scale_y = 1
|
||||
elem = movie.strip_elem_from_frame(scn.frame_current)
|
||||
scn.render.resolution_x = elem.orig_width
|
||||
scn.render.resolution_y = elem.orig_height
|
||||
else:
|
||||
self.report({'INFO'}, f'Cannot set Resolution. No Movie Found.')
|
||||
|
||||
scn.view_settings.view_transform = 'Standard'
|
||||
scn.render.image_settings.file_format = 'FFMPEG'
|
||||
scn.render.ffmpeg.gopsize = 5
|
||||
scn.render.ffmpeg.constant_rate_factor = 'HIGH'
|
||||
scn.render.ffmpeg.format = 'QUICKTIME'
|
||||
scn.render.ffmpeg.audio_codec = 'AAC'
|
||||
scn.render.ffmpeg.audio_codec = 'MP3'
|
||||
scn.render.ffmpeg.audio_mixrate = 44100
|
||||
scn.render.ffmpeg.audio_bitrate = 128
|
||||
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class VSETB_OT_set_stamps(Operator):
|
||||
bl_idname = "vse_toolbox.set_stamps"
|
||||
bl_label = "Set Stamps"
|
||||
bl_description = "Set Stamps on Video"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def execute(self, context):
|
||||
scn = context.scene
|
||||
settings = get_scene_settings()
|
||||
#strip_settings = get_strip_settings()
|
||||
channel_index = get_channel_index('Stamps')
|
||||
|
||||
for strip in get_strips('Stamps'):
|
||||
if strip.type == 'META':
|
||||
scn.sequence_editor.sequences.remove(strip)
|
||||
|
||||
bpy.ops.sequencer.select_all(action='DESELECT')
|
||||
|
||||
crop_x = int(scn.render.resolution_x * 0.4)
|
||||
crop_max_y = int(scn.render.resolution_y * 0.96)
|
||||
crop_min_y = int(scn.render.resolution_y * 0.01)
|
||||
|
||||
stamp_params = dict(start=scn.frame_start, end=scn.frame_end,
|
||||
font_size=22, y=0.015, box_margin=0.005, select=True, box_color=(0, 0, 0, 0.5))
|
||||
|
||||
# Project Name
|
||||
project_strip_stamp = new_text_strip('project_name_stamp', channel=1, **stamp_params,
|
||||
text=settings.active_project.name, x=0.01, align_x='LEFT', align_y='BOTTOM')
|
||||
|
||||
project_strip_stamp.crop.max_x = crop_x * 2
|
||||
project_strip_stamp.crop.max_y = crop_max_y
|
||||
project_strip_stamp.crop.min_y = crop_min_y
|
||||
|
||||
# Shot Name
|
||||
shot_strip_stamp = new_text_strip('shot_name_stamp', channel=2, **stamp_params,
|
||||
text='{active_shot_name}', align_y='BOTTOM')
|
||||
|
||||
shot_strip_stamp.crop.min_x = crop_x
|
||||
shot_strip_stamp.crop.max_x = crop_x
|
||||
shot_strip_stamp.crop.max_y = crop_max_y
|
||||
shot_strip_stamp.crop.min_y = crop_min_y
|
||||
|
||||
# Frame Range
|
||||
frame_strip_stamp = new_text_strip('frame_range_stamp', channel=3, **stamp_params,
|
||||
text='{active_shot_frame} / {active_shot_duration}', x=0.99, align_x='RIGHT', align_y='BOTTOM')
|
||||
|
||||
frame_strip_stamp.crop.min_x = crop_x *2
|
||||
frame_strip_stamp.crop.max_y = crop_max_y
|
||||
frame_strip_stamp.crop.min_y = crop_min_y
|
||||
|
||||
bpy.ops.sequencer.meta_make()
|
||||
stamps_strip = context.active_sequence_strip
|
||||
stamps_strip.name = 'Stamps'
|
||||
stamps_strip.channel = channel_index
|
||||
|
||||
#stamps_strip = scn.sequence_editor.sequences.new_meta('Stamps', scn.frame_start, scn.frame_end)
|
||||
#stamps_strip.channel = get_channel_index('Stamps')
|
||||
scn.frame_set(scn.frame_current) # For update stamps
|
||||
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
classes = (
|
||||
VSETB_OT_rename,
|
||||
VSETB_OT_set_sequencer,
|
||||
VSETB_OT_set_stamps,
|
||||
VSETB_OT_show_waveform,
|
||||
)
|
||||
|
||||
def register():
|
||||
for cls in classes:
|
||||
bpy.utils.register_class(cls)
|
||||
|
||||
def unregister():
|
||||
for cls in reversed(classes):
|
||||
bpy.utils.unregister_class(cls)
|
|
@ -0,0 +1,319 @@
|
|||
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
import csv
|
||||
from datetime import datetime
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
import bpy
|
||||
from bpy.types import (Operator, Menu, OperatorFileListElement)
|
||||
from bpy.props import (EnumProperty, )
|
||||
from bl_operators.presets import AddPresetBase
|
||||
|
||||
|
||||
from vse_toolbox.sequencer_utils import (get_strips, get_strip_sequence_name)
|
||||
from vse_toolbox.bl_utils import (get_addon_prefs, get_scene_settings)
|
||||
from vse_toolbox.file_utils import open_file
|
||||
|
||||
|
||||
class VSETB_MT_spreadsheet_presets(Menu):
|
||||
bl_label = 'Presets'
|
||||
preset_subdir = 'vse_toolbox'
|
||||
preset_operator = 'script.execute_preset'
|
||||
draw = Menu.draw_preset
|
||||
|
||||
|
||||
class VSETB_OT_add_spreadsheet_preset(AddPresetBase, Operator):
|
||||
|
||||
bl_idname = "vse_toolbox.add_spreadsheet_preset"
|
||||
bl_label = "Add Spreadsheet Preset"
|
||||
bl_description = "Add Spreadsheet Preset"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
preset_menu = 'VSETB_MT_spreadsheet_presets'
|
||||
|
||||
#preset_menu = 'VSETB_OT_MT_spreadsheet_presets'
|
||||
|
||||
# Common variable used for all preset values
|
||||
#C.scene.vsetb_settings.active_project.spreadsheet_options
|
||||
preset_defines = [
|
||||
'scene = bpy.context.scene',
|
||||
'settings = scene.vsetb_settings',
|
||||
'project = settings.active_project'
|
||||
]
|
||||
|
||||
# Properties to store in the preset
|
||||
preset_values = [
|
||||
'project.spreadsheet',
|
||||
'project.spreadsheet_options'
|
||||
]
|
||||
|
||||
# Directory to store the presets
|
||||
preset_subdir = 'vse_toolbox'
|
||||
|
||||
|
||||
|
||||
class VSETB_OT_spreadsheet_move(Operator):
|
||||
bl_idname = "vse_toolbox.spreadsheet_move"
|
||||
bl_label = "Move Spreadsheet items"
|
||||
bl_description = "Move Spreadsheet items"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
direction: EnumProperty(
|
||||
items=(
|
||||
('UP', "Up", ""),
|
||||
('DOWN', "Down", ""),
|
||||
)
|
||||
)
|
||||
|
||||
def execute(self, context):
|
||||
scn = context.scene
|
||||
project = get_scene_settings().active_project
|
||||
|
||||
idx = project.spreadsheet_index
|
||||
|
||||
try:
|
||||
item = project.spreadsheet[idx]
|
||||
except IndexError:
|
||||
pass
|
||||
else:
|
||||
if self.direction == 'DOWN' and idx < len(project.spreadsheet) - 1:
|
||||
item_next = project.spreadsheet[idx+1].name
|
||||
project.spreadsheet.move(idx, idx+1)
|
||||
project.spreadsheet_index += 1
|
||||
|
||||
elif self.direction == 'UP' and idx >= 1:
|
||||
item_prev = project.spreadsheet[idx-1].name
|
||||
project.spreadsheet.move(idx, idx-1)
|
||||
project.spreadsheet_index -= 1
|
||||
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
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"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
settings = get_scene_settings()
|
||||
return settings.active_project
|
||||
|
||||
def invoke(self, context, event):
|
||||
settings = get_scene_settings()
|
||||
project = settings.active_project
|
||||
|
||||
return context.window_manager.invoke_props_dialog(self)
|
||||
|
||||
def draw(self, context):
|
||||
scn = context.scene
|
||||
settings = get_scene_settings()
|
||||
project = settings.active_project
|
||||
options = project.spreadsheet_options
|
||||
|
||||
layout = self.layout
|
||||
|
||||
row = layout.row()
|
||||
row.template_list("VSETB_UL_spreadsheet", "spreadsheet", project, "spreadsheet", project, "spreadsheet_index", rows=8)
|
||||
|
||||
col_tool = row.column(align=True)
|
||||
|
||||
#bpy.types.VSETB_PT_presets.draw_panel_header(col_tool)
|
||||
#col_tool.operator('wm.call_menu', icon="PRESET").name = 'VSETB_MT_spreadsheet_presets'
|
||||
#col_tool.operator('vse_toolbox.load_spreadsheet_preset', icon='PRESET', text="")
|
||||
op = col_tool.operator('wm.call_panel', icon="PRESET", emboss=False, text='')
|
||||
op.name = 'VSETB_PT_presets'
|
||||
op.keep_open = False
|
||||
|
||||
col_tool.separator()
|
||||
col_tool.operator('vse_toolbox.spreadsheet_move', icon='TRIA_UP', text="").direction = 'UP'
|
||||
col_tool.operator('vse_toolbox.spreadsheet_move', icon='TRIA_DOWN', text="").direction = 'DOWN'
|
||||
|
||||
col = layout.column()
|
||||
col.use_property_split = True
|
||||
|
||||
row = col.row(align=True, heading='Custom Name')
|
||||
#row.use_property_split = True
|
||||
row.prop(options, 'use_custom_name', text='')
|
||||
sub = row.row(align=True)
|
||||
sub.enabled = options.use_custom_name
|
||||
sub.prop(options, 'custom_name', text='')
|
||||
|
||||
col.separator()
|
||||
|
||||
row = col.row(align=False)
|
||||
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(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]
|
||||
rows = []
|
||||
|
||||
# Header
|
||||
rows.append([cell.export_name for cell in cells])
|
||||
|
||||
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 = []
|
||||
for cell in cells:
|
||||
if cell.type == "METADATA":
|
||||
row += [getattr(strip.vsetb_strip_settings.metadata, cell.field_name)]
|
||||
elif cell.type == "ASSET_TYPE":
|
||||
asset_castings = []
|
||||
for asset_casting in strip.vsetb_strip_settings.casting:
|
||||
asset = asset_casting.asset
|
||||
if not asset.asset_type == cell.name:
|
||||
continue
|
||||
|
||||
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 {options.custom_name}')
|
||||
else:
|
||||
asset_castings.append(asset.tracker_name)
|
||||
|
||||
row += [separator.join(asset_castings)]
|
||||
elif cell.field_name == 'EPISODE':
|
||||
row += [settings.active_episode.name]
|
||||
elif cell.field_name == 'SEQUENCE':
|
||||
row += [get_strip_sequence_name(strip)]
|
||||
elif cell.field_name == 'SHOT':
|
||||
row += [strip.name]
|
||||
elif cell.field_name == 'DESCRIPTION':
|
||||
row += [strip.vsetb_strip_settings.description]
|
||||
elif cell.field_name == 'FRAMES':
|
||||
row += [strip.frame_final_duration]
|
||||
|
||||
rows.append(row)
|
||||
|
||||
#print(rows)
|
||||
|
||||
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}'):
|
||||
export_path = export_path.parent
|
||||
|
||||
else: # It's a directory
|
||||
if project.type == 'TVSHOW':
|
||||
export_name = '{date}_{project}_{episode}_{tracker}_shots.{ext}'
|
||||
else:
|
||||
export_name = '{date}_{project}_{tracker}_shots.{ext}'
|
||||
|
||||
date = datetime.now().strftime('%Y_%m_%d')
|
||||
project_name = project.name.replace(' ', '_').lower()
|
||||
episode_name = episode.name.replace(' ', '_').lower() if episode else 'episode'
|
||||
ext = options.format.lower()
|
||||
|
||||
export_name = export_name.format(date=date, project=project_name,
|
||||
episode=episode_name, tracker=settings.tracker_name.lower(), ext=ext)
|
||||
|
||||
export_path = export_path / export_name
|
||||
|
||||
#2023_04_11_kitsu_boris_ep01_shots
|
||||
export_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
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=options.delimiter)
|
||||
for row in rows:
|
||||
writer.writerow(row)
|
||||
|
||||
elif options.format == 'XLSX':
|
||||
try:
|
||||
import openpyxl
|
||||
except ModuleNotFoundError:
|
||||
self.report({'INFO'}, 'Installing openpyxl')
|
||||
openpyxl = install_module('openpyxl')
|
||||
|
||||
from openpyxl import Workbook
|
||||
|
||||
workbook = Workbook()
|
||||
worksheet = workbook.active
|
||||
for row in rows:
|
||||
worksheet.append(row)
|
||||
|
||||
for col in worksheet.columns:
|
||||
letter = col[0].column_letter
|
||||
worksheet.column_dimensions[letter].auto_size = True
|
||||
|
||||
# Save the file
|
||||
workbook.save(str(export_path))
|
||||
|
||||
if options.open_folder:
|
||||
open_file(export_path, select=True)
|
||||
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
|
||||
class VSETB_OT_import_spreadsheet(Operator):
|
||||
bl_idname = "vse_toolbox.import_spreadsheet"
|
||||
bl_label = "Import Spreadsheet"
|
||||
bl_description = "Create strips from nb frames with casting and custom data"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
directory : StringProperty(subtype='DIR_PATH')
|
||||
filepath: StringProperty(
|
||||
name="File Path",
|
||||
description="Filepath used for importing the file",
|
||||
maxlen=1024,
|
||||
subtype='FILE_PATH',
|
||||
)
|
||||
files : CollectionProperty(type=OperatorFileListElement)
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
settings = get_scene_settings()
|
||||
return settings.active_project
|
||||
|
||||
def invoke(self, context, event):
|
||||
settings = get_scene_settings()
|
||||
project = settings.active_project
|
||||
|
||||
return context.window_manager.invoke_props_dialog(self)
|
||||
|
||||
def draw(self, context):
|
||||
scn = context.scene
|
||||
|
||||
def execute(self, context):
|
||||
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
classes = (
|
||||
VSETB_OT_add_spreadsheet_preset,
|
||||
VSETB_MT_spreadsheet_presets,
|
||||
VSETB_OT_spreadsheet_move,
|
||||
VSETB_OT_export_spreadsheet,
|
||||
)
|
||||
|
||||
def register():
|
||||
for cls in classes:
|
||||
bpy.utils.register_class(cls)
|
||||
|
||||
def unregister():
|
||||
for cls in reversed(classes):
|
||||
bpy.utils.unregister_class(cls)
|
|
@ -0,0 +1,367 @@
|
|||
|
||||
from pathlib import Path
|
||||
|
||||
import bpy
|
||||
from bpy.types import (Operator, )
|
||||
from bpy.props import (BoolProperty, EnumProperty, StringProperty)
|
||||
|
||||
from vse_toolbox.constants import (ASSET_PREVIEWS, PREVIEWS_DIR)
|
||||
|
||||
from vse_toolbox.sequencer_utils import (get_strips, get_strip_render_path, get_strip_sequence_name)
|
||||
|
||||
from vse_toolbox.bl_utils import (get_addon_prefs, get_scene_settings)
|
||||
from vse_toolbox.file_utils import (norm_name,)
|
||||
|
||||
|
||||
class VSETB_OT_tracker_connect(Operator):
|
||||
bl_idname = "vse_toolbox.tracker_connect"
|
||||
bl_label = "Connect to Tracker"
|
||||
bl_description = "Connect to Tracker"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
prefs = get_addon_prefs()
|
||||
if prefs.tracker:
|
||||
|
||||
return True
|
||||
|
||||
def execute(self, context):
|
||||
prefs = get_addon_prefs()
|
||||
settings = get_scene_settings()
|
||||
try:
|
||||
prefs.tracker.connect()
|
||||
self.report({'INFO'}, f'Sucessfully login to {settings.tracker_name.title()}')
|
||||
return {"FINISHED"}
|
||||
except Exception as e:
|
||||
print('e: ', e)
|
||||
self.report({'ERROR'}, f'Cannot connect to tracker.')
|
||||
return {"CANCELLED"}
|
||||
|
||||
|
||||
class VSETB_OT_load_assets(Operator):
|
||||
bl_idname = "vse_toolbox.load_assets"
|
||||
bl_label = "Load Assets for current projects"
|
||||
bl_description = "Load Assets"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
settings = get_scene_settings()
|
||||
if settings.active_project:
|
||||
return True
|
||||
|
||||
def execute(self, context):
|
||||
settings = get_scene_settings()
|
||||
prefs = get_addon_prefs()
|
||||
tracker = prefs.tracker
|
||||
|
||||
tracker.connect()
|
||||
project = settings.active_project
|
||||
project.assets.clear()
|
||||
|
||||
assets = tracker.get_assets(project['id'])
|
||||
if not assets:
|
||||
self.report({'ERROR'}, f'No Assets found for {project.name}.')
|
||||
|
||||
for asset_data in assets:
|
||||
asset = project.assets.add()
|
||||
|
||||
asset.name = asset_data['id']
|
||||
asset.norm_name = norm_name(asset_data['name'], separator=' ', format=str.lower)
|
||||
|
||||
asset.tracker_name = asset_data['name']
|
||||
asset.id = asset_data['id']
|
||||
asset.asset_type = asset_data['asset_type']
|
||||
|
||||
#for key, value in asset_data.get('data', {}).items():
|
||||
asset['metadata'] = asset_data.get('data', {})
|
||||
|
||||
preview_id = asset_data.get('preview_file_id')
|
||||
if preview_id:
|
||||
asset.preview = preview_id
|
||||
preview_path = Path(PREVIEWS_DIR / project.id / preview_id).with_suffix('.png')
|
||||
tracker.download_preview(preview_id, preview_path)
|
||||
|
||||
if preview_id not in ASSET_PREVIEWS:
|
||||
ASSET_PREVIEWS.load(preview_id, preview_path.as_posix(), 'IMAGE', True)
|
||||
|
||||
|
||||
self.report({'INFO'}, f'Assets for {project.name} successfully loaded')
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class VSETB_OT_load_projects(Operator):
|
||||
bl_idname = "vse_toolbox.load_projects"
|
||||
bl_label = "Load Projects"
|
||||
bl_description = "Load Projects"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return True
|
||||
|
||||
def execute(self, context):
|
||||
settings = get_scene_settings()
|
||||
prefs = get_addon_prefs()
|
||||
tracker = prefs.tracker
|
||||
|
||||
old_project_name = settings.project_name.replace(' ', '_').upper()
|
||||
|
||||
settings.projects.clear()
|
||||
tracker.connect()
|
||||
|
||||
for project_data in tracker.get_projects():
|
||||
project = settings.projects.add()
|
||||
project.type = project_data['production_type'].upper().replace(' ', '')
|
||||
project.name = project_data['name']
|
||||
project.id = project_data['id']
|
||||
|
||||
if project.type == 'TVSHOW':
|
||||
for episode_data in tracker.get_episodes(project_data):
|
||||
episode = project.episodes.add()
|
||||
episode.name = episode_data['name']
|
||||
episode.id = episode_data['id']
|
||||
|
||||
for metadata_data in tracker.get_metadata_types(project_data):
|
||||
#pprint(metadata_data)
|
||||
metadata_type = project.metadata_types.add()
|
||||
metadata_type.name = metadata_data['name']
|
||||
metadata_type.field_name = metadata_data['field_name']
|
||||
#metadata_type['choices'] = metadata_data['choices']
|
||||
|
||||
if prefs.sort_metadata_items:
|
||||
metadata_data['choices'].sort()
|
||||
|
||||
for choice in metadata_data['choices']:
|
||||
choice_item = metadata_type.choices.add()
|
||||
choice_item.name = choice
|
||||
|
||||
metadata_type['entity_type'] = metadata_data['entity_type'].upper()
|
||||
|
||||
for status_data in tracker.get_task_statuses(project_data):
|
||||
#print(metadata_data)
|
||||
task_status = project.task_statuses.add()
|
||||
task_status.name = status_data['short_name'].upper()
|
||||
|
||||
for task_type_data in tracker.get_shot_task_types(project_data):
|
||||
task_type = project.task_types.add()
|
||||
task_type.name = task_type_data['name']
|
||||
|
||||
for asset_type_data in tracker.get_asset_types(project_data):
|
||||
asset_type = project.asset_types.add()
|
||||
asset_type.name = asset_type_data['name']
|
||||
|
||||
project.set_spreadsheet()
|
||||
|
||||
for project in settings.projects:
|
||||
if project.name.replace(' ', '_').upper() == old_project_name:
|
||||
settings.project_name = project.name
|
||||
|
||||
bpy.ops.vse_toolbox.load_settings()
|
||||
#else:
|
||||
# print('Could Not restore Project Name')
|
||||
|
||||
#if settings.active_project:
|
||||
# settings.active_project.set_strip_metadata()
|
||||
|
||||
#bpy.ops.vse_toolbox.load_assets()
|
||||
|
||||
self.report({"INFO"}, 'Successfully Load Tracker Projects')
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
class VSETB_OT_new_episode(Operator):
|
||||
bl_idname = "vse_toolbox.new_episode"
|
||||
bl_label = "New Epispde"
|
||||
bl_description = "Add new Episode to Project"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
episode_name : StringProperty(name="Episode Name", default="")
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return True
|
||||
|
||||
def invoke(self, context, event):
|
||||
scn = context.scene
|
||||
settings = get_scene_settings()
|
||||
|
||||
return context.window_manager.invoke_props_dialog(self)
|
||||
|
||||
def execute(self, context):
|
||||
settings = get_scene_settings()
|
||||
prefs = get_addon_prefs()
|
||||
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'}
|
||||
|
||||
|
||||
def get_task_status_items(self, context):
|
||||
settings = get_scene_settings()
|
||||
project = settings.active_project
|
||||
|
||||
status_items = [('CURRENT', 'Current', '')]
|
||||
|
||||
if project:
|
||||
status_items += [(t.name, t.name, '') for t in project.task_statuses]
|
||||
return status_items
|
||||
|
||||
def get_task_type_items(self, context):
|
||||
settings = get_scene_settings()
|
||||
project = settings.active_project
|
||||
|
||||
if not project:
|
||||
return [('NONE', 'None', '')]
|
||||
|
||||
return [(t.name, t.name, '') for t in project.task_types]
|
||||
|
||||
class VSETB_OT_upload_to_tracker(Operator):
|
||||
bl_idname = "vse_toolbox.upload_to_tracker"
|
||||
bl_label = "Upload to tracker"
|
||||
bl_description = "Upload selected strip to tracker"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
task : EnumProperty(items=get_task_type_items)
|
||||
status : EnumProperty(items=get_task_status_items)
|
||||
comment : StringProperty()
|
||||
add_preview : BoolProperty(default=True)
|
||||
preview_mode : EnumProperty(items=[(m, m.title().replace('_', ' '), '')
|
||||
for m in ('ONLY_NEW', 'REPLACE', 'ADD')])
|
||||
set_main_preview : BoolProperty(default=True)
|
||||
casting : BoolProperty(default=True)
|
||||
custom_data : BoolProperty(default=True)
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return True
|
||||
|
||||
def invoke(self, context, event):
|
||||
prefs = get_addon_prefs()
|
||||
settings = get_scene_settings()
|
||||
project = settings.active_project
|
||||
|
||||
tracker = prefs.tracker
|
||||
tracker.connect()
|
||||
|
||||
#self.bl_label = f"Upload to {settings.tracker_name.title()}"
|
||||
|
||||
return context.window_manager.invoke_props_dialog(self)
|
||||
|
||||
def draw(self, context):
|
||||
scn = context.scene
|
||||
settings = get_scene_settings()
|
||||
|
||||
layout = self.layout
|
||||
col = layout.column()
|
||||
col.use_property_split = True
|
||||
col.prop(self, 'task', text='Task')
|
||||
col.prop(self, 'status', text='Status')
|
||||
col.prop(self, 'comment', text='Comment')
|
||||
row = col.row(heading='Add Preview')
|
||||
row.prop(self, 'add_preview', text='')
|
||||
row.prop(self, 'preview_mode', text='')
|
||||
col.separator()
|
||||
col.prop(self, 'casting', text='Casting')
|
||||
col.prop(self, 'custom_data', text='Custom Data')
|
||||
|
||||
def execute(self, context):
|
||||
#self.report({'ERROR'}, f'Export not implemented yet.')
|
||||
prefs = get_addon_prefs()
|
||||
settings = get_scene_settings()
|
||||
project = settings.active_project
|
||||
episode = None
|
||||
if settings.active_episode:
|
||||
episode = settings.active_episode.id
|
||||
|
||||
|
||||
tracker = prefs.tracker
|
||||
|
||||
status = self.status
|
||||
if status == 'CURRENT':
|
||||
status = None
|
||||
|
||||
for strip in get_strips(channel='Shots', selected_only=True):
|
||||
sequence_name = get_strip_sequence_name(strip)
|
||||
shot_name = strip.name
|
||||
sequence = tracker.get_sequence(sequence_name, episode=episode)
|
||||
metadata = strip.vsetb_strip_settings.metadata.to_dict()
|
||||
#print(metadata)
|
||||
|
||||
if not sequence:
|
||||
self.report({"INFO"}, f'Create sequence {sequence_name} in Kitsu')
|
||||
sequence = tracker.new_sequence(sequence_name, episode=episode)
|
||||
|
||||
shot = tracker.get_shot(shot_name, sequence=sequence)
|
||||
if not shot:
|
||||
self.report({"INFO"}, f'Create shot {shot_name} in Kitsu')
|
||||
shot = tracker.new_shot(shot_name, sequence=sequence)
|
||||
|
||||
task = tracker.get_task(self.task, entity=shot)
|
||||
if not task:
|
||||
task = tracker.new_task(shot, task_type=self.task)
|
||||
|
||||
# print('\n', 'task comment')
|
||||
# print(task['last_comment'])
|
||||
|
||||
preview = None
|
||||
if self.add_preview:
|
||||
preview = Path(get_strip_render_path(strip, project.render_template))
|
||||
#print(preview)
|
||||
if not preview.exists():
|
||||
print(f'The preview {preview} not exists')
|
||||
preview = None
|
||||
|
||||
elif task.get('last_comment') and task['last_comment']['previews']:
|
||||
if self.preview_mode == 'REPLACE':
|
||||
tracker.remove_comment(task['last_comment'])
|
||||
elif self.preview_mode == 'ONLY_NEW':
|
||||
preview = None
|
||||
|
||||
#print(f'{preview=}')
|
||||
#print(f'{status=}')
|
||||
if status or preview:
|
||||
tracker.new_comment(task, comment=self.comment, status=status, preview=preview, set_main_preview=self.set_main_preview)
|
||||
|
||||
if self.custom_data:
|
||||
metadata = strip.vsetb_strip_settings.metadata.to_dict()
|
||||
description = strip.vsetb_strip_settings.description
|
||||
tracker.update_data(shot, metadata, frames=strip.frame_final_duration, description=description)
|
||||
|
||||
if self.casting:
|
||||
casting = [{'asset_id': a.id, 'nb_occurences': a.instance} for a in strip.vsetb_strip_settings.casting]
|
||||
tracker.update_casting(shot, casting)
|
||||
|
||||
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
classes = (
|
||||
VSETB_OT_load_assets,
|
||||
VSETB_OT_load_projects,
|
||||
VSETB_OT_new_episode,
|
||||
VSETB_OT_tracker_connect,
|
||||
VSETB_OT_upload_to_tracker,
|
||||
)
|
||||
|
||||
def register():
|
||||
for cls in classes:
|
||||
bpy.utils.register_class(cls)
|
||||
|
||||
def unregister():
|
||||
for cls in reversed(classes):
|
||||
bpy.utils.unregister_class(cls)
|
|
@ -0,0 +1,25 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
from vse_toolbox.ui import (panels, preferences, properties)
|
||||
|
||||
modules = (
|
||||
panels,
|
||||
preferences,
|
||||
properties
|
||||
)
|
||||
|
||||
if 'bpy' in locals():
|
||||
import importlib
|
||||
|
||||
for mod in modules:
|
||||
importlib.reload(mod)
|
||||
|
||||
import bpy
|
||||
|
||||
def register():
|
||||
for mod in modules:
|
||||
mod.register()
|
||||
|
||||
def unregister():
|
||||
for mod in modules:
|
||||
mod.unregister()
|
|
@ -126,13 +126,6 @@ class AssetCasting(PropertyGroup):
|
|||
'_name': self.get('_name')
|
||||
}
|
||||
|
||||
class SpreadsheetCell(PropertyGroup):
|
||||
export_name : StringProperty()
|
||||
enabled : BoolProperty(default=True)
|
||||
field_name : StringProperty()
|
||||
type : EnumProperty(items=[(t, t, "") for t in ('METADATA', 'SHOT', 'ASSET_TYPE')])
|
||||
#sort : BoolProperty(default=True)
|
||||
|
||||
|
||||
class AssetType(PropertyGroup):
|
||||
__annotations__ = {}
|
||||
|
@ -166,12 +159,20 @@ class Episode(PropertyGroup):
|
|||
return self.get(settings.project_name)
|
||||
|
||||
|
||||
class SpreadsheetCell(PropertyGroup):
|
||||
export_name : StringProperty()
|
||||
enabled : BoolProperty(default=True)
|
||||
field_name : StringProperty()
|
||||
type : EnumProperty(items=[(t, t, "") for t in ('METADATA', 'SHOT', 'ASSET_TYPE')])
|
||||
#sort : BoolProperty(default=True)
|
||||
|
||||
|
||||
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):
|
||||
class SpreadsheetImport(PropertyGroup):
|
||||
format : EnumProperty(items=[(i, i, '') for i in ('CSV', 'XLSX')])
|
||||
separator : StringProperty(default='\\n')
|
||||
delimiter : StringProperty(default=';')
|
||||
|
@ -182,6 +183,8 @@ class SpreadsheetOptions(PropertyGroup):
|
|||
|
||||
open_folder : BoolProperty(default=False)
|
||||
show_settings : BoolProperty(default=False)
|
||||
cells: CollectionProperty(type=SpreadsheetCell)
|
||||
cell_index : IntProperty(name='Spreadsheet Index', default=0)
|
||||
|
||||
|
||||
class Project(PropertyGroup):
|
||||
|
@ -222,9 +225,8 @@ 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)
|
||||
spreadsheet_import: PointerProperty(type=Spreadsheet)
|
||||
spreadsheet_export: PointerProperty(type=Spreadsheet)
|
||||
|
||||
type : StringProperty()
|
||||
|
||||
|
@ -432,7 +434,7 @@ classes = (
|
|||
Metadata,
|
||||
MetadataType,
|
||||
TaskType,
|
||||
SpreadsheetOptions,
|
||||
Spreadsheet,
|
||||
Project,
|
||||
VSETB_UL_spreadsheet,
|
||||
VSETB_UL_casting,
|
Loading…
Reference in New Issue