upload
parent
93134f25a9
commit
d2caf90ea2
|
@ -23,6 +23,8 @@ SOUND_SUFFIXES = ['.mp3', '.aaf', '.flac', '.wav']
|
|||
CONFIG_DIR = Path(appdirs.user_config_dir(__package__.split('.')[0]))
|
||||
PREVIEWS_DIR = CONFIG_DIR / 'thumbnails'
|
||||
|
||||
TASK_ITEMS = []
|
||||
|
||||
ASSET_PREVIEWS = bpy.utils.previews.new()
|
||||
|
||||
CASTING_BUFFER = CONFIG_DIR / 'casting.json'
|
|
@ -4,6 +4,7 @@ import bpy
|
|||
import importlib
|
||||
import json
|
||||
import re
|
||||
import time
|
||||
import vse_toolbox
|
||||
|
||||
from bpy_extras.io_utils import ImportHelper
|
||||
|
@ -29,6 +30,7 @@ from vse_toolbox.constants import (
|
|||
PREVIEWS_DIR,
|
||||
SOUNDS,
|
||||
SOUND_SUFFIXES,
|
||||
TASK_ITEMS
|
||||
)
|
||||
from vse_toolbox.sequencer_utils import (
|
||||
clean_sequencer,
|
||||
|
@ -41,8 +43,11 @@ from vse_toolbox.sequencer_utils import (
|
|||
render_strips,
|
||||
set_channels,
|
||||
get_channel_index,
|
||||
new_text_strip
|
||||
new_text_strip,
|
||||
get_strip_render_path,
|
||||
get_strip_sequence_name
|
||||
)
|
||||
|
||||
from vse_toolbox.bl_utils import get_addon_prefs, get_scene_settings, get_strip_settings
|
||||
from vse_toolbox.file_utils import install_module, norm_name, norm_str
|
||||
|
||||
|
@ -88,6 +93,133 @@ class VSETB_OT_export_csv(Operator):
|
|||
return {"CANCELLED"}
|
||||
|
||||
|
||||
def get_task_status_items(self, context):
|
||||
settings = get_scene_settings()
|
||||
project = settings.active_project
|
||||
|
||||
status_items = [('CURRENT', 'Current', '')]
|
||||
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
|
||||
|
||||
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=False)
|
||||
preview_mode : EnumProperty(items=[(m, m.title().replace('_', ' '), '') for m in ('ONLY_NEW', 'REPLACE', 'ADD')])
|
||||
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
|
||||
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)
|
||||
|
||||
if not sequence:
|
||||
self.report({"INFO"}, f'Create sequence {sequence_name} in Kitsu')
|
||||
sequence = tracker.new_sequence(sequence_name)
|
||||
|
||||
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)
|
||||
|
||||
print(task)
|
||||
tracker.new_comment(task, comment=self.comment, status=status)
|
||||
#status = tracker.get_task(shot, self.task)
|
||||
# if self.add_preview:
|
||||
# strip_movie_path = Path(get_strip_render_path(strip, template))
|
||||
# if path.exists():
|
||||
|
||||
# else:
|
||||
# self.report({"WARNING"}, f"{strip_movie_path} not exist, skipped")
|
||||
|
||||
|
||||
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):
|
||||
bl_idname = "import.auto_select_files"
|
||||
bl_label = "Auto Select"
|
||||
|
@ -317,8 +449,6 @@ class VSETB_OT_load_projects(Operator):
|
|||
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']
|
||||
|
@ -335,6 +465,15 @@ class VSETB_OT_load_projects(Operator):
|
|||
metadata_type = project.metadata_types.add()
|
||||
metadata_type.name = metadata_data['field_name']
|
||||
metadata_type['choices'] = metadata_data['choices']
|
||||
|
||||
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()
|
||||
|
@ -465,11 +604,12 @@ class VSETB_OT_render(Operator):
|
|||
bl_description = "Render Shots Strips"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
selected_only : BoolProperty(name="Selected Only", default=False)
|
||||
#selected_only : BoolProperty(name="Selected Only", default=False)
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return True
|
||||
settings = get_scene_settings()
|
||||
return settings.active_project
|
||||
|
||||
def invoke(self, context, event):
|
||||
scn = context.scene
|
||||
|
@ -485,15 +625,20 @@ class VSETB_OT_render(Operator):
|
|||
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, '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=settings.channel, selected_only=self.selected_only)
|
||||
strips = get_strips(channel='Shots', selected_only=True)
|
||||
|
||||
render_strips(strips)
|
||||
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"}
|
||||
|
||||
|
@ -522,6 +667,16 @@ class VSETB_OT_set_sequencer(Operator):
|
|||
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"}
|
||||
|
||||
|
||||
|
@ -744,6 +899,9 @@ class VSETB_OT_set_stamps(Operator):
|
|||
|
||||
bpy.ops.sequencer.select_all(action='DESELECT')
|
||||
|
||||
crop_x = int(scn.render.resolution_x * 0.33)
|
||||
crop_y = int(scn.render.resolution_y * 0.95)
|
||||
|
||||
# Project Name
|
||||
project_strip_stamp = new_text_strip(
|
||||
'project_name_stamp', channel=1, start=scn.frame_start, end=scn.frame_end,
|
||||
|
@ -752,6 +910,9 @@ class VSETB_OT_set_stamps(Operator):
|
|||
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_y = crop_y
|
||||
|
||||
# Shot Name
|
||||
shot_strip_stamp = new_text_strip(
|
||||
f'shot_name_stamp', channel=2, start=scn.frame_start, end=scn.frame_end,
|
||||
|
@ -760,6 +921,10 @@ class VSETB_OT_set_stamps(Operator):
|
|||
box_color=(0, 0, 0, 0.5), box_margin=0.005,
|
||||
)
|
||||
|
||||
shot_strip_stamp.crop.min_x = crop_x
|
||||
shot_strip_stamp.crop.max_x = crop_x
|
||||
shot_strip_stamp.crop.max_y = crop_y
|
||||
|
||||
# Frame Range
|
||||
frame_strip_stamp = new_text_strip(
|
||||
'frame_range_stamp', channel=3, start=scn.frame_start, end=scn.frame_end,
|
||||
|
@ -768,6 +933,8 @@ class VSETB_OT_set_stamps(Operator):
|
|||
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.max_y = crop_y
|
||||
# for shot_strip in get_strips('Shots'):
|
||||
# # Shot Name
|
||||
# shot_strip_stamp = new_text_strip(
|
||||
|
@ -806,7 +973,8 @@ classes = (
|
|||
VSETB_OT_render,
|
||||
VSETB_OT_set_sequencer,
|
||||
VSETB_OT_tracker_connect,
|
||||
VSETB_OT_set_stamps
|
||||
VSETB_OT_set_stamps,
|
||||
VSETB_OT_upload_to_tracker
|
||||
)
|
||||
|
||||
def register():
|
||||
|
|
55
panels.py
55
panels.py
|
@ -86,9 +86,13 @@ class VSETB_PT_sequencer(VSETB_main, Panel):
|
|||
col.operator('vse_toolbox.set_sequencer', text='Set-Up Sequencer', icon='SEQ_SEQUENCER')
|
||||
|
||||
#row = col.row()
|
||||
episode = project.episode_name
|
||||
sequence_label = project.sequence_template.format(episode=episode, index=project.sequence_start_number)
|
||||
shot_label = project.shot_template.format(episode=episode, sequence=sequence_label, index=project.shot_start_number)
|
||||
shot_label = ''
|
||||
sequence_label = ''
|
||||
|
||||
if project:
|
||||
episode = project.episode_name
|
||||
sequence_label = project.sequence_template.format(episode=episode, index=project.sequence_start_number)
|
||||
shot_label = project.shot_template.format(episode=episode, sequence=sequence_label, index=project.shot_start_number)
|
||||
|
||||
#row.separator()
|
||||
|
||||
|
@ -106,19 +110,41 @@ class VSETB_PT_sequencer(VSETB_main, Panel):
|
|||
row.enabled = False
|
||||
|
||||
op = row.operator('vse_toolbox.strips_rename', text=f'Rename {channel_name} ( {label} )', icon='SORTALPHA')
|
||||
op.channel_name = channel_name
|
||||
if channel_name == 'Shots':
|
||||
op.start_number = project.shot_start_number
|
||||
op.template = project.shot_template
|
||||
op.increment = project.shot_increment
|
||||
else:
|
||||
op.start_number = project.sequence_start_number
|
||||
op.template = project.sequence_template
|
||||
op.increment =project.sequence_increment
|
||||
if project:
|
||||
op.channel_name = channel_name
|
||||
if channel_name == 'Shots':
|
||||
op.start_number = project.shot_start_number
|
||||
op.template = project.shot_template
|
||||
op.increment = project.shot_increment
|
||||
else:
|
||||
op.start_number = project.sequence_start_number
|
||||
op.template = project.sequence_template
|
||||
op.increment = project.sequence_increment
|
||||
|
||||
col.operator('vse_toolbox.set_stamps', text='Set Stamps', icon='COLOR')
|
||||
|
||||
|
||||
class VSETB_PT_settings(VSETB_main, Panel):
|
||||
bl_label = "Settings"
|
||||
bl_parent_id = "VSETB_PT_main"
|
||||
bl_options = {'DEFAULT_CLOSED'}
|
||||
|
||||
#def draw_header_preset(self, context):
|
||||
# self.layout.operator('vse_toolbox.import_files', icon='IMPORT', text='', emboss=False)
|
||||
|
||||
def draw(self, context):
|
||||
prefs = get_addon_prefs()
|
||||
layout = self.layout
|
||||
settings = get_scene_settings()
|
||||
project = settings.active_project
|
||||
|
||||
col = layout.column()
|
||||
#row = col.row(align=True)
|
||||
col.prop(project, 'sequence_template')
|
||||
col.prop(project, 'shot_template')
|
||||
col.prop(project, 'render_template')
|
||||
|
||||
|
||||
class VSETB_PT_imports(VSETB_main, Panel):
|
||||
bl_label = "Imports"
|
||||
bl_parent_id = "VSETB_PT_main"
|
||||
|
@ -154,7 +180,10 @@ class VSETB_PT_exports(VSETB_main, Panel):
|
|||
|
||||
# TODO FAIRE DES VRAIS OPS
|
||||
layout.operator('vse_toolbox.strips_render', text='Render Strips', icon='SEQUENCE')
|
||||
layout.operator('vse_toolbox.export_csv', text='Export', icon='EXPORT')
|
||||
|
||||
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.export_csv', text='Export csv', icon='SPREADSHEET')
|
||||
|
||||
|
||||
class VSETB_PT_casting(VSETB_main, Panel):
|
||||
|
|
|
@ -48,16 +48,18 @@ def load_trackers():
|
|||
if not Tracker in obj.__mro__:
|
||||
continue
|
||||
|
||||
if obj is Tracker or obj.name in (a.name for a in TRACKERS):
|
||||
if obj is Tracker or name in (a.__name__ for a in TRACKERS):
|
||||
continue
|
||||
|
||||
try:
|
||||
print(f'Register Plugin {name}')
|
||||
print(f'Register Tracker {name}')
|
||||
bpy.utils.register_class(obj)
|
||||
setattr(Trackers, norm_str(obj.name), PointerProperty(type=obj))
|
||||
#obj.register()
|
||||
|
||||
setattr(Trackers, norm_str(name), PointerProperty(type=obj))
|
||||
TRACKERS.append(obj)
|
||||
except Exception as e:
|
||||
print(f'Could not register library_type {name}')
|
||||
print(f'Could not register Tracker {name}')
|
||||
print(e)
|
||||
|
||||
def load_tracker_prefs():
|
||||
|
|
|
@ -41,15 +41,20 @@ def on_project_updated(self, context):
|
|||
settings = get_scene_settings()
|
||||
settings['episodes'] = 0
|
||||
|
||||
print('Update active Project')
|
||||
#print('Update active Project')
|
||||
|
||||
bpy.ops.vse_toolbox.load_assets()
|
||||
|
||||
if settings.active_project:
|
||||
settings.active_project.set_strip_metadata()
|
||||
|
||||
os.environ['TRACKER_PROJECT_ID'] = settings.active_project.id
|
||||
|
||||
def on_episode_updated(self, context):
|
||||
os.environ['TRACKER_EPISODE_ID'] = settings.active_episode.id
|
||||
|
||||
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 Asset(PropertyGroup):
|
||||
|
@ -97,6 +102,14 @@ class MetadataType(PropertyGroup):
|
|||
choice : EnumProperty(items=lambda s, c: [(c, c.replace(' ', '_').upper(), '') for c in s['choices']])
|
||||
|
||||
|
||||
class TaskType(PropertyGroup):
|
||||
__annotations__ = {}
|
||||
|
||||
|
||||
class TaskStatus(PropertyGroup):
|
||||
__annotations__ = {}
|
||||
|
||||
|
||||
class Metadata(PropertyGroup):
|
||||
__annotations__ = {}
|
||||
|
||||
|
@ -131,11 +144,17 @@ class Project(PropertyGroup):
|
|||
shot_template : StringProperty(
|
||||
name="Shot Name", default="{sequence}_sh{index:04d}")
|
||||
|
||||
episode_name : EnumProperty(items=get_episodes_items)
|
||||
render_template : StringProperty(
|
||||
name="Render Name", default="//render/{strip_name}.{ext}")
|
||||
|
||||
episode_name : EnumProperty(items=get_episodes_items, update=on_episode_updated)
|
||||
episodes : CollectionProperty(type=Episode)
|
||||
assets : CollectionProperty(type=Asset)
|
||||
asset_types : CollectionProperty(type=AssetType)
|
||||
metadata_types : CollectionProperty(type=MetadataType)
|
||||
task_types : CollectionProperty(type=TaskType)
|
||||
task_statuses : CollectionProperty(type=TaskStatus)
|
||||
|
||||
type : StringProperty()
|
||||
|
||||
def set_strip_metadata(self):
|
||||
|
@ -272,9 +291,11 @@ classes=(
|
|||
Asset,
|
||||
AssetCasting,
|
||||
AssetType,
|
||||
TaskStatus,
|
||||
Episode,
|
||||
Metadata,
|
||||
MetadataType,
|
||||
TaskType,
|
||||
Project,
|
||||
VSETB_UL_casting,
|
||||
VSETB_PGT_scene_settings,
|
||||
|
@ -290,6 +311,7 @@ def load_handler(dummy):
|
|||
settings = get_scene_settings()
|
||||
if settings.active_project:
|
||||
settings.active_project.set_strip_metadata()
|
||||
os.environ['TRACKER_PROJECT_ID'] = settings.active_project.id
|
||||
|
||||
|
||||
def register():
|
||||
|
|
|
@ -5,10 +5,10 @@ import re
|
|||
import urllib3
|
||||
import traceback
|
||||
import time
|
||||
import uuid
|
||||
|
||||
from bpy.props import PointerProperty, StringProperty
|
||||
from pathlib import Path
|
||||
from vse_toolbox.bl_utils import get_addon_prefs, get_scene_settings
|
||||
from vse_toolbox.file_utils import install_module, norm_str
|
||||
from vse_toolbox.resources.trackers.tracker import Tracker
|
||||
|
||||
|
@ -18,34 +18,163 @@ except Exception as e:
|
|||
print('Could not install gazu')
|
||||
print(e)
|
||||
|
||||
LOGIN = None
|
||||
|
||||
class Kitsu(Tracker):
|
||||
name = "Kitsu"
|
||||
|
||||
|
||||
url: StringProperty()
|
||||
login: StringProperty()
|
||||
password: StringProperty(subtype='PASSWORD')
|
||||
|
||||
project_name = None
|
||||
def connect(self, url=None, login=None, password=None):
|
||||
'''Connect to kitsu api using provided url, login and password'''
|
||||
|
||||
global LOGIN
|
||||
|
||||
urllib3.disable_warnings()
|
||||
|
||||
if url is None:
|
||||
url = self.url
|
||||
if login is None:
|
||||
login = self.login
|
||||
if password is None:
|
||||
password = self.password
|
||||
|
||||
if not url:
|
||||
print(f'Kitsu Url: {self.url} is empty')
|
||||
return
|
||||
|
||||
if login == LOGIN:
|
||||
return
|
||||
|
||||
if not url.endswith('/api'):
|
||||
url += '/api'
|
||||
|
||||
print(f'Info: Setting Host for kitsu {url}')
|
||||
gazu.client.set_host(url)
|
||||
|
||||
if not gazu.client.host_is_up():
|
||||
print('Error: Kitsu Host is down')
|
||||
|
||||
try:
|
||||
print(f'Info: Log in to kitsu as {login}')
|
||||
res = gazu.log_in(login, password)
|
||||
LOGIN = login
|
||||
print(f'Info: Sucessfully login to Kitsu as {res["user"]["full_name"]}')
|
||||
return res['user']
|
||||
except Exception as e:
|
||||
print(f'Error: {traceback.format_exc()}')
|
||||
|
||||
def get_id(self, data):
|
||||
if isinstance(data, str):
|
||||
if self.is_id(data):
|
||||
return data
|
||||
#return None
|
||||
elif isinstance(data, dict):
|
||||
return data['id']
|
||||
elif data: # Should be a Class
|
||||
return data.id
|
||||
|
||||
def is_id(self, id):
|
||||
if not isinstance(id, str):
|
||||
return False
|
||||
try:
|
||||
uuid.UUID(id)
|
||||
return True
|
||||
except ValueError:
|
||||
return False
|
||||
|
||||
def get_project(self, project=None):
|
||||
if project:
|
||||
project_id = self.get_id(project)
|
||||
if project_id:
|
||||
return project_id
|
||||
return gazu.project.get_project_by_name(project)
|
||||
|
||||
if os.environ.get('TRACKER_PROJECT_ID'):
|
||||
return os.environ['TRACKER_PROJECT_ID']
|
||||
elif os.environ.get('TRACKER_PROJECT_NAME'):
|
||||
return os.environ['TRACKER_PROJECT_NAME']
|
||||
|
||||
def draw_prefs(self, layout):
|
||||
layout.prop(self, 'url', text='Url')
|
||||
layout.prop(self, 'login', text='Login')
|
||||
layout.prop(self, 'password', text='Password')
|
||||
|
||||
def get_projects(self):
|
||||
return gazu.project.all_open_projects()
|
||||
|
||||
def get_episode(self, episode, project=None):
|
||||
project = self.get_project(project)
|
||||
if episode:
|
||||
episode_id = self.get_id(episode)
|
||||
if episode_id:
|
||||
return episode_id
|
||||
return gazu.shot.get_episode_by_name(project, episode)
|
||||
|
||||
if os.environ.get('TRACKER_EPISODE_ID'):
|
||||
return os.environ['TRACKER_EPISODE_ID']
|
||||
elif os.environ.get('TRACKER_EPISODE_NAME'):
|
||||
return os.environ['TRACKER_EPISODE_NAME']
|
||||
|
||||
def get_episodes(self, project):
|
||||
return gazu.shot.all_episodes_for_project(project)
|
||||
|
||||
def get_episode(self, name):
|
||||
return gazu.shot.get_episode_by_name(self.project, name)
|
||||
def get_task_type(self, task_type=None):
|
||||
print('get_task_type', task_type)
|
||||
return gazu.task.get_task_type_by_name(task_type)
|
||||
|
||||
def get_asset_types(self, project):
|
||||
def get_shot_task_types(self, project=None):
|
||||
project = self.get_project(project)
|
||||
task_types = gazu.task.all_task_types_for_project(project)
|
||||
return [t for t in task_types if t['for_entity'].lower() == 'shot']
|
||||
|
||||
def get_shots_metadata(self, project=None):
|
||||
project = self.get_project(project)
|
||||
metadatas = []
|
||||
|
||||
for metadata in gazu.project.all_metadata_descriptors(project):
|
||||
if metadata['entity_type'] == 'Shot' and metadata['name']:
|
||||
metadatas.append(metadata)
|
||||
|
||||
return metadatas
|
||||
|
||||
def get_task_status(self, status=None):
|
||||
status_id = self.get_id(status)
|
||||
if status_id:
|
||||
return status_id
|
||||
|
||||
return gazu.client.fetch_first('task-status', {"short_name": status.lower()})
|
||||
|
||||
def get_task_statuses(self, project=None):
|
||||
project = self.get_project(project)
|
||||
return gazu.task.all_task_statuses_for_project(project)
|
||||
|
||||
def get_asset_types(self, project=None):
|
||||
project = self.get_project(project)
|
||||
return gazu.asset.all_asset_types_for_project(project)
|
||||
|
||||
def get_assets(self, project):
|
||||
def get_sequence(self, sequence, project=None):
|
||||
print(f'get_sequence({sequence=}, {project=})')
|
||||
project = self.get_project(project)
|
||||
|
||||
sequence_id = self.get_id(sequence)
|
||||
if sequence_id:
|
||||
return sequence_id
|
||||
|
||||
return gazu.shot.get_sequence_by_name(project, sequence)
|
||||
|
||||
def get_shot(self, shot, sequence, project=None):
|
||||
print(f'get_shot({shot=}, {sequence=}, {project=})')
|
||||
project = self.get_project(project)
|
||||
sequence = self.get_sequence(sequence, project)
|
||||
|
||||
if not sequence:
|
||||
return
|
||||
|
||||
shot_id = self.get_id(shot)
|
||||
if shot_id:
|
||||
return shot_id
|
||||
|
||||
return gazu.shot.get_shot_by_name(sequence, shot)
|
||||
|
||||
def get_assets(self, project=None):
|
||||
project = self.get_project(project)
|
||||
assets = gazu.asset.all_assets_for_project(project)
|
||||
entity_types = self.get_asset_types(project)
|
||||
entity_types_ids = {e['id']: e['name'] for e in entity_types}
|
||||
|
@ -55,14 +184,54 @@ class Kitsu(Tracker):
|
|||
|
||||
return assets
|
||||
|
||||
def get_shots_metadata(self, project):
|
||||
metadatas = []
|
||||
def get_last_comment(self, task):
|
||||
task = self.get_id(task)
|
||||
return gazu.task.get_last_comment_for_task(task)
|
||||
|
||||
for metadata in gazu.project.all_metadata_descriptors(project):
|
||||
if metadata['entity_type'] == 'Shot' and metadata['name']:
|
||||
metadatas.append(metadata)
|
||||
def get_task(self, task=None, entity=None):
|
||||
entity = self.get_id(entity)
|
||||
|
||||
return metadatas
|
||||
task_type = self.get_task_type(task)
|
||||
|
||||
print('task_type', task_type)
|
||||
print('task_type', task_type)
|
||||
|
||||
task = gazu.task.get_task_by_name(entity, task_type)
|
||||
#task = gazu.task.get_task(task['id'])
|
||||
|
||||
task['last_comment'] = self.get_last_comment(task)
|
||||
|
||||
return task
|
||||
|
||||
def new_comment(self, task, status=None, comment='', preview=None, set_main_preview=False):
|
||||
#task = self.get_task(task)
|
||||
|
||||
#print('Add Comment', status)
|
||||
if status is None:
|
||||
#print(task)
|
||||
status = {'id' : task['task_status_id']}
|
||||
else:
|
||||
status = self.get_task_status(status)
|
||||
|
||||
|
||||
|
||||
comment = gazu.task.add_comment(
|
||||
task=task,
|
||||
task_status=status,
|
||||
comment=comment )
|
||||
|
||||
if preview:
|
||||
logging.info(f'Adding Preview to Kitsu {preview}')
|
||||
preview = self.add_preview(
|
||||
task=task,
|
||||
comment=comment,
|
||||
preview=str(preview) )
|
||||
|
||||
if set_main_preview:
|
||||
gazu.task.set_main_preview(preview)
|
||||
|
||||
|
||||
return comment
|
||||
|
||||
def download_preview(self, preview_id, filepath):
|
||||
if isinstance(filepath, str):
|
||||
|
@ -74,100 +243,43 @@ class Kitsu(Tracker):
|
|||
filepath.parent.mkdir(parents=True, exist_ok=True)
|
||||
gazu.files.download_preview_file_thumbnail(preview_id, filepath.as_posix())
|
||||
|
||||
def connect(self, url=None, login=None, password=None):
|
||||
'''Connect to kitsu api using provided url, login and password'''
|
||||
def new_sequence(self, sequence, episode=None, project=None):
|
||||
project = self.get_project(project)
|
||||
|
||||
urllib3.disable_warnings()
|
||||
params = dict(name=sequence, project=project)
|
||||
episode = self.get_episode(episode)
|
||||
if episode:
|
||||
params['episode'] = episode
|
||||
|
||||
if not self.url:
|
||||
print(f'Kitsu Url: {self.url} is empty')
|
||||
return
|
||||
return gazu.shot.new_sequence(**params)
|
||||
|
||||
url = self.url
|
||||
if not url.endswith('/api'):
|
||||
url += '/api'
|
||||
def new_shot(self, shot, sequence, nb_frames=None, frame_in=None,
|
||||
frame_out=None, description='', custom_data=None, project=None):
|
||||
|
||||
print(f'Info: Setting Host for kitsu {url}')
|
||||
gazu.client.set_host(url)
|
||||
project = self.get_project(project)
|
||||
sequence = self.get_sequence(sequence)
|
||||
|
||||
if not gazu.client.host_is_up():
|
||||
print('Error: Kitsu Host is down')
|
||||
custom_data = custom_data or {}
|
||||
|
||||
try:
|
||||
print(f'Info: Log in to kitsu as {self.login}')
|
||||
res = gazu.log_in(self.login, self.password)
|
||||
print(f'Info: Sucessfully login to Kitsu as {res["user"]["full_name"]}')
|
||||
return res['user']
|
||||
except Exception as e:
|
||||
print(f'Error: {traceback.format_exc()}')
|
||||
if frame_in is not None:
|
||||
custom_data["frame_in"] = frame_in
|
||||
if frame_out is not None:
|
||||
custom_data["frame_out"] = frame_out
|
||||
|
||||
def get_asset_path(self, name, catalog, directory=None):
|
||||
directory = directory or self.source_directory
|
||||
return Path(directory, self.get_asset_relative_path(name, catalog))
|
||||
params = dict(name=shot, data=custom_data,
|
||||
sequence_id=self.get_id(sequence), description=description)
|
||||
|
||||
def get_asset_info(self, data, asset_path):
|
||||
modified = time.time_ns()
|
||||
catalog = data['entity_type_name'].title()
|
||||
asset_path = self.prop_rel_path(asset_path, 'source_directory')
|
||||
if nb_frames is not None:
|
||||
params["nb_frames"] = nb_frames
|
||||
|
||||
asset_info = dict(
|
||||
filepath=asset_path,
|
||||
modified=modified,
|
||||
library_id=self.library.id,
|
||||
assets=[dict(
|
||||
catalog=catalog,
|
||||
metadata=data.get('data', {}),
|
||||
description=data['description'],
|
||||
tags=[],
|
||||
type=self.data_type,
|
||||
name=data['name'])
|
||||
]
|
||||
)
|
||||
|
||||
return asset_info
|
||||
shot = self.get_shot(shot=shot, sequence=sequence)
|
||||
if shot:
|
||||
return shot
|
||||
else:
|
||||
path = f"data/projects/{self.get_id(project)}/shots"
|
||||
return gazu.client.post(path, params)
|
||||
|
||||
def fetch(self):
|
||||
"""Gather in a list all assets found in the folder"""
|
||||
print(f'Fetch Assets for {self.library.name}')
|
||||
|
||||
self.connect()
|
||||
|
||||
template_file = Template(self.template_file)
|
||||
template_name = Template(self.template_name)
|
||||
|
||||
project = gazu.client.fetch_first('projects', {'name': self.project_name})
|
||||
entity_types = gazu.client.fetch_all('entity-types')
|
||||
entity_types_ids = {e['id']: e['name'] for e in entity_types}
|
||||
|
||||
cache = self.read_cache()
|
||||
|
||||
for asset_data in gazu.asset.all_assets_for_project(project):
|
||||
asset_data['entity_type_name'] = entity_types_ids[asset_data.pop('entity_type_id')]
|
||||
asset_name = asset_data['name']
|
||||
|
||||
asset_field_data = dict(asset_name=asset_name, type=asset_data['entity_type_name'], source_directory=self.source_directory)
|
||||
|
||||
try:
|
||||
asset_field_data.update(template_name.parse(asset_name))
|
||||
except Exception:
|
||||
print(f'Warning: Could not parse {asset_name} with template {template_name}')
|
||||
|
||||
asset_path = template_file.find(asset_field_data)
|
||||
if not asset_path:
|
||||
print(f'Warning: Could not find file for {template_file.format(asset_field_data)}')
|
||||
continue
|
||||
|
||||
|
||||
asset_path = self.prop_rel_path(asset_path, 'source_directory')
|
||||
asset_cache_data = dict(
|
||||
catalog=asset_data['entity_type_name'].title(),
|
||||
metadata=asset_data.get('data', {}),
|
||||
description=asset_data['description'],
|
||||
tags=[],
|
||||
type=self.data_type,
|
||||
name=asset_data['name']
|
||||
)
|
||||
|
||||
cache.add_asset_cache(asset_cache_data, filepath=asset_path)
|
||||
|
||||
return cache
|
||||
def draw_prefs(self, layout):
|
||||
layout.prop(self, 'url', text='Url')
|
||||
layout.prop(self, 'login', text='Login')
|
||||
layout.prop(self, 'password', text='Password')
|
|
@ -1,4 +1,9 @@
|
|||
import bpy
|
||||
from bpy.types import PropertyGroup
|
||||
from bpy.props import PointerProperty, StringProperty
|
||||
from vse_toolbox.file_utils import norm_str
|
||||
|
||||
|
||||
class Tracker(PropertyGroup):
|
||||
pass
|
||||
pass
|
||||
|
|
@ -1,10 +1,11 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
import bpy
|
||||
import re
|
||||
|
||||
from bpy.app.handlers import persistent
|
||||
from pathlib import Path
|
||||
|
||||
import bpy
|
||||
from bpy.app.handlers import persistent
|
||||
|
||||
from vse_toolbox.bl_utils import get_scene_settings, get_strip_settings
|
||||
from vse_toolbox.constants import SOUND_SUFFIXES
|
||||
|
||||
|
@ -122,9 +123,31 @@ def set_channels():
|
|||
for i, c in enumerate(items.keys(), start=1):
|
||||
scn.sequence_editor.channels[i].name = c.title()
|
||||
|
||||
def render_strips(strips):
|
||||
def get_strip_render_path(strip, template):
|
||||
return template.format(strip_name=strip.name, ext=Path(scn.render.frame_path()).suffix)
|
||||
|
||||
def render_strips(strips, template):
|
||||
scn = bpy.context.scene
|
||||
scene_start = scn.frame_start
|
||||
scene_end = scn.frame_end
|
||||
|
||||
for strip in strips:
|
||||
print(strip.name)
|
||||
#print(render_template, strip.name, path)
|
||||
|
||||
scn.frame_start = strip.frame_final_start
|
||||
scn.frame_end = strip.frame_final_end - 1
|
||||
|
||||
## render animatic
|
||||
#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)
|
||||
#print(scn.render.filepath)
|
||||
print(f'Render Strip to {scn.render.filepath}')
|
||||
#bpy.ops.render.render(animation=True)
|
||||
bpy.ops.render.opengl(animation=True, sequencer=True)
|
||||
|
||||
|
||||
scn.frame_start = scene_start
|
||||
scn.frame_end = scene_end
|
||||
|
||||
def import_edit(filepath, adapter="cmx_3600", clean_sequencer=False):
|
||||
import opentimelineio as otio
|
||||
|
|
Loading…
Reference in New Issue