spine连接器和导出修改
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
import { access, stat } from 'node:fs/promises'
|
||||
import { constants } from 'node:fs'
|
||||
import { homedir, platform } from 'node:os'
|
||||
import { join } from 'node:path'
|
||||
import { execFile } from 'node:child_process'
|
||||
|
||||
const SUPPORTED_VERSIONS = new Set(['3.8', '4.0', '4.1', '4.2', '4.3'])
|
||||
|
||||
async function executable(path) {
|
||||
if (!path) return false
|
||||
try {
|
||||
await access(path, platform() === 'win32' ? constants.F_OK : constants.X_OK)
|
||||
return true
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
export async function findSpineExecutable() {
|
||||
const candidates = []
|
||||
if (process.env.SPINE_EXECUTABLE) candidates.push(process.env.SPINE_EXECUTABLE)
|
||||
if (platform() === 'darwin') {
|
||||
candidates.push(
|
||||
'/Applications/Spine.app/Contents/MacOS/Spine',
|
||||
join(homedir(), 'Applications/Spine.app/Contents/MacOS/Spine'),
|
||||
)
|
||||
} else if (platform() === 'win32') {
|
||||
const programFiles = [process.env.ProgramFiles, process.env['ProgramFiles(x86)'], process.env.LOCALAPPDATA].filter(Boolean)
|
||||
for (const root of programFiles) {
|
||||
candidates.push(join(root, 'Spine', 'Spine.com'), join(root, 'Esoteric Software', 'Spine', 'Spine.com'))
|
||||
}
|
||||
} else {
|
||||
candidates.push('/opt/Spine/Spine.sh', join(homedir(), 'Spine/Spine.sh'), '/usr/local/bin/Spine', '/usr/bin/Spine')
|
||||
}
|
||||
for (const candidate of candidates) if (await executable(candidate)) return candidate
|
||||
return null
|
||||
}
|
||||
|
||||
export function normalizeSpineVersion(value) {
|
||||
const match = /^(3\.8|4\.[0-3])(?:\.\d+)?$/.exec(String(value || '').trim())
|
||||
if (!match || !SUPPORTED_VERSIONS.has(match[1])) throw new Error(`不支持的 Spine 版本:${value}`)
|
||||
return match[1]
|
||||
}
|
||||
|
||||
function run(executablePath, args, timeoutMs = 180000) {
|
||||
return new Promise((resolve, reject) => {
|
||||
execFile(executablePath, args, { timeout: timeoutMs, windowsHide: true, maxBuffer: 4 * 1024 * 1024 }, (error, stdout, stderr) => {
|
||||
if (error) {
|
||||
const details = [stdout, stderr, error.message]
|
||||
.filter(Boolean)
|
||||
.join('\n')
|
||||
.replace(/^Licensed to:.*$/gim, 'Spine 授权信息已隐藏')
|
||||
.trim()
|
||||
reject(new Error(details || 'Spine 命令执行失败'))
|
||||
return
|
||||
}
|
||||
resolve({ stdout, stderr })
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
export async function importSpineProject({ executablePath, inputPath, outputPath, projectName, spineVersion }) {
|
||||
const version = normalizeSpineVersion(spineVersion)
|
||||
const editorVersion = `${version}.xx`
|
||||
const result = await run(executablePath, [
|
||||
'--update', editorVersion,
|
||||
'--input', inputPath,
|
||||
'--output', outputPath,
|
||||
'--import', projectName,
|
||||
])
|
||||
const output = await stat(outputPath).catch(() => null)
|
||||
if (!output?.isFile() || output.size === 0) throw new Error('Spine 未生成有效的工程文件')
|
||||
const detected = /Starting:\s+Spine\s+([0-9.]+)/i.exec(`${result.stdout}\n${result.stderr}`)?.[1] || editorVersion
|
||||
return { ...result, editorVersion: detected, size: output.size }
|
||||
}
|
||||
Reference in New Issue
Block a user