Merge shot_builder into blender kitsu #3

Merged
Nick Alberelli merged 1 commits from :feature/merge_shot_builder_into_blender_kitsu into master 2023-04-04 16:43:33 +02:00
33 changed files with 697 additions and 753 deletions

View File

@ -20,6 +20,7 @@
import bpy import bpy
from blender_kitsu import ( from blender_kitsu import (
shot_builder,
lookdev, lookdev,
bkglobals, bkglobals,
types, types,
@ -92,6 +93,7 @@ def register():
# tasks.register() # tasks.register()
playblast.register() playblast.register()
anim.register() anim.register()
shot_builder.register()
LoggerLevelManager.configure_levels() LoggerLevelManager.configure_levels()
logger.info("Registered blender-kitsu") logger.info("Registered blender-kitsu")
@ -109,6 +111,7 @@ def unregister():
prefs.unregister() prefs.unregister()
lookdev.unregister() lookdev.unregister()
playblast.unregister() playblast.unregister()
shot_builder.unregister()
LoggerLevelManager.restore_levels() LoggerLevelManager.restore_levels()

View File

@ -285,6 +285,15 @@ class KITSU_addon_preferences(bpy.types.AddonPreferences):
type=KITSU_media_update_search_paths type=KITSU_media_update_search_paths
) )
production_path: bpy.props.StringProperty( # type: ignore
name="Production Root",
description="The location to load configuration files from when "
"they couldn't be found in any parent folder of the current "
"file. Folder must contain a sub-folder named `shot-builder` "
"that holds the configuration files",
subtype='DIR_PATH',
)
session: Session = Session() session: Session = Session()
tasks: bpy.props.CollectionProperty(type=KITSU_task) tasks: bpy.props.CollectionProperty(type=KITSU_task)

View File

@ -18,30 +18,28 @@
# <pep8 compliant> # <pep8 compliant>
from shot_builder.ui import * from blender_kitsu.shot_builder.ui import *
from shot_builder.connectors.kitsu import * from blender_kitsu.shot_builder.connectors.kitsu import *
from shot_builder.operators import * from blender_kitsu.shot_builder.operators import *
from shot_builder.properties import *
import bpy import bpy
# import logging # import logging
# logging.basicConfig(level=logging.DEBUG) # logging.basicConfig(level=logging.DEBUG)
bl_info = { # bl_info = {
'name': 'Shot Builder', # 'name': 'Shot Builder',
"author": "Jeroen Bakker", # "author": "Jeroen Bakker",
'version': (0, 1), # 'version': (0, 1),
'blender': (2, 90, 0), # 'blender': (2, 90, 0),
'location': 'Addon Preferences panel and file new menu', # 'location': 'Addon Preferences panel and file new menu',
'description': 'Shot builder production tool.', # 'description': 'Shot builder production tool.',
'category': 'Studio', # 'category': 'Studio',
} # }
classes = ( classes = (
KitsuPreferences, KitsuPreferences,
ShotBuilderPreferences,
SHOTBUILDER_OT_NewShotFile, SHOTBUILDER_OT_NewShotFile,
) )

View File

@ -1,13 +1,13 @@
from shot_builder.project import Production from blender_kitsu.shot_builder.project import Production
from shot_builder.task_type import TaskType from blender_kitsu.shot_builder.task_type import TaskType
from shot_builder.asset import Asset, AssetRef from blender_kitsu.shot_builder.asset import Asset, AssetRef
from shot_builder.builder.build_step import BuildStep, BuildContext from blender_kitsu.shot_builder.builder.build_step import BuildStep, BuildContext
from shot_builder.builder.init_asset import InitAssetStep from blender_kitsu.shot_builder.builder.init_asset import InitAssetStep
from shot_builder.builder.init_shot import InitShotStep from blender_kitsu.shot_builder.builder.init_shot import InitShotStep
from shot_builder.builder.set_render_settings import SetRenderSettingsStep from blender_kitsu.shot_builder.builder.set_render_settings import SetRenderSettingsStep
from shot_builder.builder.new_scene import NewSceneStep from blender_kitsu.shot_builder.builder.new_scene import NewSceneStep
from shot_builder.builder.invoke_hook import InvokeHookStep from blender_kitsu.shot_builder.builder.invoke_hook import InvokeHookStep
from shot_builder.builder.save_file import SaveFileStep from blender_kitsu.shot_builder.builder.save_file import SaveFileStep
import bpy import bpy

View File

@ -1,11 +1,11 @@
import bpy import bpy
import typing import typing
from shot_builder.project import Production from blender_kitsu.shot_builder.project import Production
from shot_builder.shot import Shot from blender_kitsu.shot_builder.shot import Shot
from shot_builder.task_type import TaskType from blender_kitsu.shot_builder.task_type import TaskType
from shot_builder.render_settings import RenderSettings from blender_kitsu.shot_builder.render_settings import RenderSettings
from shot_builder.asset import Asset from blender_kitsu.shot_builder.asset import Asset
class BuildContext: class BuildContext:

View File

@ -1,7 +1,7 @@
from shot_builder.builder.build_step import BuildStep, BuildContext from blender_kitsu.shot_builder.builder.build_step import BuildStep, BuildContext
from shot_builder.asset import * from blender_kitsu.shot_builder.asset import *
from shot_builder.project import * from blender_kitsu.shot_builder.project import *
from shot_builder.shot import * from blender_kitsu.shot_builder.shot import *
import bpy import bpy

View File

@ -1,7 +1,7 @@
from shot_builder.builder.build_step import BuildStep, BuildContext from blender_kitsu.shot_builder.builder.build_step import BuildStep, BuildContext
from shot_builder.asset import * from blender_kitsu.shot_builder.asset import *
from shot_builder.project import * from blender_kitsu.shot_builder.project import *
from shot_builder.shot import * from blender_kitsu.shot_builder.shot import *
import bpy import bpy

View File

@ -1,5 +1,5 @@
from shot_builder.builder.build_step import BuildStep, BuildContext from blender_kitsu.shot_builder.builder.build_step import BuildStep, BuildContext
from shot_builder.hooks import HookFunction from blender_kitsu.shot_builder.hooks import HookFunction
import bpy import bpy
import typing import typing

View File

@ -1,5 +1,5 @@
from shot_builder.builder.build_step import BuildStep, BuildContext from blender_kitsu.shot_builder.builder.build_step import BuildStep, BuildContext
from shot_builder.render_settings import RenderSettings from blender_kitsu.shot_builder.render_settings import RenderSettings
import bpy import bpy
import logging import logging

View File

@ -1,7 +1,7 @@
from shot_builder.builder.build_step import BuildStep, BuildContext from blender_kitsu.shot_builder.builder.build_step import BuildStep, BuildContext
from shot_builder.asset import * from blender_kitsu.shot_builder.asset import *
from shot_builder.project import * from blender_kitsu.shot_builder.project import *
from shot_builder.shot import * from blender_kitsu.shot_builder.shot import *
import pathlib import pathlib
import bpy import bpy

View File

@ -1,4 +1,4 @@
from shot_builder.builder.build_step import BuildStep, BuildContext from blender_kitsu.shot_builder.builder.build_step import BuildStep, BuildContext
import bpy import bpy
import typing import typing

View File

@ -21,16 +21,16 @@
This module contains the Connector class. It is an abstract base class for concrete connectors. This module contains the Connector class. It is an abstract base class for concrete connectors.
""" """
from shot_builder.shot import Shot, ShotRef from blender_kitsu.shot_builder.shot import Shot, ShotRef
from shot_builder.asset import Asset, AssetRef from blender_kitsu.shot_builder.asset import Asset, AssetRef
from shot_builder.task_type import TaskType from blender_kitsu.shot_builder.task_type import TaskType
from shot_builder.render_settings import RenderSettings from blender_kitsu.shot_builder.render_settings import RenderSettings
from typing import * from typing import *
if TYPE_CHECKING: if TYPE_CHECKING:
from shot_builder.project import Production from blender_kitsu.shot_builder.project import Production
from shot_builder.properties import ShotBuilderPreferences from blender_kitsu.shot_builder.properties import ShotBuilderPreferences
class Connector: class Connector:
@ -58,8 +58,8 @@ class Connector:
Example of using predefined connectors in a production config file: Example of using predefined connectors in a production config file:
```shot-builder/config.py ```shot-builder/config.py
from shot_builder.connectors.default import DefaultConnector from blender_kitsu.shot_builder.connectors.default import DefaultConnector
from shot_builder.connectors.kitsu import KitsuConnector from blender_kitsu.shot_builder.connectors.kitsu import KitsuConnector
PRODUCTION_NAME = DefaultConnector PRODUCTION_NAME = DefaultConnector
TASK_TYPES = KitsuConnector TASK_TYPES = KitsuConnector

View File

@ -17,11 +17,11 @@
# ##### END GPL LICENSE BLOCK ##### # ##### END GPL LICENSE BLOCK #####
# <pep8 compliant> # <pep8 compliant>
from shot_builder.shot import Shot, ShotRef from blender_kitsu.shot_builder.shot import Shot, ShotRef
from shot_builder.asset import Asset, AssetRef from blender_kitsu.shot_builder.asset import Asset, AssetRef
from shot_builder.task_type import TaskType from blender_kitsu.shot_builder.task_type import TaskType
from shot_builder.render_settings import RenderSettings from blender_kitsu.shot_builder.render_settings import RenderSettings
from shot_builder.connectors.connector import Connector from blender_kitsu.shot_builder.connectors.connector import Connector
from typing import * from typing import *

View File

@ -18,13 +18,16 @@
# <pep8 compliant> # <pep8 compliant>
import bpy import bpy
from shot_builder import vars from blender_kitsu.shot_builder import vars
from shot_builder.shot import Shot, ShotRef from blender_kitsu.shot_builder.shot import Shot, ShotRef
from shot_builder.asset import Asset, AssetRef from blender_kitsu.shot_builder.asset import Asset, AssetRef
from shot_builder.task_type import TaskType from blender_kitsu.shot_builder.task_type import TaskType
from shot_builder.render_settings import RenderSettings from blender_kitsu.shot_builder.render_settings import RenderSettings
from shot_builder.connectors.connector import Connector from blender_kitsu.shot_builder.connectors.connector import Connector
import requests import requests
from blender_kitsu import cache
from blender_kitsu.gazu.asset import all_assets_for_shot
from blender_kitsu.gazu.shot import all_shots_for_project, all_sequences_for_project
import typing import typing
import logging import logging
@ -131,47 +134,9 @@ class KitsuConnector(Connector):
def __init__(self, **kwargs): def __init__(self, **kwargs):
super().__init__(**kwargs) super().__init__(**kwargs)
self.__jwt_access_token = ""
self.__validate()
self.__authorize()
def __validate(self) -> None:
self._preferences.kitsu._validate()
if not self._production.config.get('KITSU_PROJECT_ID'):
raise KitsuException(
"KITSU_PROJECT_ID is not configured in config.py")
def __authorize(self) -> None:
kitsu_pref = self._preferences.kitsu
backend = kitsu_pref.backend
username = kitsu_pref.username
password = kitsu_pref.password
logger.info(f"authorize {username} against {backend}")
response = requests.post(
url=f"{backend}/auth/login", data={'email': username, 'password': password})
if response.status_code != 200:
self.__jwt_access_token = ""
raise KitsuException(
f"unable to authorize (status code={response.status_code})")
json_response = response.json()
self.__jwt_access_token = json_response['access_token']
def __api_get(self, api: str) -> typing.Any:
kitsu_pref = self._preferences.kitsu
backend = kitsu_pref.backend
response = requests.get(url=f"{backend}{api}", headers={
"Authorization": f"Bearer {self.__jwt_access_token}"
})
if response.status_code != 200:
raise KitsuException(
f"unable to call kitsu (api={api}, status code={response.status_code})")
return response.json()
def __get_production_data(self) -> KitsuProject: def __get_production_data(self) -> KitsuProject:
project_id = self._production.config['KITSU_PROJECT_ID'] production = cache.project_active_get()
production = self.__api_get(f"data/projects/{project_id}")
project = KitsuProject(typing.cast( project = KitsuProject(typing.cast(
typing.Dict[str, typing.Any], production)) typing.Dict[str, typing.Any], production))
return project return project
@ -181,22 +146,23 @@ class KitsuConnector(Connector):
return production.get_name() return production.get_name()
def get_task_types(self) -> typing.List[TaskType]: def get_task_types(self) -> typing.List[TaskType]:
task_types = self.__api_get(f"data/task_types/") project = cache.project_active_get()
task_types = project.task_types
import pprint import pprint
pprint.pprint(task_types) pprint.pprint(task_types)
return [] return []
def get_shots(self) -> typing.List[ShotRef]: def get_shots(self) -> typing.List[ShotRef]:
project_id = self._production.config['KITSU_PROJECT_ID'] project = cache.project_active_get()
kitsu_sequences = self.__api_get(f"data/projects/{project_id}/sequences") kitsu_sequences = all_sequences_for_project(project.id)
sequence_lookup = {sequence_data['id']: KitsuSequenceRef( sequence_lookup = {sequence_data['id']: KitsuSequenceRef(
kitsu_id=sequence_data['id'], kitsu_id=sequence_data['id'],
name=sequence_data['name'], name=sequence_data['name'],
code=sequence_data['code'], code=sequence_data['code'],
) for sequence_data in kitsu_sequences} ) for sequence_data in kitsu_sequences}
kitsu_shots = self.__api_get(f"data/projects/{project_id}/shots") kitsu_shots = all_shots_for_project(project.id)
shots: typing.List[ShotRef] = [] shots: typing.List[ShotRef] = []
for shot_data in kitsu_shots: for shot_data in kitsu_shots:
@ -232,8 +198,8 @@ class KitsuConnector(Connector):
return shots return shots
def get_assets_for_shot(self, shot: Shot) -> typing.List[AssetRef]: def get_assets_for_shot(self, shot: Shot) -> typing.List[AssetRef]:
kitsu_assets = self.__api_get( kitsu_assets = all_assets_for_shot(shot.kitsu_id)
f"data/shots/{shot.kitsu_id}/assets")
return [AssetRef(name=asset_data['name'], code=asset_data['code']) return [AssetRef(name=asset_data['name'], code=asset_data['code'])
for asset_data in kitsu_assets] for asset_data in kitsu_assets]
@ -241,7 +207,5 @@ class KitsuConnector(Connector):
""" """
Retrieve the render settings for the given shot. Retrieve the render settings for the given shot.
""" """
kitsu_project = self.__get_production_data() project = cache.project_active_get()
resolution = kitsu_project.get_resolution() return RenderSettings(width=int(project.resolution.split('x')[0]), height=int(project.resolution.split('x')[1]), frames_per_second=project.fps)
frames_per_second = shot.frames_per_second
return RenderSettings(width=resolution[0], height=resolution[1], frames_per_second=frames_per_second)

View File

@ -1,4 +1,4 @@
from shot_builder.asset import Asset from blender_kitsu.shot_builder.asset import Asset
class SpriteFrightAsset(Asset): class SpriteFrightAsset(Asset):

View File

@ -1,4 +1,4 @@
from shot_builder.connectors.kitsu import KitsuConnector from blender_kitsu.shot_builder.connectors.kitsu import KitsuConnector
PRODUCTION_NAME = KitsuConnector PRODUCTION_NAME = KitsuConnector
SHOTS = KitsuConnector SHOTS = KitsuConnector

View File

@ -1,8 +1,8 @@
import bpy import bpy
from shot_builder.hooks import hook, Wildcard from blender_kitsu.shot_builder.hooks import hook, Wildcard
from shot_builder.asset import Asset from blender_kitsu.shot_builder.asset import Asset
from shot_builder.shot import Shot from blender_kitsu.shot_builder.shot import Shot
from shot_builder.project import Production from blender_kitsu.shot_builder.project import Production
import logging import logging

View File

@ -0,0 +1,6 @@
# Example configuration files
This folder contains an example shot builder configuration. It shows the part
that a TD would do to incorporate the shot builder in a production.

View File

@ -1,5 +1,5 @@
from shot_builder.shot import Shot from blender_kitsu.shot_builder.shot import Shot
from shot_builder.project import Production from blender_kitsu.shot_builder.project import Production
class SpriteFrightShot(Shot): class SpriteFrightShot(Shot):

View File

@ -78,7 +78,7 @@ class Hooks:
def _register_hook(func: types.FunctionType) -> None: def _register_hook(func: types.FunctionType) -> None:
from shot_builder.project import get_active_production from blender_kitsu.shot_builder.project import get_active_production
production = get_active_production() production = get_active_production()
production.hooks.register(func) production.hooks.register(func)

View File

@ -19,10 +19,11 @@
# <pep8 compliant> # <pep8 compliant>
from typing import * from typing import *
import bpy import bpy
from shot_builder.shot import ShotRef from blender_kitsu.shot_builder.shot import ShotRef
from shot_builder.project import * from blender_kitsu.shot_builder.project import ensure_loaded_production, get_active_production
from shot_builder.builder import ShotBuilder from blender_kitsu.shot_builder.builder import ShotBuilder
from shot_builder.task_type import TaskType from blender_kitsu.shot_builder.task_type import TaskType
from blender_kitsu import prefs, cache
_production_task_type_items: List[Tuple[str, str, str]] = [] _production_task_type_items: List[Tuple[str, str, str]] = []
@ -103,18 +104,34 @@ class SHOTBUILDER_OT_NewShotFile(bpy.types.Operator):
) )
def invoke(self, context: bpy.types.Context, event: bpy.types.Event) -> Set[str]: def invoke(self, context: bpy.types.Context, event: bpy.types.Event) -> Set[str]:
addon_prefs = prefs.addon_prefs_get(bpy.context)
project = cache.project_active_get()
production_root = get_production_root(context) if addon_prefs.session.is_auth() is False:
if production_root is None:
self.report( self.report(
{'WARNING'}, "Operator is cancelled due to inability to determine the production path. Make sure the a default path in configured in the preferences.") {'ERROR'}, "Must be logged into Kitsu to continue. Check login status in 'Blender Kitsu' addon preferences.")
return {'CANCELLED'} return {'CANCELLED'}
if project.id == "":
self.report(
{'ERROR'}, "Operator is not able to determine the Kitsu production's name. Check project is selected in 'Blender Kitsu' addon preferences.")
return {'CANCELLED'}
if not addon_prefs.is_project_root_valid:
self.report(
{'ERROR'}, "Operator is not able to determine the project root directory. Check project root directiory is configured in 'Blender Kitsu' addon preferences.")
return {'CANCELLED'}
self.production_root = addon_prefs.project_root_dir
self.production_name = project.name
ensure_loaded_production(context) ensure_loaded_production(context)
production = get_active_production() production = get_active_production()
self.production_root = str(production.path)
self.production_name = production.get_name(context=context) self.production_root = addon_prefs.project_root_dir
self.production_name = project.name
global _production_task_type_items global _production_task_type_items
_production_task_type_items = production.get_task_type_items( _production_task_type_items = production.get_task_type_items(

View File

@ -23,15 +23,18 @@ from collections import defaultdict
import bpy import bpy
from shot_builder.task_type import * from blender_kitsu.shot_builder.task_type import *
from shot_builder.shot import Shot, ShotRef from blender_kitsu.shot_builder.shot import Shot, ShotRef
from shot_builder.render_settings import RenderSettings from blender_kitsu.shot_builder.render_settings import RenderSettings
from shot_builder.asset import Asset, AssetRef from blender_kitsu.shot_builder.asset import Asset, AssetRef
from shot_builder.sys_utils import * from blender_kitsu.shot_builder.sys_utils import *
from shot_builder.hooks import Hooks, register_hooks from blender_kitsu.shot_builder.hooks import Hooks, register_hooks
from shot_builder.connectors.default import DefaultConnector from blender_kitsu.shot_builder.connectors.default import DefaultConnector
from shot_builder.connectors.connector import Connector from blender_kitsu.shot_builder.connectors.connector import Connector
from blender_kitsu import prefs
from pathlib import Path
from typing import * from typing import *
import types import types
@ -77,7 +80,7 @@ class Production:
connector_cls: Type[Connector], connector_cls: Type[Connector],
context: bpy.types.Context) -> Connector: context: bpy.types.Context) -> Connector:
# TODO: Cache connector # TODO: Cache connector
preferences = context.preferences.addons[__package__].preferences preferences = context.preferences.addons["blender_kitsu"].preferences
return connector_cls(production=self, preferences=preferences) return connector_cls(production=self, preferences=preferences)
def __format_shot_name(self, shot: Shot) -> str: def __format_shot_name(self, shot: Shot) -> str:
@ -382,8 +385,9 @@ def get_production_root(context: bpy.types.Context) -> Optional[pathlib.Path]:
production_root = _find_production_root(current_file) production_root = _find_production_root(current_file)
if production_root: if production_root:
return production_root return production_root
production_root = pathlib.Path(
context.preferences.addons[__package__].preferences.production_path) addon_prefs = prefs.addon_prefs_get(bpy.context)
production_root = Path(addon_prefs.project_root_dir)
if is_valid_production_root(production_root): if is_valid_production_root(production_root):
return production_root return production_root
return None return None

View File

@ -18,7 +18,7 @@
# <pep8 compliant> # <pep8 compliant>
from shot_builder.asset import Asset from blender_kitsu.shot_builder.asset import Asset
from typing import * from typing import *

View File

@ -19,7 +19,7 @@
# <pep8 compliant> # <pep8 compliant>
import bpy import bpy
from typing import * from typing import *
from shot_builder.operators import * from blender_kitsu.shot_builder.operators import *
def topbar_file_new_draw_handler(self: Any, context: bpy.types.Context) -> None: def topbar_file_new_draw_handler(self: Any, context: bpy.types.Context) -> None:

View File

@ -1,57 +0,0 @@
# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
# <pep8 compliant>
import bpy
import pathlib
from shot_builder.project import is_valid_production_root
from shot_builder.connectors.kitsu import KitsuPreferences
class ShotBuilderPreferences(bpy.types.AddonPreferences):
bl_idname = __package__
production_path: bpy.props.StringProperty( # type: ignore
name="Production Root",
description="The location to load configuration files from when "
"they couldn't be found in any parent folder of the current "
"file. Folder must contain a sub-folder named `shot-builder` "
"that holds the configuration files",
subtype='DIR_PATH',
)
kitsu: bpy.props.PointerProperty( # type: ignore
name="Kitsu Preferences",
type=KitsuPreferences
)
def draw(self, context: bpy.types.Context) -> None:
layout = self.layout
is_valid = is_valid_production_root(pathlib.Path(self.production_path))
layout.prop(self, "production_path",
icon='NONE' if is_valid else 'ERROR')
if not is_valid:
layout.label(text="Folder must contain a sub-folder named "
"`shot-builder` that holds the configuration "
"files.",
icon="ERROR")
sublayout = layout.box()
self.kitsu.draw(sublayout, context)