From 008d027f062f5f07631e2bd738d8bb31f1aaa4b0 Mon Sep 17 00:00:00 2001 From: zachEastin <14118550+zachEastin@users.noreply.github.com> Date: Thu, 15 Aug 2024 15:49:12 -0500 Subject: [PATCH] Initial Blender Version stuff --- ops/asset_ops.py | 87 +++++++++----------- ops/blender_exes_ops.py | 32 +++++++- settings/asset.py | 176 +++++++++++++++++++++++++++++++++------- ui/asset_browser.py | 48 ++++------- ui/prefs.py | 66 +++++++++------ utils.py | 117 ++++++++------------------ 6 files changed, 301 insertions(+), 225 deletions(-) diff --git a/ops/asset_ops.py b/ops/asset_ops.py index 26ce504..468eb64 100644 --- a/ops/asset_ops.py +++ b/ops/asset_ops.py @@ -33,8 +33,6 @@ def execute(self, context): self.report({"ERROR"}, "No active asset found") return {"CANCELLED"} - prefs = utils.get_prefs() - asset = utils.Asset(context.asset) if context.asset.metadata.sh_catalog == "CUSTOM": @@ -99,9 +97,7 @@ def _invoke(self, context: Context, event): self.prog = self.scene_sets.side_panel_batch_asset_update_progress_bar self.prog.metadata_label = "Metadata Update" self.prog.start() - self.prog.draw_icon_rendering = ( - self.scene_sets.metadata_update.render_thumbnails - ) + self.prog.draw_icon_rendering = self.scene_sets.metadata_update.render_thumbnails self.active_bar = self.prog.metadata_bar @@ -151,9 +147,7 @@ def modal(self, context: Context, event: Event): context.window_manager.event_timer_remove(self._timer) bpy.app.timers.register(self.prog.end, first_interval=1) return {"FINISHED"} - elif event.value == "ESC" and utils.mouse_in_window( - context.window, event.mouse_x, event.mouse_y - ): + elif event.value == "ESC" and utils.mouse_in_window(context.window, event.mouse_x, event.mouse_y): self.prog.cancel = True elif self.prog.cancel: self._thread.join() @@ -180,9 +174,7 @@ def main( break asset = utils.Asset(bpy_asset) md_update.process_asset_metadata(asset, bpy_asset, lib) - asset.update_asset( - prefs.ensure_default_blender_version().path, debug=md_update.debug_scene - ) + asset.update_asset(prefs.ensure_default_blender_version().path, debug=md_update.debug_scene) self.metadata_progress = (i + 1) / len(selected_assets) self.update = True @@ -190,45 +182,44 @@ def main( self.start_icon = True self.update = True - blends = {} + blend_files_dict: dict[str, dict[str, list[tuple[str, str]]]] = {} for a in selected_assets: # if a.id_type=='OBJECT' or a.id_type=='COLLECTION' or a.id_type=='MATERIAL': if a.id_type in {"OBJECT", "COLLECTION", "MATERIAL"}: - blend_data = blends.get(a.full_library_path) - blends[a.full_library_path] = ( - blend_data + [(a.name, a.id_type)] - if blend_data - else [(a.name, a.id_type)] + exe = a.metadata.sh_get_blender_version.path + exe_data = blend_files_dict.get(exe) + if not exe_data: + exe_data = {} + blend_files_dict[exe] = exe_data + + blend_data = exe_data.get(a.full_library_path) + + blend_files_dict[a.full_library_path] = ( + blend_data + [(a.name, a.id_type)] if blend_data else [(a.name, a.id_type)] ) - lib_path = [ - a - for a in bpy.context.preferences.filepaths.asset_libraries - if a.name == library_name - ] + lib_path = [a for a in bpy.context.preferences.filepaths.asset_libraries if a.name == library_name] if lib_path: lib_path = lib_path[0].path prefs = utils.get_prefs() - utils.rerender_thumbnail( - paths=[b for b in blends.keys()], - directory=lib_path, - objects=[f for f in blends.values()], - shading=md_update.shading, - angle=utils.resolve_angle( - md_update.camera_angle, - md_update.flip_x, - md_update.flip_y, - md_update.flip_z, - ), - add_plane=prefs.add_ground_plane and not md_update.flip_z, - world_name=md_update.scene_lighting, - world_strength=md_update.world_strength, - padding=1 - md_update.padding, - rotate_world=md_update.rotate_world, - debug_scene=md_update.debug_scene, - op=self, - ) + for i, (exe, blend_files) in enumerate(blend_files_dict.items()): + utils.rerender_thumbnail( + paths=[b for b in blend_files.keys()], + directory=lib_path, + objects=[f for f in blend_files.values()], + shading=md_update.shading, + angle=utils.resolve_angle( + md_update.camera_angle, md_update.flip_x, md_update.flip_y, md_update.flip_z + ), + add_plane=prefs.add_ground_plane and not md_update.flip_z, + world_name=md_update.scene_lighting, + world_strength=md_update.world_strength, + padding=1 - md_update.padding, + rotate_world=md_update.rotate_world, + debug_scene=md_update.debug_scene, + op=self, + ) if md_update.reset_settings: md_update.reset() @@ -367,7 +358,7 @@ def poll(cls, context): @classmethod def description(cls, context, operator_properties): - return f"Rerender the thumbnail of the asset{'s' if len(context.selected_assets)>1 else ''}" + return f"Rerender the thumbnail of the asset{'s' if len(context.selected_assets) > 1 else ''}" def draw(self, context): self.draw_thumbnail_props(self.layout) @@ -383,9 +374,7 @@ def execute(self, context): if a.id_type in {"OBJECT", "COLLECTION", "MATERIAL"}: blend_data = blends.get(a.full_library_path) blends[a.full_library_path] = ( - blend_data + [(a.name, a.id_type)] - if blend_data - else [(a.name, a.id_type)] + blend_data + [(a.name, a.id_type)] if blend_data else [(a.name, a.id_type)] ) self.threads = [] @@ -404,9 +393,7 @@ def execute(self, context): directory=lib_path, objects=[f for f in blends.values()], shading=self.shading, - angle=utils.resolve_angle( - self.camera_angle, self.flip_x, self.flip_y, self.flip_z - ), + angle=utils.resolve_angle(self.camera_angle, self.flip_x, self.flip_y, self.flip_z), add_plane=prefs.add_ground_plane and not self.flip_z, world_name=self.scene_lighting, world_strength=self.world_strength, @@ -432,7 +419,7 @@ def execute(self, context): def modal(self, context, event): if event.type == "TIMER": - context.scene.sh_progress_t = f"Regenerating Thumbnails..." + context.scene.sh_progress_t = "Regenerating Thumbnails..." if context.area: context.area.tag_redraw() if not self.thread.is_alive(): @@ -479,6 +466,8 @@ def execute(self, context): asset = utils.Asset(context.asset) asset.icon_path = self.filepath + asset.update_asset() + prefs = utils.get_prefs() asset.update_asset(prefs.ensure_default_blender_version().path) diff --git a/ops/blender_exes_ops.py b/ops/blender_exes_ops.py index 92f6364..8be89ca 100644 --- a/ops/blender_exes_ops.py +++ b/ops/blender_exes_ops.py @@ -8,6 +8,8 @@ from .. import utils +from time import time + class SH_OT_GatherBlenderExes(Operator): bl_idname = "bkeeper.gather_blender_exes" @@ -20,14 +22,42 @@ def execute(self, context): prefs = utils.get_prefs() + print() for path in paths: p = Path(path) - for bexe in p.glob(f"**/{executable_name}"): + + t = time() + bexes = self.find_blender_executables(str(p), executable_name) + + if not bexes: + continue + + print(f"Search: {time() - t:.2f}s") + t = time() + + for bexe in bexes: prefs.add_blender_version( bexe.parent.name.replace("-", " ").title(), str(bexe) ) + print() return {"FINISHED"} + + def find_blender_executables(self, path: str, executable_name: str) -> list[Path]: + if not os.path.exists(path): + return [] + found_files = [] + + def scan_dir(directory): + with os.scandir(directory) as it: + for entry in it: + if entry.is_file() and entry.name == executable_name: + found_files.append(Path(entry.path)) + elif entry.is_dir(): + scan_dir(entry.path) + + scan_dir(path) + return found_files def _get_blender_paths(self) -> list[str]: system = platform.system() diff --git a/settings/asset.py b/settings/asset.py index 3e55302..5d6eb54 100644 --- a/settings/asset.py +++ b/settings/asset.py @@ -1,4 +1,5 @@ import uuid +from typing import TYPE_CHECKING import bpy from bpy.props import ( @@ -6,6 +7,7 @@ CollectionProperty, EnumProperty, IntProperty, + IntVectorProperty, PointerProperty, StringProperty, ) @@ -18,7 +20,10 @@ UIList, ) -from .. import hive_mind +from .. import hive_mind, utils + +if TYPE_CHECKING: + from ..ui import prefs def _has_context_asset(C: Context = None) -> bool: @@ -68,13 +73,10 @@ def check_is_dirty(self, context: Context, context_check: bool = None) -> None: return False # No asset to check asset = context.asset return len(asset.metadata.tags) != len(self.tags) or any( - orig_tag.name not in {new_tag.name for new_tag in self.tags} - for orig_tag in asset.metadata.tags + orig_tag.name not in {new_tag.name for new_tag in self.tags} for orig_tag in asset.metadata.tags ) - def set_is_dirty( - self, context: Context, value: bool, context_check: bool = None - ) -> None: + def set_is_dirty(self, context: Context, value: bool, context_check: bool = None) -> None: if context_check is None and not _has_context_asset(C=context): return False # No asset to check asset = context.asset @@ -90,9 +92,7 @@ def update_is_dirty(self, context: Context) -> None: context_check=True, ) - def new_tag( - self, name: str, context: Context, id: str = None, desc: str = None - ) -> SH_AssetTag: + def new_tag(self, name: str, context: Context, id: str = None, desc: str = None) -> SH_AssetTag: """ Create a new asset tag. @@ -186,19 +186,17 @@ def validate(self, context: Context) -> None: def is_dirty(self: AssetRepresentation) -> bool: - return any( - [ - self.sh_is_dirty_name, - self.sh_is_dirty_description, - self.sh_is_dirty_catalog, - self.sh_is_dirty_author, - self.sh_is_dirty_license, - self.sh_is_dirty_copyright, - self.sh_is_dirty_tags, - self.sh_is_dirty_created_blender_version, - # self.sh_is_dirty_icon, - ] - ) + return any([ + self.sh_is_dirty_name, + self.sh_is_dirty_description, + self.sh_is_dirty_catalog, + self.sh_is_dirty_author, + self.sh_is_dirty_license, + self.sh_is_dirty_copyright, + self.sh_is_dirty_tags, + self.sh_is_dirty_created_blender_version, + # self.sh_is_dirty_icon, + ]) classes = ( @@ -238,6 +236,124 @@ def set(self, value): default="", ) + AssetMetaData.sh_blend_version = IntVectorProperty( + name="Blender Version", + description="Blender version the asset was created in and/or should use for editing", + size=3, + default=(-1, -1, -1), + ) + AssetMetaData.sh_blend_version_str = StringProperty(default="Default") + AssetMetaData.sh_is_dirty_blend_version = BoolProperty() + + def _get_blend_version_items(self: "AssetMetaData", context: Context) -> list: + items = [ + ("Default", "Default", "Use the default Blender version", 0), + ] + + bv: "prefs.SH_BlenderVersion" + for i, bv in enumerate(utils.get_prefs().blender_versions): + items.append(( + bv.name, + f"{bv.display_name} (v{bv.version[0]}.{bv.version[1]}.{bv.version[2]})", + f"Use Blender version {bv.display_name}", + i + 1, + )) + return items + + def _update_blend_version(self: "AssetMetaData", context: Context) -> None: + self.sh_blend_version_str = self.sh_blend_version_enum + if self.sh_blend_version_enum == "Default": + self.sh_blend_version = [-1, -1, -1] + else: + bv = utils.get_prefs().blender_versions.get(self.sh_blend_version_enum) + self.sh_blend_version = bv.version + + AssetMetaData.sh_blend_version_enum = EnumProperty( + name="Blender Version", + description="Blender version the asset was created in and/or should use for editing", + items=_get_blend_version_items, + update=_update_blend_version, + ) + + def sh_get_blender_version(self: "AssetMetaData") -> str: + prefs = utils.get_prefs() + blender_versions = prefs.blender_versions + bv = blender_versions.get(self.sh_blend_version_enum) + + if not bv and self.sh_blend_version_str != "Default": + print("No blender version found for", self.sh_blend_version_enum) + bv = blender_versions.get(self.sh_blend_version_str) + if not bv and self.sh_blend_version != (-1, -1, -1): + print("No blender version found for", self.sh_blend_version_str) + bv = prefs.get_by_blender_version(self.sh_blend_version) + + if not bv and self.sh_blend_version_str == "Default" or self.sh_blend_version == (-1, -1, -1): + print("No blender version found for", self.sh_blend_version, ", using default") + bv = prefs.ensure_default_blender_version() + + return bv + + AssetMetaData.sh_get_blender_version = sh_get_blender_version + + AssetMetaData.sh_blend_version = IntVectorProperty( + name="Blender Version", + description="Blender version the asset was created in and/or should use for editing", + size=3, + default=(-1, -1, -1), + ) + AssetMetaData.sh_blend_version_str = StringProperty(default="Default") + AssetMetaData.sh_is_dirty_blend_version = BoolProperty() + + def _get_blend_version_items(self: "AssetMetaData", context: Context) -> list: + items = [ + ("Default", "Default", "Use the default Blender version", 0), + ] + + bv: "prefs.SH_BlenderVersion" + for i, bv in enumerate(utils.get_prefs().blender_versions): + items.append(( + bv.name, + f"{bv.display_name} (v{bv.version[0]}.{bv.version[1]}.{bv.version[2]})", + f"Use Blender version {bv.display_name}", + i + 1, + )) + return items + + def _update_blend_version(self: "AssetMetaData", context: Context) -> None: + self.sh_blend_version_str = self.sh_blend_version_enum + if self.sh_blend_version_enum == "Default": + self.sh_blend_version = [-1, -1, -1] + else: + bv = utils.get_prefs().blender_versions.get(self.sh_blend_version_enum) + self.sh_blend_version = bv.version + + AssetMetaData.sh_blend_version_enum = EnumProperty( + name="Blender Version", + description="Blender version the asset was created in and/or should use for editing", + items=_get_blend_version_items, + update=_update_blend_version, + ) + + def sh_get_blender_version(self: "AssetMetaData") -> str: + prefs = utils.get_prefs() + blender_versions = prefs.blender_versions + bv = blender_versions.get(self.sh_blend_version_enum) + + if not bv and self.sh_blend_version_str != "Default": + print("No blender version found for", self.sh_blend_version_enum) + bv = blender_versions.get(self.sh_blend_version_str) + if not bv and self.sh_blend_version != (-1, -1, -1): + print("No blender version found for", self.sh_blend_version_str) + bv = prefs.get_by_blender_version(self.sh_blend_version) + + if not bv and self.sh_blend_version_str == "Default" or self.sh_blend_version == (-1, -1, -1): + print("No blender version found for", self.sh_blend_version, ", using default") + bv = prefs.ensure_default_blender_version() + + return bv + + AssetMetaData.sh_get_blender_version = sh_get_blender_version + def _get_active_asset_name(self: "AssetMetaData"): item = self.get("_name", None) if item is not None: @@ -259,6 +375,7 @@ def _set_active_asset_name(self, value: str): get=_get_active_asset_name, set=_set_active_asset_name, ) + AssetMetaData.sh_is_dirty_description = BoolProperty() AssetMetaData.sh_description = StringProperty( name="Description", @@ -277,9 +394,7 @@ def _get_asset_catalog(self: AssetMetaData) -> dict: if item: return item.get("id_int", 444) - item = hive_mind.get_catalog_by_name( - self.catalog_simple_name, is_catalog_simple_name=True - ) + item = hive_mind.get_catalog_by_name(self.catalog_simple_name, is_catalog_simple_name=True) return item.get("id_int", 444) if item else 444 @@ -295,9 +410,7 @@ def _set_asset_catalog(self: AssetMetaData, value) -> dict: else: set_cat_name = self.catalog_simple_name - self.sh_is_dirty_catalog = not any( - (item["id"] == self.catalog_id, item["name"] == set_cat_name) - ) + self.sh_is_dirty_catalog = not any((item["id"] == self.catalog_id, item["name"] == set_cat_name)) AssetMetaData.sh_is_dirty_catalog = BoolProperty() AssetMetaData.sh_catalog = EnumProperty( @@ -325,6 +438,7 @@ def _set_sh_catalog_custom(self, value: str) -> dict: get=_get_sh_catalog_custom, set=_set_sh_catalog_custom, ) + AssetMetaData.sh_is_dirty_author = BoolProperty() AssetMetaData.sh_author = StringProperty( name="Author", @@ -353,9 +467,7 @@ def _set_asset_license(self: AssetMetaData, value) -> dict: None, ) - self.sh_is_dirty_license = not any( - (item["id"] == self.license, item["name"] == self.license) - ) + self.sh_is_dirty_license = not any((item["id"] == self.license, item["name"] == self.license)) AssetMetaData.sh_is_dirty_license = BoolProperty() AssetMetaData.sh_license = EnumProperty( @@ -378,12 +490,14 @@ def _set_asset_license(self: AssetMetaData, value) -> dict: get=_get_asset_data("copyright"), set=_set_asset_data("copyright"), ) + AssetMetaData.sh_is_dirty_created_blender_version = BoolProperty() AssetMetaData.sh_created_blender_version = StringProperty( name="Created Blender Version", description="Blender version the asset was created in", default=bpy.app.version_string, ) + AssetMetaData.sh_is_dirty_tags = BoolProperty() AssetMetaData.sh_tags = PointerProperty( name="Tags", diff --git a/ui/asset_browser.py b/ui/asset_browser.py index 791705f..150e979 100644 --- a/ui/asset_browser.py +++ b/ui/asset_browser.py @@ -32,10 +32,7 @@ class SH_PT_AssetSettings(asset_utils.AssetMetaDataPanel, Panel): @classmethod def poll(cls, context: Context) -> bool: - return ( - polls.is_asset_browser(context) - and context.scene.superhive.library_mode == "SUPERHIVE" - ) + return polls.is_asset_browser(context) and context.scene.superhive.library_mode == "SUPERHIVE" def draw_header(self, context: Context) -> None: layout = self.layout @@ -49,9 +46,7 @@ def draw_header(self, context: Context) -> None: if asset and asset.metadata.sh_is_dirty(): row = layout.split(factor=0.8) - row.operator( - "bkeeper.update_asset", text="*Unsaved Changes", icon="FILE_TICK" - ) + row.operator("bkeeper.update_asset", text="*Unsaved Changes", icon="FILE_TICK") r = row.row() r.alignment = "RIGHT" @@ -110,7 +105,6 @@ def set_is_dirty_text(prop: str, text: str = None): def display_metadata( layout: UILayout, - data, prop: str, orig_prop: str, orig_value: str, @@ -119,19 +113,15 @@ def display_metadata( row = layout.row(align=True) row.prop(asset.metadata, prop, text=text or set_is_dirty_text(orig_prop)) if getattr(asset.metadata, f"sh_is_dirty_{orig_prop}"): - op = row.operator( - "bkeeper.reset_asset_metadata_property", icon="X", text="" - ) + op = row.operator("bkeeper.reset_asset_metadata_property", icon="X", text="") op.property = prop op.original_value = orig_value - display_metadata(layout, asset, "sh_name", "name", asset.name) - display_metadata( - layout, asset, "sh_description", "description", asset.metadata.description - ) - display_metadata(layout, asset, "sh_author", "author", asset.metadata.author) + display_metadata(layout, "sh_name", "name", asset.name) + display_metadata(layout, "sh_description", "description", asset.metadata.description) + display_metadata(layout, "sh_author", "author", asset.metadata.author) col = layout.column(align=True) - display_metadata(col, asset, "sh_license", "license", asset.metadata.license) + display_metadata(col, "sh_license", "license", asset.metadata.license) if asset.metadata.sh_license == "CUSTOM": display_metadata( col, @@ -142,7 +132,7 @@ def display_metadata( text="Custom License", ) col = layout.column(align=True) - display_metadata(col, asset, "sh_catalog", "catalog", asset.metadata.catalog_id) + display_metadata(col, "sh_catalog", "catalog", asset.metadata.catalog_id) if asset.metadata.sh_catalog == "CUSTOM": row = col.row(align=True) row.prop( @@ -151,18 +141,13 @@ def display_metadata( text=f"Custom Catalog{'*' if asset.metadata.sh_is_dirty_catalog_custom else ''}", ) if asset.metadata.sh_is_dirty_catalog_custom: - op = row.operator( - "bkeeper.reset_asset_metadata_property", icon="X", text="" - ) + op = row.operator("bkeeper.reset_asset_metadata_property", icon="X", text="") op.property = "sh_catalog_custom" op.original_value = "" - display_metadata( - layout, asset, "sh_copyright", "copyright", asset.metadata.copyright - ) + display_metadata(layout, "sh_copyright", "copyright", asset.metadata.copyright) + display_metadata(layout, "sh_blend_version_enum", "blend_version", asset.metadata.sh_blend_version_str) - layout.label( - text="Tags*:" if asset.metadata.sh_is_dirty_tags else "Tags:", icon="TAG" - ) + layout.label(text="Tags*:" if asset.metadata.sh_is_dirty_tags else "Tags:", icon="TAG") row = layout.row() row.template_list( "SH_UL_TagList", @@ -210,9 +195,7 @@ def draw_multiple_assets(self, context: Context, layout: UILayout): row = col.row(align=True) row.operator("bkeeper.batch_update_assets_from_scene") - row.prop( - scene_sets.metadata_update, "reset_settings", text="", icon="FILE_REFRESH" - ) + row.prop(scene_sets.metadata_update, "reset_settings", text="", icon="FILE_REFRESH") class SH_PT_LibrarySettings(Panel): @@ -226,10 +209,7 @@ class SH_PT_LibrarySettings(Panel): @classmethod def poll(cls, context): - return ( - polls.is_asset_browser(context) - and context.scene.superhive.library_mode == "SUPERHIVE" - ) + return polls.is_asset_browser(context) and context.scene.superhive.library_mode == "SUPERHIVE" def draw(self, context): layout: UILayout = self.layout diff --git a/ui/prefs.py b/ui/prefs.py index 7247978..26e1cb1 100644 --- a/ui/prefs.py +++ b/ui/prefs.py @@ -1,4 +1,5 @@ import os +import subprocess from pathlib import Path import bpy @@ -7,6 +8,7 @@ CollectionProperty, EnumProperty, IntProperty, + IntVectorProperty, StringProperty, ) from bpy.types import AddonPreferences, PropertyGroup, UILayout, UIList @@ -36,6 +38,8 @@ def draw_item( row.alignment = "LEFT" row.prop(item, "display_name", icon="BLENDER", text="", emboss=False) + row.label(text=f"v{item.version[0]}.{item.version[1]}.{item.version[2]}") + row = layout.row() row.active = False row.label(text=item.path) @@ -53,6 +57,12 @@ class SH_BlenderVersion(PropertyGroup): description="The name to display for the Blender version", ) + version: IntVectorProperty( + name="Version", + description="The version of Blender", + size=3, + ) + def _update_is_default(self, context): if self.is_default: for bv in utils.get_prefs().blender_versions: @@ -70,6 +80,12 @@ def _update_is_default(self, context): def data(self) -> "SH_AddonPreferences": return utils.get_prefs() + def load_version(self): + proc = subprocess.run([self.path, "-v"], stdout=subprocess.PIPE) + vers = proc.stdout.decode("utf-8").splitlines()[0].split()[1] + for i, v in enumerate(vers.split(".")): + self.version[i] = int(v) + class SH_AddonPreferences(AddonPreferences, scene.RenderThumbnailProps): bl_idname = base_package @@ -119,7 +135,8 @@ class SH_AddonPreferences(AddonPreferences, scene.RenderThumbnailProps): @property def active_blender_version(self) -> SH_BlenderVersion: - return self.blender_versions[self.active_blender_version_index] + if self.blender_versions: + return self.blender_versions[self.active_blender_version_index] @property def default_blender_version(self) -> SH_BlenderVersion: @@ -130,6 +147,7 @@ def add_blender_version(self, name: str, path: str): bv.name = name bv.display_name = name bv.path = path + bv.load_version() return bv def remove_blender_version(self, item: SH_BlenderVersion | str | int): @@ -152,6 +170,20 @@ def ensure_default_blender_version(self): return self.blender_versions return self.default_blender_version + def get_by_blender_version( + self, name: str, version: tuple[int, int, int], get_exact_version=False + ) -> SH_BlenderVersion: + """Get the Blender version by name or if name doesn't exist get it by the version""" + bv = self.blender_versions.get(name) + if not bv or bv.version != tuple(version): + bv = next((bv for bv in self.blender_versions if tuple(bv.version) == tuple(version)), None) + + if get_exact_version: + return bv + + bv = next((bv for bv in self.blender_versions if tuple(bv.version[:2]) == tuple(version[:2])), None) + return bv + def draw(self, context): layout = self.layout @@ -166,9 +198,7 @@ def draw(self, context): col = body.column(align=True) col.scale_y = 0.75 col.label(text="Location: Asset Browser > Side Panel > Superhive Tab") - col.label( - text=" " * 8 + "Ensure new dropdown on the left side of the header" - ) + col.label(text=" " * 8 + "Ensure new dropdown on the left side of the header") col.label(text=" " * 8 + "is set to 'Superhive' instead of 'Blender'") layout.separator() @@ -177,31 +207,15 @@ def draw(self, context): col.scale_y = 0.75 col.label(text="Add Assets to Libraries:") col.label(text=" " * 8 + "From Asset Browser") - col.label( - text=" " * 16 - + "Select existing assets. Then right-click to bring up menu." - ) + col.label(text=" " * 16 + "Select existing assets. Then right-click to bring up menu.") col.label(text=" " * 16 + "Notice the 'SuperHive' section.") - col.label( - text=" " * 16 - + "Click 'Add to Library'. Select library or choose '+ New'" - ) + col.label(text=" " * 16 + "Click 'Add to Library'. Select library or choose '+ New'") col.label(text=" " * 16 + "in order to create a new library") col.label(text=" " * 8 + "From Outliner/3D View") - col.label( - text=" " * 16 + "Select the object(s) you want to add. Then right-click" - ) - col.label( - text=" " * 16 + "to bring up menu. Notice the 'SuperHive' section." - ) - col.label( - text=" " * 16 - + "Click 'Add to Library'. Select library or choose '+ New'" - ) - col.label( - text=" " * 16 - + "to create a new library. Items will be marked as an asset and added." - ) + col.label(text=" " * 16 + "Select the object(s) you want to add. Then right-click") + col.label(text=" " * 16 + "to bring up menu. Notice the 'SuperHive' section.") + col.label(text=" " * 16 + "Click 'Add to Library'. Select library or choose '+ New'") + col.label(text=" " * 16 + "to create a new library. Items will be marked as an asset and added.") layout.label(text="Metadata Defaults:") layout.prop(self, "default_author_name") diff --git a/utils.py b/utils.py index 0ed90ab..7e47040 100644 --- a/utils.py +++ b/utils.py @@ -17,7 +17,7 @@ if TYPE_CHECKING: from .ops import asset_ops, export_library, import_from_directory from .settings import asset as asset_settings - from .ui.prefs import SH_AddonPreferences + from .ui.prefs import SH_AddonPreferences, SH_BlenderVersion ASSET_TYPES_TO_ID_TYPES = { @@ -365,9 +365,7 @@ def __init__(self, dir: Path, is_new=False) -> None: self.path = dir / "blender_assets.cats.txt" break if not found_new: - raise FileNotFoundError( - f"Catalogs file not found in directory: {self.path.parent}" - ) + raise FileNotFoundError(f"Catalogs file not found in directory: {self.path.parent}") self.load_catalogs() @@ -420,9 +418,7 @@ def delete_file(self) -> None: if self.exists(): self.path.unlink() - def add_catalog( - self, name: str, id: str = None, path: str = None, auto_place=False - ) -> Catalog: + def add_catalog(self, name: str, id: str = None, path: str = None, auto_place=False) -> Catalog: """ Add a catalog at the root level or under a parent catalog. @@ -647,12 +643,14 @@ def __init__(self, asset: AssetRepresentation) -> None: self.bpy_tags = [tag.name for tag in asset.metadata.tags] self.icon_path = None - def update_asset(self, blender_exe: str, debug: bool = False) -> None: + self.blender_exe: "SH_BlenderVersion" = asset.metadata.sh_get_blender_version() + + def update_asset(self, debug: bool = False) -> None: """Open asset's blend file and update the asset's metadata.""" python_file = Path(__file__).parent / "stand_alone_scripts" / "update_asset.py" proc: subprocess.CompletedProcess = subprocess.run( [ - blender_exe, + self.blender_exe.path, "-b", "--factory-startup", str(self.blend_path), @@ -679,9 +677,7 @@ def update_asset(self, blender_exe: str, debug: bool = False) -> None: print("".center(100, "-")) text = proc.stdout.decode() text.splitlines() - new_text = "\n".join( - line for line in text.splitlines() if line.startswith("|") - ) + new_text = "\n".join(line for line in text.splitlines() if line.startswith("|")) print(new_text) print("".center(100, "-")) print() @@ -727,9 +723,7 @@ def reset_metadata(self, context: Context) -> None: sh_tags.new_tag(tag.name, context) self.orig_asset.metadata.sh_is_dirty_tags = False - def rerender_thumbnail( - self, path, directory, objects, shading, angle="X", add_plane=False - ): + def rerender_thumbnail(self, path, directory, objects, shading, angle="X", add_plane=False): prefs = get_prefs() cmd = [bpy.app.binary_path] # cmd.append("--background") @@ -738,11 +732,7 @@ def rerender_thumbnail( cmd.append("--python") # cmd.append(os.path.join(os.path.dirname( # os.path.abspath(__file__)), "rerender_thumbnails.py")) - cmd.append( - str( - Path(__file__).parent / "stand_alone_scripts" / "rerender_thumbnails.py" - ) - ) + cmd.append(str(Path(__file__).parent / "stand_alone_scripts" / "rerender_thumbnails.py")) cmd.append("--") cmd.append(":--separator--:".join(path)) names = [] @@ -772,9 +762,7 @@ def rerender_thumbnail( def save_out_preview(self, directory: Path) -> None: """Save out the preview image of the asset.""" - python_file = ( - Path(__file__).parent / "stand_alone_scripts" / "save_out_previews.py" - ) + python_file = Path(__file__).parent / "stand_alone_scripts" / "save_out_previews.py" args = [ bpy.app.binary_path, @@ -845,8 +833,7 @@ def __init__( area for window in context.window_manager.windows for area in window.screen.areas - if area.type == "FILE_BROWSER" - and asset_utils.SpaceAssetInfo.is_asset_browser(area.spaces.active) + if area.type == "FILE_BROWSER" and asset_utils.SpaceAssetInfo.is_asset_browser(area.spaces.active) ), None, ) @@ -868,9 +855,7 @@ def __init__( def get_context(self) -> Context: if not self.context: - raise ValueError( - "Context not set. Please set `context` before calling this method." - ) + raise ValueError("Context not set. Please set `context` before calling this method.") return self.context def load_catalogs(self): @@ -897,9 +882,7 @@ def open_catalogs_file(self) -> CatalogsFile: @classmethod def create_bpy_library(cls, name: str, path: str) -> UserAssetLibrary: """Create a new UserAssetLibrary in Blender.""" - return bpy.context.preferences.filepaths.asset_libraries.new( - name=name, directory=path - ) + return bpy.context.preferences.filepaths.asset_libraries.new(name=name, directory=path) @classmethod def create_new_library( @@ -934,11 +917,7 @@ def save_repository_prefs(cls, name: str, path: str): if not prefs_blend.exists(): return - python_file = ( - Path(__file__).parent - / "stand_alone_scripts" - / "add_repository_to_userpref.py" - ) + python_file = Path(__file__).parent / "stand_alone_scripts" / "add_repository_to_userpref.py" args = [ bpy.app.binary_path, @@ -954,17 +933,14 @@ def save_repository_prefs(cls, name: str, path: str): subprocess.run(args) -def get_active_bpy_library_from_context( - context: Context, area: Area = None -) -> UserAssetLibrary: +def get_active_bpy_library_from_context(context: Context, area: Area = None) -> UserAssetLibrary: if not area: area = next( ( area for window in context.window_manager.windows for area in window.screen.areas - if area.type == "FILE_BROWSER" - and asset_utils.SpaceAssetInfo.is_asset_browser(area.spaces.active) + if area.type == "FILE_BROWSER" and asset_utils.SpaceAssetInfo.is_asset_browser(area.spaces.active) ), None, ) @@ -976,21 +952,15 @@ def get_active_bpy_library_from_context( return context.preferences.filepaths.asset_libraries.get(lib_name) -def from_name( - name: str, context: Context = None, load_assets=False, load_catalogs=False -) -> AssetLibrary: +def from_name(name: str, context: Context = None, load_assets=False, load_catalogs=False) -> AssetLibrary: """Gets a library by name and returns an AssetLibrary object.""" lib = bpy.context.preferences.filepaths.asset_libraries.get(name) if not lib: raise ValueError(f"Library with name '{name}' not found.") - return AssetLibrary( - lib, context=context, load_assets=load_assets, load_catalogs=load_catalogs - ) + return AssetLibrary(lib, context=context, load_assets=load_assets, load_catalogs=load_catalogs) -def from_active( - context: Context, area: Area = None, load_assets=False, load_catalogs=False -) -> AssetLibrary: +def from_active(context: Context, area: Area = None, load_assets=False, load_catalogs=False) -> AssetLibrary: """Gets the active library from the UI context and returns an AssetLibrary object.""" return AssetLibrary( get_active_bpy_library_from_context(context, area=area), @@ -1112,9 +1082,10 @@ def get_prefs() -> "SH_AddonPreferences": def rerender_thumbnail( + blender_exe: str, paths: list[str], directory: str, - objects: list[tuple[str, str]], + objects: list[tuple[str, str, str]], shading: str, angle: str = "X", add_plane: bool = False, @@ -1129,6 +1100,7 @@ def rerender_thumbnail( Rerenders the thumbnail using the specified parameters. Parameters: + - blender_exe (str): The path to the Blender executable. - path (str): The path of the blend files. - directory (str): The directory where the thumbnail will be saved. - objects: A list of tuples containing the object name and type. @@ -1151,9 +1123,7 @@ def rerender_thumbnail( print("*" * 115) print("Thread Starting".center(100, "*")) print("*" * 115) - python_file = ( - Path(__file__).parent / "stand_alone_scripts" / "rerender_thumbnails.py" - ) + python_file = Path(__file__).parent / "stand_alone_scripts" / "rerender_thumbnails.py" names = [] types = [] @@ -1181,7 +1151,7 @@ def rerender_thumbnail( op.label = "Setting Up Scenes" op.update = True args = [ - bpy.app.binary_path, + blender_exe or bpy.app.binary_path, "-b", "--factory-startup", "-P", @@ -1247,14 +1217,8 @@ def rerender_thumbnail( for i, tbp in enumerate(thumbnail_blends): orig_stem = tbp.stem.split("=+=")[0] orig_blend_path = tbp.with_stem(orig_stem) - thumbnail_path = ( - orig_blend_path.parent - / f"{tbp.stem.replace('_thumbnail_copy', '')}_thumbnail_1.png" - ) - thumbnail_path_for_terminal = ( - orig_blend_path.parent - / f"{tbp.stem.replace('_thumbnail_copy', '')}_thumbnail_#" - ) + thumbnail_path = orig_blend_path.parent / f"{tbp.stem.replace('_thumbnail_copy', '')}_thumbnail_1.png" + thumbnail_path_for_terminal = orig_blend_path.parent / f"{tbp.stem.replace('_thumbnail_copy', '')}_thumbnail_#" args = [ bpy.app.binary_path, "-b", @@ -1274,9 +1238,7 @@ def rerender_thumbnail( print(" - exists", tbp.exists()) print("CMD:", " ".join(args)) try: - proc: subprocess.CompletedProcess = subprocess.run( - args, stdout=subprocess.PIPE, check=True - ) + proc: subprocess.CompletedProcess = subprocess.run(args, stdout=subprocess.PIPE, check=True) proc.returncode except subprocess.CalledProcessError as e: print(f"- Error: {e}") @@ -1422,10 +1384,7 @@ def mouse_in_window(window: Window, x, y) -> bool: Returns: bool: True if the mouse is within the window boundaries, False otherwise. """ - return ( - window.x <= x <= window.x + window.width - and window.y <= y <= window.y + window.height - ) + return window.x <= x <= window.x + window.width and window.y <= y <= window.y + window.height def pack_files(blend_file: Path): @@ -1501,9 +1460,7 @@ def export_helper( str(destination_dir), ] - proc = subprocess.Popen( - args, stderr=subprocess.STDOUT, stdout=subprocess.PIPE, text=True - ) + proc = subprocess.Popen(args, stderr=subprocess.STDOUT, stdout=subprocess.PIPE, text=True) # proc = subprocess.Popen(args) if op: @@ -1552,9 +1509,7 @@ def export_helper( print(f"Output: {proc.stdout.decode()}") -def update_asset_browser_areas( - context: Context = None, tag_redraw=True, update_library=True -): +def update_asset_browser_areas(context: Context = None, tag_redraw=True, update_library=True): C = context or bpy.context for area in C.screen.areas: @@ -1613,15 +1568,9 @@ def mark_assets_in_blend( Key: Blend File Path Value: Tuple of (Asset Name, Asset Type) """ - python_file = ( - Path(__file__).parent / "stand_alone_scripts" / "mark_assets_in_blend_file.py" - ) + python_file = Path(__file__).parent / "stand_alone_scripts" / "mark_assets_in_blend_file.py" - blends = ( - list(Path(directory).rglob("**/*.blend")) - if recursive - else list(Path(directory).glob("*.blend")) - ) + blends = list(Path(directory).rglob("**/*.blend")) if recursive else list(Path(directory).glob("*.blend")) tags_to_add = [] for tag_name, tag_bool in zip(hive_mind.TAGS_DICT.keys(), tags):