Files
NexusLauncher/plugins/substancepainter/sp_api_plugin.py
2025-11-23 20:41:50 +08:00

319 lines
13 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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()