319 lines
11 KiB
Python
319 lines
11 KiB
Python
|
|
||
|
# 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)
|