Export Csv

pull/5/head
Christophe SEUX 2023-04-21 21:44:05 +02:00
parent 299c240385
commit 02785f9ec0
5 changed files with 363 additions and 113 deletions

View File

@ -5,6 +5,12 @@ import importlib
import json import json
import re import re
import time import time
from pprint import pprint
import csv
from datetime import datetime
import os
import vse_toolbox import vse_toolbox
from bpy_extras.io_utils import ImportHelper from bpy_extras.io_utils import ImportHelper
@ -77,20 +83,122 @@ class VSETB_OT_tracker_connect(Operator):
return {"CANCELLED"} return {"CANCELLED"}
class VSETB_OT_export_csv(Operator): class VSETB_OT_export_spreadsheet(Operator):
bl_idname = "vse_toolbox.export_csv" bl_idname = "vse_toolbox.export_spreadsheet"
bl_label = "Set Scene" bl_label = "Export Spreadsheet"
bl_description = "Set Scene for Breakdown" bl_description = "Export Shot data in a table as a csv or an xlsl"
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
format : EnumProperty(items=[(i, i, '') for i in ('CSV', 'XLSL')])
separator : StringProperty(default='\\n')
delimiter : StringProperty(default=';')
export_path : StringProperty(default='//export')
@classmethod @classmethod
def poll(cls, context): def poll(cls, context):
return True 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
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)
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
col.prop(self, "separator", expand=True, text='Separator')
row = col.row(align=True)
row.prop(self, "format", expand=True, text='Format')
if self.format == 'CSV':
col.prop(self, "delimiter", expand=True, text='Delimiter')
col.prop(self, 'export_path')
def execute(self, context): def execute(self, context):
self.report({'ERROR'}, f'Export not implemented yet.') #self.report({'ERROR'}, f'Export not implemented yet.')
prefs = get_addon_prefs()
settings = get_scene_settings()
project = settings.active_project
episode = settings.active_episode
return {"CANCELLED"} cells = [cell for cell in project.spreadsheet if cell.enabled]
rows = []
# Header
rows.append([cell.export_name for cell in cells])
for strip in get_strips('Shots'):
row = []
for cell in cells:
if cell.is_metadata:
row += [getattr(strip.vsetb_strip_settings.metadata, cell.field_name)]
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(self.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 = self.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)
print('Writing .csv file to', export_path)
with open(str(export_path), 'w', newline='\n', encoding='utf-8') as f:
writer = csv.writer(f)
for row in rows:
writer.writerow(row)
#if show_in_explorer:
# open_file(filepath, select=True)
return {"FINISHED"}
def get_task_status_items(self, context): def get_task_status_items(self, context):
@ -121,8 +229,9 @@ class VSETB_OT_upload_to_tracker(Operator):
task : EnumProperty(items=get_task_type_items) task : EnumProperty(items=get_task_type_items)
status : EnumProperty(items=get_task_status_items) status : EnumProperty(items=get_task_status_items)
comment : StringProperty() comment : StringProperty()
add_preview : BoolProperty(default=False) add_preview : BoolProperty(default=True)
preview_mode : EnumProperty(items=[(m, m.title().replace('_', ' '), '') for m in ('ONLY_NEW', 'REPLACE', 'ADD')]) 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) casting : BoolProperty(default=True)
custom_data : BoolProperty(default=True) custom_data : BoolProperty(default=True)
@ -174,10 +283,8 @@ class VSETB_OT_upload_to_tracker(Operator):
sequence_name = get_strip_sequence_name(strip) sequence_name = get_strip_sequence_name(strip)
shot_name = strip.name shot_name = strip.name
sequence = tracker.get_sequence(sequence_name) sequence = tracker.get_sequence(sequence_name)
metadata = strip.vsetb_strip_settings.metadata.to_dict() metadata = strip.vsetb_strip_settings.metadata.to_dict()
print(metadata) #print(metadata)
if not sequence: if not sequence:
self.report({"INFO"}, f'Create sequence {sequence_name} in Kitsu') self.report({"INFO"}, f'Create sequence {sequence_name} in Kitsu')
@ -198,49 +305,33 @@ class VSETB_OT_upload_to_tracker(Operator):
preview = None preview = None
if self.add_preview: if self.add_preview:
preview = Path(get_strip_render_path(strip, project.render_template)) preview = Path(get_strip_render_path(strip, project.render_template))
#print(preview)
if not preview.exists(): if not preview.exists():
preview = None preview = None
elif task['last_comment'].get('previews'): elif task.get('last_comment') and task['last_comment']['previews']:
if self.preview_mode == 'REPLACE': if self.preview_mode == 'REPLACE':
tracker.remove_comment(task['last_comment']) tracker.remove_comment(task['last_comment'])
elif self.preview_mode == 'ONLY_NEW': elif self.preview_mode == 'ONLY_NEW':
preview = None preview = None
if status != 'CURRENT' or preview: #print(f'{preview=}')
tracker.new_comment(task, comment=self.comment, status=status, preview=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: if self.custom_data:
tracker.update_data(shot, strip.vsetb_strip_settings.metadata.to_dict()) 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"} return {"FINISHED"}
# if add_to_kitsu:
# shot_name = T['shot_tracker_name'].fields['shot'].format(data['shot'])
# seq_name = T['sequence_tracker_name'].fields['sequence'].format(data['sequence'])
# sequence = p.sequences.add(seq_name)
# shot = sequence.shots.add(shot_name)
# animatic_task = shot.tasks.add('animatic')
# animatic_task.comments.new(
# comment=f'Reel edit v{version:03d}',
# status='done',
# preview=movie_output,
# set_main_preview=True
# )
# shot_data = {
# 'fps': int(FPS),
# 'frame_in': strip.frame_final_start,
# 'frame_out': strip.frame_final_end-1,
# 'nb_frames': strip.frame_final_duration
# }
# shot.update_data(shot_data)
# return {"CANCELLED"}
class VSETB_OT_auto_select_files(Operator): class VSETB_OT_auto_select_files(Operator):
bl_idname = "import.auto_select_files" bl_idname = "import.auto_select_files"
@ -485,9 +576,10 @@ class VSETB_OT_load_projects(Operator):
episode.id = episode_data['id'] episode.id = episode_data['id']
for metadata_data in tracker.get_shots_metadata(project_data): for metadata_data in tracker.get_shots_metadata(project_data):
#print(metadata_data) pprint(metadata_data)
metadata_type = project.metadata_types.add() metadata_type = project.metadata_types.add()
metadata_type.name = metadata_data['field_name'] metadata_type.name = metadata_data['name']
metadata_type.field_name = metadata_data['field_name']
metadata_type['choices'] = metadata_data['choices'] metadata_type['choices'] = metadata_data['choices']
for status_data in tracker.get_task_statuses(project_data): for status_data in tracker.get_task_statuses(project_data):
@ -503,11 +595,13 @@ class VSETB_OT_load_projects(Operator):
asset_type = project.asset_types.add() asset_type = project.asset_types.add()
asset_type.name = asset_type_data['name'] asset_type.name = asset_type_data['name']
project.set_spreadsheet()
for project in settings.projects: for project in settings.projects:
print(project.name, project.name.replace(' ', '_').upper(), old_project_name) #print(project.name, project.name.replace(' ', '_').upper(), old_project_name)
if project.name.replace(' ', '_').upper() == old_project_name: if project.name.replace(' ', '_').upper() == old_project_name:
print('Restore Project Name') #print('Restore Project Name')
settings.project_name = project.name settings.project_name = project.name
#else: #else:
# print('Could Not restore Project Name') # print('Could Not restore Project Name')
@ -545,8 +639,8 @@ class VSETB_OT_new_episode(Operator):
episode_name = settings.episode_template.format(index=int(self.episode_name)) episode_name = settings.episode_template.format(index=int(self.episode_name))
print(self.episode_name) #print(self.episode_name)
print('episode_name: ', episode_name) #print('episode_name: ', episode_name)
episode = tracker.get_episode(episode_name) episode = tracker.get_episode(episode_name)
if episode: if episode:
@ -805,11 +899,11 @@ class VSETB_OT_casting_remove(Operator):
class VSETB_OT_casting_move(Operator): class VSETB_OT_casting_move(Operator):
bl_idname = "vse_toolbox.casting_move" bl_idname = "vse_toolbox.casting_move"
bl_label = "Casting Actions" bl_label = "Move Casting items"
bl_description = "Actions to Add, Remove, Move casting items" bl_description = "Move Casting items"
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
action: EnumProperty( direction: EnumProperty(
items=( items=(
('UP', "Up", ""), ('UP', "Up", ""),
('DOWN', "Down", ""), ('DOWN', "Down", ""),
@ -824,7 +918,7 @@ class VSETB_OT_casting_move(Operator):
if active_strip: if active_strip:
return True return True
def invoke(self, context, event): def execute(self, context):
scn = context.scene scn = context.scene
strip_settings = get_strip_settings() strip_settings = get_strip_settings()
@ -835,12 +929,12 @@ class VSETB_OT_casting_move(Operator):
except IndexError: except IndexError:
pass pass
else: else:
if self.action == 'DOWN' and idx < len(strip_settings.casting) - 1: if self.direction == 'DOWN' and idx < len(strip_settings.casting) - 1:
item_next = strip_settings.casting[idx+1].name item_next = strip_settings.casting[idx+1].name
strip_settings.casting.move(idx, idx+1) strip_settings.casting.move(idx, idx+1)
strip_settings.casting_index += 1 strip_settings.casting_index += 1
elif self.action == 'UP' and idx >= 1: elif self.direction == 'UP' and idx >= 1:
item_prev = strip_settings.casting[idx-1].name item_prev = strip_settings.casting[idx-1].name
strip_settings.casting.move(idx, idx-1) strip_settings.casting.move(idx, idx-1)
strip_settings.casting_index -= 1 strip_settings.casting_index -= 1
@ -851,9 +945,45 @@ class VSETB_OT_casting_move(Operator):
return {"FINISHED"} return {"FINISHED"}
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_copy_casting(Operator): class VSETB_OT_copy_casting(Operator):
bl_idname = "vse_toolbox.copy_casting" bl_idname = "vse_toolbox.copy_casting"
bl_label = "Casting Actions" bl_label = "Copy Casting"
bl_description = "Copy Casting from active strip" bl_description = "Copy Casting from active strip"
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
@ -879,8 +1009,8 @@ class VSETB_OT_copy_casting(Operator):
class VSETB_OT_paste_casting(Operator): class VSETB_OT_paste_casting(Operator):
bl_idname = "vse_toolbox.paste_casting" bl_idname = "vse_toolbox.paste_casting"
bl_label = "Casting Actions" bl_label = "Paste Casting"
bl_description = "Copy Casting from active strip" bl_description = "Paste Casting to active strip"
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
@classmethod @classmethod
@ -932,50 +1062,37 @@ class VSETB_OT_set_stamps(Operator):
bpy.ops.sequencer.select_all(action='DESELECT') bpy.ops.sequencer.select_all(action='DESELECT')
crop_x = int(scn.render.resolution_x * 0.33) crop_x = int(scn.render.resolution_x * 0.4)
crop_y = int(scn.render.resolution_y * 0.95) 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 Name
project_strip_stamp = new_text_strip( project_strip_stamp = new_text_strip('project_name_stamp', channel=1, **stamp_params,
'project_name_stamp', channel=1, start=scn.frame_start, end=scn.frame_end, text=settings.active_project.name, x=0.01, align_x='LEFT', align_y='BOTTOM')
text=settings.active_project.name, font_size=24,
x=0.01, y=0.01, align_x='LEFT', align_y='BOTTOM', select=True,
box_color=(0, 0, 0, 0.5), box_margin=0.005,
)
project_strip_stamp.crop.max_x = crop_x * 2 project_strip_stamp.crop.max_x = crop_x * 2
project_strip_stamp.crop.max_y = crop_y project_strip_stamp.crop.max_y = crop_max_y
project_strip_stamp.crop.min_y = crop_min_y
# Shot Name # Shot Name
shot_strip_stamp = new_text_strip( shot_strip_stamp = new_text_strip('shot_name_stamp', channel=2, **stamp_params,
f'shot_name_stamp', channel=2, start=scn.frame_start, end=scn.frame_end, text='{active_shot_name}', align_y='BOTTOM')
text='{active_shot_name}', font_size=24,
y=0.01, align_y='BOTTOM', select=True,
box_color=(0, 0, 0, 0.5), box_margin=0.005,
)
shot_strip_stamp.crop.min_x = crop_x shot_strip_stamp.crop.min_x = crop_x
shot_strip_stamp.crop.max_x = crop_x shot_strip_stamp.crop.max_x = crop_x
shot_strip_stamp.crop.max_y = crop_y shot_strip_stamp.crop.max_y = crop_max_y
shot_strip_stamp.crop.min_y = crop_min_y
# Frame Range # Frame Range
frame_strip_stamp = new_text_strip( frame_strip_stamp = new_text_strip('frame_range_stamp', channel=3, **stamp_params,
'frame_range_stamp', channel=3, start=scn.frame_start, end=scn.frame_end, text='{active_shot_frame} / {active_shot_duration}', x=0.99, align_x='RIGHT', align_y='BOTTOM')
text='{active_shot_frame} / {active_shot_duration}', font_size=24,
x=0.99, y=0.01, align_x='RIGHT', align_y='BOTTOM', select=True,
box_color=(0, 0, 0, 0.5), box_margin=0.005,
)
frame_strip_stamp.crop.min_x = int(scn.render.resolution_x * 0.66) frame_strip_stamp.crop.min_x = crop_x *2
frame_strip_stamp.crop.max_y = crop_y frame_strip_stamp.crop.max_y = crop_max_y
# for shot_strip in get_strips('Shots'): frame_strip_stamp.crop.min_y = crop_min_y
# # Shot Name
# shot_strip_stamp = new_text_strip(
# f'{shot_strip.name}_stamp', channel=3, start=shot_strip.frame_final_start, end=shot_strip.frame_final_end,
# text=shot_strip.name, font_size=24,
# y=0.01, align_y='BOTTOM', select=True,
# box_color=(0, 0, 0, 0.35), box_margin=0.005,
# )
bpy.ops.sequencer.meta_make() bpy.ops.sequencer.meta_make()
stamps_strip = context.active_sequence_strip stamps_strip = context.active_sequence_strip
@ -994,9 +1111,10 @@ classes = (
VSETB_OT_casting_add, VSETB_OT_casting_add,
VSETB_OT_casting_remove, VSETB_OT_casting_remove,
VSETB_OT_casting_move, VSETB_OT_casting_move,
VSETB_OT_spreadsheet_move,
VSETB_OT_copy_casting, VSETB_OT_copy_casting,
VSETB_OT_paste_casting, VSETB_OT_paste_casting,
VSETB_OT_export_csv, VSETB_OT_export_spreadsheet,
VSETB_OT_import_files, VSETB_OT_import_files,
VSETB_OT_load_assets, VSETB_OT_load_assets,
VSETB_OT_load_projects, VSETB_OT_load_projects,

View File

@ -170,7 +170,7 @@ class VSETB_PT_exports(VSETB_main, Panel):
bl_options = {'DEFAULT_CLOSED'} bl_options = {'DEFAULT_CLOSED'}
def draw_header_preset(self, context): def draw_header_preset(self, context):
self.layout.operator('vse_toolbox.export_csv', icon='EXPORT', text='', emboss=False) self.layout.operator('vse_toolbox.export_spreadsheet', icon='EXPORT', text='', emboss=False)
def draw(self, context): def draw(self, context):
prefs = get_addon_prefs() prefs = get_addon_prefs()
@ -183,7 +183,7 @@ class VSETB_PT_exports(VSETB_main, Panel):
tracker_label = settings.tracker_name.title().replace('_', ' ') tracker_label = settings.tracker_name.title().replace('_', ' ')
layout.operator('vse_toolbox.upload_to_tracker', text=f'Upload to {tracker_label}', icon='EXPORT') layout.operator('vse_toolbox.upload_to_tracker', text=f'Upload to {tracker_label}', icon='EXPORT')
layout.operator('vse_toolbox.export_csv', text='Export csv', icon='SPREADSHEET') layout.operator('vse_toolbox.export_spreadsheet', text='Export Spreadsheet', icon='SPREADSHEET')
class VSETB_PT_casting(VSETB_main, Panel): class VSETB_PT_casting(VSETB_main, Panel):
@ -232,8 +232,8 @@ class VSETB_PT_casting(VSETB_main, Panel):
col_tool.operator('vse_toolbox.casting_add', icon='ADD', text="") col_tool.operator('vse_toolbox.casting_add', icon='ADD', text="")
col_tool.operator('vse_toolbox.casting_remove', icon='REMOVE', text="") col_tool.operator('vse_toolbox.casting_remove', icon='REMOVE', text="")
col_tool.separator() col_tool.separator()
col_tool.operator('vse_toolbox.casting_move', icon='TRIA_UP', text="").action = 'UP' col_tool.operator('vse_toolbox.casting_move', icon='TRIA_UP', text="").direction = 'UP'
col_tool.operator('vse_toolbox.casting_move', icon='TRIA_DOWN', text="").action = 'DOWN' col_tool.operator('vse_toolbox.casting_move', icon='TRIA_DOWN', text="").direction = 'DOWN'
col_tool.separator() col_tool.separator()
col_tool.operator('vse_toolbox.copy_casting', icon='COPYDOWN', text="") col_tool.operator('vse_toolbox.copy_casting', icon='COPYDOWN', text="")
col_tool.operator('vse_toolbox.paste_casting', icon='PASTEDOWN', text="") col_tool.operator('vse_toolbox.paste_casting', icon='PASTEDOWN', text="")
@ -260,6 +260,8 @@ class VSETB_PT_metadata(VSETB_main, Panel):
if not project: if not project:
return return
layout.prop(strip_settings, 'description', text='DESCRIPTION')
#col = layout.column() #col = layout.column()
for key in strip_settings.metadata.__annotations__.keys(): for key in strip_settings.metadata.__annotations__.keys():
layout.prop(strip_settings.metadata, key, text=key.upper()) layout.prop(strip_settings.metadata, key, text=key.upper())

View File

@ -57,6 +57,28 @@ def get_tracker_items(self, context):
return [(norm_str(a.__name__, format=str.upper), a.__name__, "", i) for i, a in enumerate(TRACKERS)] return [(norm_str(a.__name__, format=str.upper), a.__name__, "", i) for i, a in enumerate(TRACKERS)]
class CollectionPropertyGroup(PropertyGroup):
def __iter__(self):
return (v for v in self.values())
def props(self):
return [p for p in self.bl_rna.properties if p.identifier not in ('rna_type', 'name')]
def keys(self):
return [k for k in self.bl_rna.properties.keys() if k not in ('rna_type', 'name')]
def values(self):
return [getattr(self, k) for k in self.keys()]
def items(self):
return self.to_dict().items()
def to_dict(self, use_name=True):
if use_name:
return {p.name: getattr(self, p.identifier) for p in self.props()}
else:
return {k: getattr(self, k) for k in self.keys()}
class Asset(PropertyGroup): class Asset(PropertyGroup):
name : StringProperty(default='') name : StringProperty(default='')
id : StringProperty(default='') id : StringProperty(default='')
@ -80,17 +102,19 @@ class AssetCasting(PropertyGroup):
id : StringProperty(default='') id : StringProperty(default='')
instance : IntProperty(default=1) instance : IntProperty(default=1)
def __iter__(self):
return (getattr(self, p) for p in self.bl_rna.properties.keys() if p not in ('rna_type', 'name'))
@property @property
def asset(self): def asset(self):
settings = get_scene_settings() settings = get_scene_settings()
project = settings.active_project project = settings.active_project
return project.assets.get(self.id) return project.assets.get(self.id)
def to_dict(self):
return {k: v for k,v in self.items()} class SpreadsheetCell(PropertyGroup):
export_name : StringProperty()
enabled : BoolProperty(default=True)
is_metadata : BoolProperty(default=False)
field_name : StringProperty()
#type : EnumProperty(items=[(t, t, "")] for t in ('METADATA', 'STRIP_DATA'))
class AssetType(PropertyGroup): class AssetType(PropertyGroup):
@ -100,6 +124,7 @@ class AssetType(PropertyGroup):
class MetadataType(PropertyGroup): class MetadataType(PropertyGroup):
choices = [] choices = []
choice : EnumProperty(items=lambda s, c: [(c, c.replace(' ', '_').upper(), '') for c in s['choices']]) choice : EnumProperty(items=lambda s, c: [(c, c.replace(' ', '_').upper(), '') for c in s['choices']])
field_name : bpy.props.StringProperty()
class TaskType(PropertyGroup): class TaskType(PropertyGroup):
@ -110,11 +135,9 @@ class TaskStatus(PropertyGroup):
__annotations__ = {} __annotations__ = {}
class Metadata(PropertyGroup): class Metadata(CollectionPropertyGroup):
__annotations__ = {} __annotations__ = {}
def to_dict(self):
return {p.name: getattr(self, p.identifier) for p in self.bl_rna.properties.keys() if p.name != 'RNA'}
class Episode(PropertyGroup): class Episode(PropertyGroup):
id : StringProperty(default='') id : StringProperty(default='')
@ -157,8 +180,30 @@ class Project(PropertyGroup):
task_types : CollectionProperty(type=TaskType) task_types : CollectionProperty(type=TaskType)
task_statuses : CollectionProperty(type=TaskStatus) task_statuses : CollectionProperty(type=TaskStatus)
spreadsheet : CollectionProperty(type=SpreadsheetCell)
spreadsheet_index : IntProperty(name='Spreadsheet Index', default=0)
type : StringProperty() type : StringProperty()
def set_spreadsheet(self):
cell_names = ['Sequence', 'Shot', 'Frames', 'Description']
if self.type == 'TVSHOW':
cell_names.insert(0, 'Episode')
for cell_name in cell_names:
cell = self.spreadsheet.add()
cell.name = cell_name
cell.export_name = cell_name
cell.field_name = cell_name.upper()
for metadata_type in self.metadata_types:
cell = self.spreadsheet.add()
cell.name = metadata_type.name
cell.export_name = metadata_type.name
cell.field_name = metadata_type.field_name
cell.is_metadata = True
def set_strip_metadata(self): def set_strip_metadata(self):
# Clear Metadatas # Clear Metadatas
@ -167,16 +212,17 @@ class Project(PropertyGroup):
delattr(Metadata, attr) delattr(Metadata, attr)
del Metadata.__annotations__[attr] del Metadata.__annotations__[attr]
for metadata_type in self.metadata_types: for metadata_type in self.metadata_types:
prop_name = metadata_type.name field_name = metadata_type.field_name
if metadata_type.get('choices'): name = metadata_type.name
prop = bpy.props.EnumProperty(items=[(c, c.replace(' ', '_').upper(), '') for c in ['/'] + metadata_type['choices']])
else:
prop = bpy.props.StringProperty()
Metadata.__annotations__[prop_name] = prop if metadata_type.get('choices'):
setattr(Metadata, prop_name, prop) prop = bpy.props.EnumProperty(items=[(c, c, '') for c in ['/'] + metadata_type['choices']], name=name)
else:
prop = bpy.props.StringProperty(name=name)
Metadata.__annotations__[field_name] = prop
setattr(Metadata, field_name, prop)
class VSETB_UL_casting(UIList): class VSETB_UL_casting(UIList):
@ -242,6 +288,34 @@ class VSETB_UL_casting(UIList):
return filtered, ordered return filtered, ordered
class VSETB_UL_spreadsheet(UIList):
def draw_item(self, context, layout, data, item, icon, active_data,
active_propname, index):
settings = get_scene_settings()
project = settings.active_project
layout.use_property_split = True
layout.use_property_decorate = False
row = layout.row(align=True)
row.alignment = 'LEFT'
row.prop(item, 'enabled', text='')
layout.label(text=item.name)
layout.prop(item, 'export_name', text='')
def draw_filter(self, context, layout):
row = layout.row()
subrow = row.row(align=True)
subrow.prop(self, "filter_name", text="")
subrow.prop(self, "use_filter_invert", text="", icon='ARROW_LEFTRIGHT')
subrow.separator()
subrow.prop(self, "order_by_type", text="Order by Type", icon='MESH_DATA')
class VSETB_PGT_scene_settings(PropertyGroup): class VSETB_PGT_scene_settings(PropertyGroup):
projects : CollectionProperty(type=Project) projects : CollectionProperty(type=Project)
@ -287,11 +361,13 @@ class VSETB_PGT_strip_settings(PropertyGroup):
casting_index : IntProperty(name='Casting Index', default=0) casting_index : IntProperty(name='Casting Index', default=0)
source_name : StringProperty(name='') source_name : StringProperty(name='')
metadata : PointerProperty(type=Metadata) metadata : PointerProperty(type=Metadata)
description : StringProperty()
classes=( classes=(
Asset, Asset,
AssetCasting, AssetCasting,
SpreadsheetCell,
AssetType, AssetType,
TaskStatus, TaskStatus,
Episode, Episode,
@ -299,6 +375,7 @@ classes=(
MetadataType, MetadataType,
TaskType, TaskType,
Project, Project,
VSETB_UL_spreadsheet,
VSETB_UL_casting, VSETB_UL_casting,
VSETB_PGT_scene_settings, VSETB_PGT_scene_settings,
VSETB_PGT_strip_settings, VSETB_PGT_strip_settings,
@ -313,6 +390,7 @@ def load_handler(dummy):
settings = get_scene_settings() settings = get_scene_settings()
if settings.active_project: if settings.active_project:
settings.active_project.set_strip_metadata() settings.active_project.set_strip_metadata()
#settings.active_project.set_spreadsheet()
os.environ['TRACKER_PROJECT_ID'] = settings.active_project.id os.environ['TRACKER_PROJECT_ID'] = settings.active_project.id

View File

@ -6,6 +6,7 @@ import urllib3
import traceback import traceback
import time import time
import uuid import uuid
from pprint import pprint
from bpy.props import PointerProperty, StringProperty from bpy.props import PointerProperty, StringProperty
from pathlib import Path from pathlib import Path
@ -176,6 +177,20 @@ class Kitsu(Tracker):
return gazu.shot.get_shot_by_name(sequence, shot) return gazu.shot.get_shot_by_name(sequence, shot)
def get_asset(self, asset, asset_type=None, project=None):
#print('get_asset', "name", name, 'asset_type', asset_type)
asset_id = self.get_id(asset)
if asset_id:
return asset_id
project = self.get_project(project)
asset_type = self.get_id(asset_type)
asset = gazu.asset.get_asset_by_name(project, asset, asset_type)
return asset
def get_assets(self, project=None): def get_assets(self, project=None):
project = self.get_project(project) project = self.get_project(project)
assets = gazu.asset.all_assets_for_project(project) assets = gazu.asset.all_assets_for_project(project)
@ -327,10 +342,41 @@ class Kitsu(Tracker):
else: else:
entity['data'].update(data) entity['data'].update(data)
#pprint(entity)
entity_data = gazu.client.put(f"data/entities/{entity_id}", entity) entity_data = gazu.client.put(f"data/entities/{entity_id}", entity)
return entity_data['data'] return entity_data['data']
def get_casting(self, shot, project=None):
project = self.get_project(project)
project_id = self.get_id(project)
return gazu.casting.get_shot_casting({'id': self.get_id(shot), 'project_id': project_id})
def update_casting(self, shot, casting, clear=True, project=None):
project = self.get_project(project)
shot_id = self.get_id(shot)
norm_casting = []
if clear is False:
norm_casting += self.get_casting(shot, project)
for asset in casting:
if isinstance(asset, dict) and 'asset_id' in asset: # It's an asset instance
asset_id = asset['asset_id']
nb_occurences = asset['nb_occurences']
else: # It's an asset
asset = self.get_asset(asset)
nb_occurences = 1
cast = next((c for c in norm_casting if c['asset_id'] == asset_id), None)
if cast:
cast['nb_occurences'] += 1
else:
norm_casting.append({'asset_id': asset_id, 'nb_occurences': nb_occurences})
return gazu.casting.update_shot_casting(project, shot_id, norm_casting)
def draw_prefs(self, layout): def draw_prefs(self, layout):
layout.prop(self, 'url', text='Url') layout.prop(self, 'url', text='Url')
layout.prop(self, 'login', text='Login') layout.prop(self, 'login', text='Login')

View File

@ -2,6 +2,7 @@
import re import re
from pathlib import Path from pathlib import Path
import os
import bpy import bpy
from bpy.app.handlers import persistent from bpy.app.handlers import persistent
@ -125,12 +126,15 @@ def set_channels():
def get_strip_render_path(strip, template): def get_strip_render_path(strip, template):
scn = bpy.context.scene scn = bpy.context.scene
return template.format(strip_name=strip.name, ext=Path(scn.render.frame_path()).suffix) suffix = Path(scn.render.frame_path()).suffix
render_path = template.format(strip_name=strip.name, ext=suffix[1:])
return Path(os.path.abspath(bpy.path.abspath(render_path)))
def render_strips(strips, template): def render_strips(strips, template):
scn = bpy.context.scene scn = bpy.context.scene
scene_start = scn.frame_start scene_start = scn.frame_start
scene_end = scn.frame_end scene_end = scn.frame_end
render_path = scn.render.filepath
for strip in strips: for strip in strips:
#print(render_template, strip.name, path) #print(render_template, strip.name, path)
@ -140,7 +144,8 @@ def render_strips(strips, template):
## render animatic ## render animatic
#strip_render_path = render_template.format(strip_name=strip.name, ext=Path(scn.render.frame_path()).suffix) #strip_render_path = render_template.format(strip_name=strip.name, ext=Path(scn.render.frame_path()).suffix)
scn.render.filepath = get_strip_render_path(strip, template)
scn.render.filepath = str(get_strip_render_path(strip, template))
#print(scn.render.filepath) #print(scn.render.filepath)
print(f'Render Strip to {scn.render.filepath}') print(f'Render Strip to {scn.render.filepath}')
#bpy.ops.render.render(animation=True) #bpy.ops.render.render(animation=True)
@ -149,6 +154,7 @@ def render_strips(strips, template):
scn.frame_start = scene_start scn.frame_start = scene_start
scn.frame_end = scene_end scn.frame_end = scene_end
scn.render.filepath = render_path
def import_edit(filepath, adapter="cmx_3600", clean_sequencer=False): def import_edit(filepath, adapter="cmx_3600", clean_sequencer=False):
import opentimelineio as otio import opentimelineio as otio