2025-07-01
This commit is contained in:
@@ -0,0 +1,199 @@
|
||||
# ##### 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)
|
||||
Reference in New Issue
Block a user