#!/usr/bin/env python # -*- coding: utf-8 -*- """ Substance Painter API 插件 通过 Python API 自动添加项目库 此文件会被复制到临时目录,在 SP 启动时自动执行 """ import os import sys def setup_project_library_via_registry(library_name, library_path): """通过注册表设置项目库(Windows) Args: library_name: 库名称 library_path: 库路径 Returns: bool: 是否成功 """ try: import winreg import re # SP 库名称规则:只能包含小写字母、数字、下划线和连字符 safe_library_name = library_name.lower().replace(' ', '_') safe_library_name = re.sub(r'[^a-z0-9_-]', '', safe_library_name) print(f"[NexusLauncher] Setting up library via registry: {library_name}") if safe_library_name != library_name: print(f"[NexusLauncher] Normalized name: {safe_library_name}") print(f"[NexusLauncher] Path: {library_path}") # 检查路径是否存在 if not os.path.exists(library_path): print(f"[NexusLauncher] Error: Library path does not exist: {library_path}") return False # 规范化路径 normalized_path = library_path.replace('\\', '/') # 注册表路径 registry_key_name = r"SOFTWARE\Adobe\Adobe Substance 3D Painter\Shelf\pathInfos" # 连接到注册表 reg_connection = winreg.ConnectRegistry(None, winreg.HKEY_CURRENT_USER) try: # 打开父键 key = winreg.OpenKey(reg_connection, registry_key_name, 0, winreg.KEY_READ) except FileNotFoundError: print(f"[NexusLauncher] Registry key not found. Please open SP settings once to create it.") return False # 检查是否已存在同名库 sub_key_count = winreg.QueryInfoKey(key)[0] shelf_exists = False shelf_number = 0 for x in range(sub_key_count): sub_key_name = winreg.EnumKey(key, x) shelf_number = max(shelf_number, int(sub_key_name)) sub_key = winreg.OpenKey(reg_connection, registry_key_name + "\\" + sub_key_name, 0, winreg.KEY_READ) try: existing_name = winreg.QueryValueEx(sub_key, "name")[0] existing_path = winreg.QueryValueEx(sub_key, "path")[0] if existing_name == safe_library_name: print(f"[NexusLauncher] Library already exists: {existing_name} at {existing_path}") shelf_exists = True winreg.CloseKey(sub_key) break finally: winreg.CloseKey(sub_key) if not shelf_exists: # 添加新库 shelf_number += 1 print(f"[NexusLauncher] Adding new library with ID: {shelf_number}") # 创建新键 new_key = winreg.CreateKey(key, str(shelf_number)) winreg.SetValueEx(new_key, "disabled", 0, winreg.REG_SZ, "false") winreg.SetValueEx(new_key, "name", 0, winreg.REG_SZ, safe_library_name) winreg.SetValueEx(new_key, "path", 0, winreg.REG_SZ, normalized_path) winreg.CloseKey(new_key) # 更新计数 try: count = winreg.QueryValueEx(key, "size")[0] new_count = count + 1 except: # 如果读取失败,使用当前最大的 shelf_number + 1 new_count = shelf_number + 1 winreg.CloseKey(key) key = winreg.OpenKeyEx(reg_connection, registry_key_name, 0, winreg.KEY_SET_VALUE) winreg.SetValueEx(key, "size", 0, winreg.REG_DWORD, new_count) print(f"[NexusLauncher] Updated size to: {new_count}") print(f"[NexusLauncher] ✓ Library added to registry successfully") # 设置为默认库(writableShelf) try: shelf_key = r"SOFTWARE\Adobe\Adobe Substance 3D Painter\Shelf" default_key = winreg.OpenKeyEx(reg_connection, shelf_key, 0, winreg.KEY_SET_VALUE) winreg.SetValueEx(default_key, "writableShelf", 0, winreg.REG_SZ, safe_library_name) winreg.CloseKey(default_key) print(f"[NexusLauncher] ✓ Set as default library (writableShelf)") except Exception as e: print(f"[NexusLauncher] Warning: Could not set as default: {e}") winreg.CloseKey(key) else: winreg.CloseKey(key) print(f"[NexusLauncher] ========================================") print(f"[NexusLauncher] ✓ Library setup complete") print(f"[NexusLauncher] Note: Restart SP to see the changes") print(f"[NexusLauncher] ========================================") return True except Exception as e: print(f"[NexusLauncher] Error: {e}") import traceback traceback.print_exc() return False def setup_project_library(library_name, library_path, set_as_default=True): """设置项目库 Args: library_name: 库名称 library_path: 库路径 set_as_default: 是否设置为默认库 Returns: bool: 是否成功 """ try: # 延迟导入,因为只有在 SP 中才能导入 import substance_painter.resource # SP 库名称规则:只能包含小写字母、数字、下划线和连字符 # 将名称转换为小写并替换无效字符 safe_library_name = library_name.lower().replace(' ', '_') # 移除其他无效字符 import re safe_library_name = re.sub(r'[^a-z0-9_-]', '', safe_library_name) print(f"[NexusLauncher] Setting up library: {library_name}") if safe_library_name != library_name: print(f"[NexusLauncher] Normalized name: {safe_library_name} (SP naming rules)") print(f"[NexusLauncher] Path: {library_path}") # 检查路径是否存在 if not os.path.exists(library_path): print(f"[NexusLauncher] Error: Library path does not exist: {library_path}") return False # 获取所有现有的 Shelf existing_shelves = substance_painter.resource.Shelves.all() print(f"[NexusLauncher] Found {len(existing_shelves)} existing shelves") # 检查是否已存在同名或同路径的库 shelf_exists = False for shelf in existing_shelves: shelf_name = shelf.name() shelf_path = shelf.path() print(f"[NexusLauncher] Existing shelf: {shelf_name} -> {shelf_path}") # 规范化路径进行比较 from os.path import normpath normalized_shelf_path = normpath(shelf_path).lower() normalized_library_path = normpath(library_path).lower() if shelf_name == safe_library_name or normalized_shelf_path == normalized_library_path: print(f"[NexusLauncher] Library already exists: {shelf_name} at {shelf_path}") shelf_exists = True existing_shelf = shelf break if not shelf_exists: # 添加新库 print(f"[NexusLauncher] Adding new library...") try: # 探索可用的 API 方法 print(f"[NexusLauncher] Available Shelf methods: {[m for m in dir(substance_painter.resource.Shelf) if not m.startswith('_')]}") print(f"[NexusLauncher] Available Shelves methods: {[m for m in dir(substance_painter.resource.Shelves) if not m.startswith('_')]}") # 检查是否有项目打开(添加库需要关闭项目) try: import substance_painter.project if substance_painter.project.is_open(): print(f"[NexusLauncher] ERROR: A project is currently open!") print(f"[NexusLauncher] According to SP API docs, no project should be open when adding shelves") print(f"[NexusLauncher] Please close the project and try again") print(f"[NexusLauncher] Or add the library manually: Edit -> Settings -> Libraries") return False else: print(f"[NexusLauncher] ✓ No project is open, safe to add shelf") except Exception as e: print(f"[NexusLauncher] Warning: Could not check project status: {e}") # 规范化路径为正斜杠格式(SP 可能需要这个) normalized_path = library_path.replace('\\', '/') print(f"[NexusLauncher] Normalized path: {normalized_path}") # 使用 Shelves.add 方法(使用规范化的名称) print(f"[NexusLauncher] Calling Shelves.add('{safe_library_name}', '{normalized_path}')") new_shelf = substance_painter.resource.Shelves.add(safe_library_name, normalized_path) print(f"[NexusLauncher] ✓ Library added successfully") # 刷新资源 substance_painter.resource.Shelves.refresh_all() print(f"[NexusLauncher] ✓ Resources refreshed") except Exception as e: print(f"[NexusLauncher] Error adding library: {e}") import traceback traceback.print_exc() return False else: print(f"[NexusLauncher] Library already configured") # 设置为默认库(如果需要) if set_as_default: # 注意:SP API 可能没有直接设置默认库的方法 # 这需要通过修改配置文件来实现 print(f"[NexusLauncher] Note: Default library setting may require manual configuration") print(f"[NexusLauncher] ========================================") print(f"[NexusLauncher] ✓ Library setup complete") print(f"[NexusLauncher] ========================================") return True except ImportError as e: print(f"[NexusLauncher] Error: Cannot import substance_painter module") print(f"[NexusLauncher] This script must be run inside Substance Painter") return False except Exception as e: print(f"[NexusLauncher] Error: {e}") import traceback traceback.print_exc() return False def get_library_info_from_env(): """从环境变量获取库信息 Returns: tuple: (library_name, library_path) 或 (None, None) """ # NexusLauncher 会设置这些环境变量 library_name = os.environ.get('NEXUS_SP_LIBRARY_NAME') library_path = os.environ.get('NEXUS_SP_LIBRARY_PATH') if library_name and library_path: return library_name, library_path return None, None def main(): """主函数""" print(f"[NexusLauncher] ========================================") print(f"[NexusLauncher] Substance Painter Library Setup Plugin") print(f"[NexusLauncher] ========================================") print(f"[NexusLauncher] Plugin loaded from: {__file__}") print(f"[NexusLauncher] Python version: {sys.version}") # 检查环境变量 print(f"[NexusLauncher] Checking environment variables...") library_name = os.environ.get('NEXUS_SP_LIBRARY_NAME') library_path = os.environ.get('NEXUS_SP_LIBRARY_PATH') print(f"[NexusLauncher] NEXUS_SP_LIBRARY_NAME: {library_name}") print(f"[NexusLauncher] NEXUS_SP_LIBRARY_PATH: {library_path}") if not library_name or not library_path: print(f"[NexusLauncher] Error: Library info not found in environment variables") print(f"[NexusLauncher] Please launch SP through NexusLauncher") return False # 优先使用注册表方法(更可靠) print(f"[NexusLauncher] Method 1: Using Windows Registry...") success = setup_project_library_via_registry(library_name, library_path) if not success: print(f"[NexusLauncher] Method 2: Using Python API...") success = setup_project_library(library_name, library_path, set_as_default=True) return success # SP 插件入口点 def start_plugin(): """SP 插件启动入口点""" main() def close_plugin(): """SP 插件关闭入口点""" pass # 如果作为插件运行 if __name__ == "__plugin__": start_plugin() # 如果作为脚本运行 elif __name__ == "__main__": main()