Files
blender-portable-repo/extensions/user_default/blenderkit/unpack_asset_bg.py
T
2026-03-17 14:30:01 -06:00

200 lines
7.3 KiB
Python

# ##### 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 #####
import json
import os
import sys
import traceback
import bpy
def get_texture_filepath(tex_dir_path, image, resolution="blend"):
if len(image.packed_files) > 0:
path = image.packed_files[0].filepath
else:
path = image.filepath
# backslashes needs to be replaced because bpy.path.basename(path)
# does not work on Mac for Windows paths
path = path.replace("\\", "/")
image_file_name = bpy.path.basename(path)
if image_file_name == "":
image_file_name = image.name.split(".")[0]
# check if there is allready an image with same name and thus also assigned path
# (can happen easily with genearted tex sets and more materials)
file_path_original = os.path.join(tex_dir_path, image_file_name)
file_path_final = file_path_original
i = 0
done = False
while not done:
is_solo = True
for image1 in bpy.data.images:
if image != image1 and image1.filepath == file_path_final:
is_solo = False
fpleft, fpext = os.path.splitext(file_path_original)
file_path_final = fpleft + str(i).zfill(3) + fpext
i += 1
if is_solo:
done = True
return file_path_final
def get_resolution_from_file_path(file_path):
possible_resolutions = {
"_0_5K_": "resolution_0_5K",
"_1K_": "resolution_1K",
"_2K_": "resolution_2K",
"_4K_": "resolution_4K",
"_8K_": "resolution_8K",
}
for res in possible_resolutions:
if res in file_path:
return possible_resolutions[res]
return "blend"
def unpack_asset(data):
print("🗃️ unpacking asset")
asset_data = data["asset_data"]
resolution = get_resolution_from_file_path(bpy.data.filepath)
# TODO - passing resolution inside asset data might not be the best solution
tex_dir_path = paths.get_texture_directory(asset_data, resolution=resolution)
tex_dir_abs = bpy.path.abspath(tex_dir_path)
if not os.path.exists(tex_dir_abs):
try:
os.mkdir(tex_dir_abs)
except Exception as e:
traceback.print_exc()
bpy.data.use_autopack = False
for image in bpy.data.images:
if image.name == "Render Result":
continue # skip rendered images
# suffix = paths.resolution_suffix(data['suffix'])
fp = get_texture_filepath(tex_dir_path, image, resolution=resolution)
print(f"🖼️ unpacking file: {image.name} - {image.filepath}, {fp}")
for pf in image.packed_files:
pf.filepath = fp # bpy.path.abspath(fp)
image.filepath = fp # bpy.path.abspath(fp)
image.filepath_raw = fp # bpy.path.abspath(fp)
# image.save()
if len(image.packed_files) > 0:
# image.unpack(method='REMOVE')
image.unpack(method="WRITE_ORIGINAL")
# mark asset browser asset
data_block = None
if asset_data["assetType"] in ("model", "printable"):
for ob in bpy.data.objects:
if ob.parent is None and ob in bpy.context.visible_objects:
if bpy.app.version >= (3, 0, 0):
ob.asset_mark()
# for c in bpy.data.collections:
# if c.get('asset_data') is not None:
# if bpy.app.version >= (3, 0, 0):
# c.asset_mark()
# data_block = c
elif asset_data["assetType"] == "material":
for m in bpy.data.materials:
if bpy.app.version >= (3, 0, 0):
m.asset_mark()
data_block = m
elif asset_data["assetType"] == "scene":
if bpy.app.version >= (3, 0, 0):
bpy.context.scene.asset_mark()
elif asset_data["assetType"] == "brush":
for b in bpy.data.brushes:
if b.get("asset_data") is not None:
if bpy.app.version >= (3, 0, 0):
b.asset_mark()
data_block = b
if bpy.app.version >= (3, 0, 0) and data_block is not None:
tags = data_block.asset_data.tags
for t in tags:
tags.remove(t)
tags.new("description: " + asset_data.get("description", ""))
tags.new("tags: " + ",".join(asset_data.get("tags", [])))
# if this isn't here, blender crashes when saving file.
if bpy.app.version >= (3, 0, 0):
bpy.context.preferences.filepaths.file_preview_type = "NONE"
bpy.ops.wm.save_as_mainfile(filepath=bpy.data.filepath, compress=False)
# now try to delete the .blend1 file
try:
os.remove(bpy.data.filepath + "1")
except Exception as e:
traceback.print_exc()
bpy.ops.wm.quit_blender()
sys.exit()
def patch_imports(addon_module_name: str):
"""Patch the python configuration, so the relative imports work as expected. There are few problems to fix:
1. Script is not recognized as module which would break at relative import. We need to set __package__ = "blenderkit" for legacy addon.
Or __package__ = "bl_ext.user_default.blenderkit"/"bl_ext.blenderkit_com.blenderkit_com". Otherwise we would see:
from . import paths
ImportError: attempted relative import with no known parent package
2. External repository (e.g. blenderkit_com) is not available as we start with --factory-startup, we need to enable it.
We can add it as LOCAL repo as the add-on is installed and we do not care about updates or anything in this BG script. Otherwise we would see:
from . import paths
ModuleNotFoundError: No module named 'bl_ext.blenderkit_com'; 'bl_ext' is not a package
"""
print(f"- Setting __package__ = '{addon_module_name}'")
global __package__
__package__ = addon_module_name
if bpy.app.version < (4, 2, 0):
print(
f"- Skipping, Blender version {bpy.app.version} < (4,2,0), no need to handle repositories"
)
return
parts = addon_module_name.split(".")
if len(parts) != 3:
print("- Skipping, addon_module_name does not contain 3 parts")
return
bpy.ops.preferences.extension_repo_add( # type: ignore[attr-defined]
name=parts[1], type="LOCAL"
) # Local is enough
print(f"- Local repository {parts[1]} added")
if __name__ == "__main__":
# args order must match the order in blenderkit/client/download.go:UnpackAsset()!
json_path = sys.argv[-2]
patch_imports(
sys.argv[-1]
) # will be something like: "bl_ext.user_default.blenderkit" or "bl_ext.blenderkit_com.blenderkit", or just "blenderkit" on Blender < 4.2
from . import paths
with open(json_path, "r", encoding="utf-8") as f:
data = json.load(f)
unpack_asset(data)