Appearance
导入管理器
概述
导入管理器(Import Manager)是 Eagle2Ae AE 扩展 v2.4.0 的核心组件,负责处理从 Eagle 素材库到 After Effects 的素材导入流程。该组件支持多种导入模式,提供智能路径管理、进度跟踪、错误处理等功能,确保素材导入过程的稳定性和可靠性。
核心特性
多种导入模式
- 直接导入 - 从源目录直接导入到AE项目(不复制文件)
- 项目旁复制 - 将文件复制到AE项目文件旁边后导入
- 指定文件夹 - 将文件复制到用户指定的文件夹后导入
- 智能导入 - 根据文件类型自动选择最佳导入方式
智能路径管理
- 自动路径解析 - 智能解析和验证文件路径
- 路径规范化 - 统一处理不同操作系统的路径格式
- 权限检查 - 验证文件访问权限
- 冲突处理 - 处理文件名冲突和覆盖问题
进度跟踪和监控
- 导入进度 - 实时显示导入进度和状态
- 性能监控 - 监控导入过程的性能指标
- 错误统计 - 统计导入过程中的错误
- 日志记录 - 详细记录导入过程
错误处理和恢复
- 重试机制 - 自动重试失败的导入操作
- 错误隔离 - 隔离和处理单个文件导入错误
- 回滚支持 - 支持导入失败时的清理操作
- 详细错误信息 - 提供详细的错误描述和解决方案
技术实现
核心类结构
javascript
/**
* 导入管理器
* 负责处理从Eagle素材库到After Effects的素材导入流程
*/
class ImportManager {
/**
* 构造函数
* @param {Object} aeExtension - AE扩展实例
*/
constructor(aeExtension) {
this.aeExtension = aeExtension;
this.csInterface = aeExtension.csInterface;
this.settingsManager = aeExtension.settingsManager;
this.logManager = aeExtension.logManager;
this.eagleConnectionManager = aeExtension.eagleConnectionManager;
this.projectStatusChecker = aeExtension.projectStatusChecker;
// 导入配置
this.config = {
maxConcurrentImports: 5, // 最大并发导入数
retryAttempts: 3, // 重试次数
retryDelay: 1000, // 重试延迟(毫秒)
chunkSize: 10, // 分批处理大小
timeout: 30000, // 操作超时时间
enableProgressTracking: true, // 启用进度跟踪
enableLogging: true, // 启用日志记录
enableValidation: true // 启用验证
};
// 当前导入状态
this.currentImport = {
active: false,
progress: 0,
total: 0,
completed: 0,
failed: 0,
currentFile: null,
startTime: null,
estimatedTime: null
};
// 导入队列
this.importQueue = [];
this.activeImports = new Set();
// 性能监控
this.performanceMetrics = {
totalImportTime: 0,
averageImportTime: 0,
importRate: 0,
memoryUsage: new Map()
};
// 统计数据
this.stats = {
totalImports: 0,
successfulImports: 0,
failedImports: 0,
byType: new Map(),
bySource: new Map(),
startTime: Date.now()
};
// 回调函数
this.callbacks = {
onProgress: null,
onComplete: null,
onError: null,
onCancel: null
};
// 绑定方法上下文
this.importFiles = this.importFiles.bind(this);
this.importFile = this.importFile.bind(this);
this.importToProject = this.importToProject.bind(this);
this.importToProjectAdjacent = this.importToProjectAdjacent.bind(this);
this.importToCustomFolder = this.importToCustomFolder.bind(this);
this.validateImport = this.validateImport.bind(this);
this.processImportQueue = this.processImportQueue.bind(this);
this.handleImportError = this.handleImportError.bind(this);
this.updateProgress = this.updateProgress.bind(this);
this.cancelImport = this.cancelImport.bind(this);
this.log('📥 导入管理器已初始化', 'debug');
}
}导入模式实现
javascript
/**
* 导入文件到AE项目
* @param {Array} files - 要导入的文件列表
* @param {Object} options - 导入选项
* @returns {Object} 导入结果
*/
async importFiles(files, options = {}) {
try {
this.log('📥 开始导入文件...', 'info', {
fileCount: files.length,
options: options
});
// 验证导入条件
const validation = this.validateImportConditions(options);
if (!validation.valid) {
this.log(`❌ 导入条件验证失败: ${validation.error}`, 'error');
return {
success: false,
error: validation.error,
importedCount: 0,
failedCount: files.length
};
}
// 初始化导入状态
this.currentImport = {
active: true,
progress: 0,
total: files.length,
completed: 0,
failed: 0,
currentFile: null,
startTime: Date.now(),
estimatedTime: this.estimateImportTime(files.length)
};
// 分批处理文件
const chunks = this.chunkArray(files, this.config.chunkSize);
let results = {
success: true,
importedFiles: [],
failedFiles: [],
totalProcessed: 0,
totalErrors: 0
};
for (const chunk of chunks) {
const chunkResult = await this.processImportChunk(chunk, options);
results.importedFiles.push(...chunkResult.importedFiles);
results.failedFiles.push(...chunkResult.failedFiles);
results.totalProcessed += chunkResult.totalProcessed;
results.totalErrors += chunkResult.totalErrors;
// 更新总体状态
this.currentImport.completed = results.importedFiles.length;
this.currentImport.failed = results.failedFiles.length;
if (results.totalErrors > 0 && options.stopOnError) {
this.log(`❌ 遇到错误且设置为停止,终止导入`, 'error');
break;
}
}
// 更新统计
this.stats.totalImports += results.totalProcessed;
this.stats.successfulImports += results.importedFiles.length;
this.stats.failedImports += results.failedFiles.length;
// 更新导入状态
this.currentImport.active = false;
const success = results.totalErrors === 0;
this.log(`📥 导入完成: ${success ? '成功' : '部分失败'}`, 'info', {
total: files.length,
successful: results.importedFiles.length,
failed: results.failedFiles.length
});
// 调用完成回调
if (this.callbacks.onComplete) {
this.callbacks.onComplete(results);
}
return {
success: success,
importedFiles: results.importedFiles,
failedFiles: results.failedFiles,
totalProcessed: results.totalProcessed,
totalErrors: results.totalErrors,
importTime: Date.now() - this.currentImport.startTime
};
} catch (error) {
this.log(`💥 导入过程异常: ${error.message}`, 'error');
this.currentImport.active = false;
if (this.callbacks.onError) {
this.callbacks.onError(error);
}
return {
success: false,
error: error.message,
importedCount: 0,
failedCount: files.length
};
}
}
/**
* 处理导入块
* @param {Array} chunk - 文件块
* @param {Object} options - 导入选项
* @returns {Object} 块处理结果
*/
async processImportChunk(chunk, options) {
const results = {
importedFiles: [],
failedFiles: [],
totalProcessed: 0,
totalErrors: 0
};
// 并发处理块中的文件
const promises = chunk.map(async (file) => {
if (!this.currentImport.active) {
return { success: false, file: file, error: '导入已取消' };
}
try {
const result = await this.importFile(file, options);
if (result.success) {
results.importedFiles.push(result.file);
} else {
results.failedFiles.push({
file: file,
error: result.error
});
results.totalErrors++;
}
results.totalProcessed++;
// 更新进度
this.updateProgress();
} catch (error) {
results.failedFiles.push({
file: file,
error: error.message
});
results.totalErrors++;
results.totalProcessed++;
}
});
await Promise.all(promises);
return results;
}
/**
* 导入单个文件
* @param {Object} file - 要导入的文件
* @param {Object} options - 导入选项
* @returns {Object} 导入结果
*/
async importFile(file, options) {
try {
this.currentImport.currentFile = file;
this.log(`📥 开始导入文件: ${file.name}`, 'debug', {
file: file,
path: file.path
});
// 验证单个文件
const fileValidation = this.validateFileForImport(file);
if (!fileValidation.valid) {
throw new Error(fileValidation.error);
}
// 根据导入模式处理
let importResult;
switch (options.mode || this.settingsManager.getField('import.mode')) {
case 'direct':
importResult = await this.importToProject(file, options);
break;
case 'project_adjacent':
importResult = await this.importToProjectAdjacent(file, options);
break;
case 'custom_folder':
importResult = await this.importToCustomFolder(file, options);
break;
default:
// 根据设置获取默认模式
const defaultMode = this.settingsManager.getField('import.mode');
switch (defaultMode) {
case 'direct':
importResult = await this.importToProject(file, options);
break;
case 'project_adjacent':
importResult = await this.importToProjectAdjacent(file, options);
break;
case 'custom_folder':
importResult = await this.importToCustomFolder(file, options);
break;
default:
throw new Error(`未知的导入模式: ${defaultMode}`);
}
}
if (importResult.success) {
this.log(`✅ 文件导入成功: ${file.name}`, 'success', { file: file });
// 如果需要添加到合成
if (options.addToComposition !== false &&
this.settingsManager.getField('import.addToComposition')) {
const addToCompResult = await this.addToComposition(importResult.importedFile, options);
if (!addToCompResult.success) {
this.log(`⚠️ 添加到合成失败: ${addToCompResult.error}`, 'warning');
}
}
return {
success: true,
file: file,
importedFile: importResult.importedFile,
mode: options.mode
};
} else {
throw new Error(importResult.error || '未知错误');
}
} catch (error) {
this.log(`❌ 文件导入失败: ${file.name} - ${error.message}`, 'error', {
file: file,
error: error.message
});
return {
success: false,
file: file,
error: error.message
};
}
}三种导入模式实现
javascript
/**
* 直接导入到项目
* @param {Object} file - 文件对象
* @param {Object} options - 选项
* @returns {Object} 导入结果
*/
async importToProject(file, options = {}) {
try {
this.log('🔄 直接导入模式: 从源目录导入到AE项目', 'debug');
// 验证文件存在
const fileExists = await this.checkFileExists(file.path);
if (!fileExists) {
throw new Error(`文件不存在: ${file.path}`);
}
// 在DEMO模式下模拟导入
if (window.__DEMO_MODE_ACTIVE__) {
return {
success: true,
importedFile: {
...file,
importedPath: file.path,
importMode: 'direct'
}
};
}
// CEP模式:使用ExtendScript导入
const script = `
var file = new File("${this.normalizePath(file.path)}");
if (file.exists) {
try {
var project = app.project;
var item = project.importFile(new ImportOptions(file));
{ success: true, itemId: item.id, importedPath: file.fsName }
} catch (e) {
{ success: false, error: e.message }
}
} else {
{ success: false, error: "文件不存在" }
}
`;
const result = await this.aeExtension.executeExtendScript(script);
if (result && result.success) {
return {
success: true,
importedFile: {
...file,
importedPath: result.importedPath,
itemId: result.itemId,
importMode: 'direct'
}
};
} else {
throw new Error(result.error || '导入失败');
}
} catch (error) {
this.log(`❌ 直接导入失败: ${error.message}`, 'error');
return {
success: false,
error: error.message
};
}
}
/**
* 项目旁复制后导入
* @param {Object} file - 文件对象
* @param {Object} options - 选项
* @returns {Object} 导入结果
*/
async importToProjectAdjacent(file, options = {}) {
try {
this.log('🔄 项目旁复制模式: 复制到项目旁后导入', 'debug');
// 获取项目路径
const projectInfo = await this.projectStatusChecker.getProjectInfo();
if (!projectInfo.success) {
throw new Error('无法获取项目信息');
}
// 确定目标文件夹
const projectFolder = this.extractFolderPath(projectInfo.projectPath);
const customFolderName = this.settingsManager.getField('import.projectAdjacentFolder') || 'Eagle_Assets';
const targetFolder = this.joinPath(projectFolder, customFolderName);
// 创建目标文件夹
await this.createFolder(targetFolder);
// 确定目标文件路径
const targetPath = this.joinPath(targetFolder, file.name);
// 如果文件已存在,处理重命名
let finalTargetPath = targetPath;
if (await this.checkFileExists(targetPath)) {
finalTargetPath = await this.resolveFileNameConflict(targetPath);
}
// 复制文件
const copyResult = await this.copyFile(file.path, finalTargetPath);
if (!copyResult.success) {
throw new Error(`文件复制失败: ${copyResult.error}`);
}
// 导入复制的文件
const importResult = await this.importToProject({
...file,
path: finalTargetPath,
name: this.extractFileName(finalTargetPath)
}, { ...options, mode: 'direct' });
if (importResult.success) {
return {
success: true,
importedFile: {
...importResult.importedFile,
originalPath: file.path,
importMode: 'project_adjacent'
}
};
} else {
throw new Error(importResult.error);
}
} catch (error) {
this.log(`❌ 项目旁复制导入失败: ${error.message}`, 'error');
return {
success: false,
error: error.message
};
}
}
/**
* 自定义文件夹导入
* @param {Object} file - 文件对象
* @param {Object} options - 选项
* @returns {Object} 导入结果
*/
async importToCustomFolder(file, options = {}) {
try {
this.log('🔄 自定义文件夹模式: 复制到指定文件夹后导入', 'debug');
// 获取自定义文件夹路径
let customFolderPath = options.customFolder ||
this.settingsManager.getField('import.customFolderPath');
if (!customFolderPath) {
// 如果没有指定路径,让用户选择
const browseResult = await this.browseFolder();
if (!browseResult.success) {
throw new Error('未选择自定义文件夹');
}
customFolderPath = browseResult.folderPath;
}
// 确定目标文件路径
const targetPath = this.joinPath(customFolderPath, file.name);
// 如果文件已存在,处理重命名
let finalTargetPath = targetPath;
if (await this.checkFileExists(targetPath)) {
finalTargetPath = await this.resolveFileNameConflict(targetPath);
}
// 复制文件
const copyResult = await this.copyFile(file.path, finalTargetPath);
if (!copyResult.success) {
throw new Error(`文件复制失败: ${copyResult.error}`);
}
// 导入复制的文件
const importResult = await this.importToProject({
...file,
path: finalTargetPath,
name: this.extractFileName(finalTargetPath)
}, { ...options, mode: 'direct' });
if (importResult.success) {
return {
success: true,
importedFile: {
...importResult.importedFile,
originalPath: file.path,
importMode: 'custom_folder'
}
};
} else {
throw new Error(importResult.error);
}
} catch (error) {
this.log(`❌ 自定义文件夹导入失败: ${error.message}`, 'error');
return {
success: false,
error: error.message
};
}
}
/**
* 添加到合成
* @param {Object} importedFile - 导入的文件
* @param {Object} options - 选项
* @returns {Object} 添加结果
*/
async addToComposition(importedFile, options = {}) {
try {
this.log('🔄 将文件添加到当前合成', 'debug');
// 检查时间轴选项
const timelineOptions = this.settingsManager.getField('import.timelineOptions');
const addToComposition = this.settingsManager.getField('import.addToComposition');
if (!addToComposition || !timelineOptions.enabled) {
this.log('⏭️ 未启用添加到合成选项', 'debug');
return { success: true, message: '跳过添加到合成' };
}
// 确保有活动合成
const activeComp = await this.projectStatusChecker.getActiveCompInfo();
if (!activeComp.success) {
throw new Error('没有活动合成');
}
// 在DEMO模式下模拟添加
if (window.__DEMO_MODE_ACTIVE__) {
this.log('🎭 Demo模式:模拟添加到合成', 'debug');
return { success: true, demo: true };
}
// CEP模式:使用ExtendScript添加到合成
let script = `
try {
var project = app.project;
var comp = app.project.activeItem;
if (!(comp instanceof CompItem)) {
throw "没有活动合成";
}
// 根据导入的项目ID获取素材
var item = project.item(${importedFile.itemId || 1});
// 计算放置时间
var time = 0;
`;
if (timelineOptions.placement === 'current_time') {
script += `
time = comp.time;
`;
}
script += `
// 添加图层到合成
var layer = comp.layers.add(item);
layer.startTime = time;
// 设置序列帧间隔
if (${timelineOptions.sequenceInterval} && item.hasVideo && item.frameDuration) {
layer.outPoint = layer.inPoint + (${timelineOptions.sequenceInterval} / item.frameDuration);
}
{ success: true, layerId: layer.index, startTime: time }
} catch (e) {
{ success: false, error: e.message }
}
`;
const result = await this.aeExtension.executeExtendScript(script);
if (result && result.success) {
this.log('✅ 文件成功添加到合成', 'success');
return result;
} else {
throw new Error(result.error || '添加到合成失败');
}
} catch (error) {
this.log(`❌ 添加到合成失败: ${error.message}`, 'error');
return {
success: false,
error: error.message
};
}
}路径和文件操作
javascript
/**
* 检查文件是否存在
* @param {string} filePath - 文件路径
* @returns {Promise<boolean>} 文件是否存在
*/
async checkFileExists(filePath) {
try {
if (window.__DEMO_MODE_ACTIVE__) {
// Demo模式:模拟文件存在检查
return true;
}
// CEP模式:使用ExtendScript检查文件
const script = `
var file = new File("${this.normalizePath(filePath)}");
file.exists;
`;
const result = await this.aeExtension.executeExtendScript(script);
return result === true;
} catch (error) {
this.log(`检查文件存在失败: ${error.message}`, 'error');
return false;
}
}
/**
* 创建文件夹
* @param {string} folderPath - 文件夹路径
* @returns {Promise<Object>} 创建结果
*/
async createFolder(folderPath) {
try {
if (window.__DEMO_MODE_ACTIVE__) {
// Demo模式:模拟创建文件夹
this.log(`🎭 Demo模式:模拟创建文件夹: ${folderPath}`, 'debug');
return { success: true };
}
// CEP模式:使用ExtendScript创建文件夹
const script = `
try {
var folder = new Folder("${this.normalizePath(folderPath)}");
if (!folder.exists) {
folder.create();
}
{ success: true }
} catch (e) {
{ success: false, error: e.message }
}
`;
const result = await this.aeExtension.executeExtendScript(script);
if (result.success) {
this.log(`📁 文件夹创建成功: ${folderPath}`, 'debug');
return result;
} else {
throw new Error(result.error || '创建文件夹失败');
}
} catch (error) {
this.log(`创建文件夹失败: ${error.message}`, 'error');
return {
success: false,
error: error.message
};
}
}
/**
* 复制文件
* @param {string} sourcePath - 源路径
* @param {string} targetPath - 目标路径
* @returns {Promise<Object>} 复制结果
*/
async copyFile(sourcePath, targetPath) {
try {
if (window.__DEMO_MODE_ACTIVE__) {
// Demo模式:模拟文件复制
this.log(`🎭 Demo模式:模拟复制文件 ${sourcePath} -> ${targetPath}`, 'debug');
return { success: true };
}
// CEP模式:使用ExtendScript复制文件
const script = `
try {
var sourceFile = new File("${this.normalizePath(sourcePath)}");
var targetFile = new File("${this.normalizePath(targetPath)}");
if (!sourceFile.exists) {
throw "源文件不存在";
}
var result = sourceFile.copy(targetFile);
{ success: result, targetPath: targetFile.fsName }
} catch (e) {
{ success: false, error: e.message }
}
`;
const result = await this.aeExtension.executeExtendScript(script);
if (result.success) {
this.log(`📄 文件复制成功: ${sourcePath} -> ${targetPath}`, 'debug');
return result;
} else {
throw new Error(result.error || '文件复制失败');
}
} catch (error) {
this.log(`文件复制失败: ${error.message}`, 'error');
return {
success: false,
error: error.message
};
}
}
/**
* 解决文件名冲突
* @param {string} targetPath - 目标路径
* @returns {Promise<string>} 解决冲突后的路径
*/
async resolveFileNameConflict(targetPath) {
try {
let counter = 1;
const basePath = this.removeFileExtension(targetPath);
const extension = this.getFileExtension(targetPath);
let newPath = targetPath;
while (await this.checkFileExists(newPath)) {
const fileName = `${basePath} (${counter})${extension}`;
newPath = fileName;
counter++;
// 防止无限循环
if (counter > 1000) {
throw new Error('文件名冲突解决失败:达到最大重试次数');
}
}
this.log(`🔄 文件名冲突已解决: ${targetPath} -> ${newPath}`, 'debug');
return newPath;
} catch (error) {
this.log(`解决文件名冲突失败: ${error.message}`, 'error');
throw error;
}
}
/**
* 浏览文件夹
* @returns {Promise<Object>} 浏览结果
*/
async browseFolder() {
try {
if (window.__DEMO_MODE_ACTIVE__) {
// Demo模式:模拟文件夹选择
if (window.demoFileSystem) {
const result = window.demoFileSystem.showFolderPicker({
title: '选择导入文件夹',
mode: 'selectFolder'
});
return {
success: result.success,
folderPath: result.folderPath || null
};
} else {
// 降级到输入框
const folderPath = prompt('请输入目标文件夹路径:');
if (folderPath && folderPath.trim()) {
return {
success: true,
folderPath: folderPath.trim()
};
} else {
return {
success: false,
error: '用户取消了文件夹选择'
};
}
}
}
// CEP模式:使用ExtendScript浏览文件夹
const script = `
try {
var folder = Folder.selectDialog("选择导入文件夹");
if (folder) {
{ success: true, folderPath: folder.fsName }
} else {
{ success: false, error: "用户取消了选择" }
}
} catch (e) {
{ success: false, error: e.message }
}
`;
const result = await this.aeExtension.executeExtendScript(script);
return result;
} catch (error) {
this.log(`浏览文件夹失败: ${error.message}`, 'error');
return {
success: false,
error: error.message
};
}
}
/**
* 提取文件夹路径
* @param {string} filePath - 文件路径
* @returns {string} 文件夹路径
*/
extractFolderPath(filePath) {
try {
if (!filePath) return '';
// 规范化路径分隔符
let path = filePath.replace(/\\/g, '/');
// 移除文件名部分
const lastSlashIndex = path.lastIndexOf('/');
if (lastSlashIndex !== -1) {
return path.substring(0, lastSlashIndex);
}
return '';
} catch (error) {
this.log(`提取文件夹路径失败: ${error.message}`, 'error');
return '';
}
}
/**
* 提取文件名
* @param {string} filePath - 文件路径
* @returns {string} 文件名
*/
extractFileName(filePath) {
try {
if (!filePath) return '';
// 规范化路径分隔符
const path = filePath.replace(/\\/g, '/');
// 获取最后一个分隔符后的内容
const lastSlashIndex = path.lastIndexOf('/');
if (lastSlashIndex !== -1) {
return path.substring(lastSlashIndex + 1);
}
return path;
} catch (error) {
this.log(`提取文件名失败: ${error.message}`, 'error');
return '';
}
}
/**
* 获取文件扩展名
* @param {string} filePath - 文件路径
* @returns {string} 扩展名
*/
getFileExtension(filePath) {
try {
if (!filePath) return '';
const lastDotIndex = filePath.lastIndexOf('.');
if (lastDotIndex !== -1) {
return filePath.substring(lastDotIndex);
}
return '';
} catch (error) {
this.log(`获取文件扩展名失败: ${error.message}`, 'error');
return '';
}
}
/**
* 移除文件扩展名
* @param {string} filePath - 文件路径
* @returns {string} 移除扩展名后的路径
*/
removeFileExtension(filePath) {
try {
if (!filePath) return filePath;
const lastDotIndex = filePath.lastIndexOf('.');
if (lastDotIndex !== -1) {
return filePath.substring(0, lastDotIndex);
}
return filePath;
} catch (error) {
this.log(`移除文件扩展名失败: ${error.message}`, 'error');
return filePath;
}
}
/**
* 连接路径
* @param {...string} paths - 路径片段
* @returns {string} 连接后的路径
*/
joinPath(...paths) {
try {
if (paths.length === 0) return '';
// 移除空路径片段
const validPaths = paths.filter(p => p && typeof p === 'string');
if (validPaths.length === 0) return '';
// 规范化路径分隔符
let normalizedPath = validPaths[0].replace(/\\/g, '/');
for (let i = 1; i < validPaths.length; i++) {
let pathSegment = validPaths[i].replace(/\\/g, '/');
// 移除开头的分隔符
if (pathSegment.startsWith('/')) {
pathSegment = pathSegment.substring(1);
}
// 添加分隔符
if (!normalizedPath.endsWith('/')) {
normalizedPath += '/';
}
normalizedPath += pathSegment;
}
return normalizedPath;
} catch (error) {
this.log(`连接路径失败: ${error.message}`, 'error');
return paths.join('/');
}
}
/**
* 规范化路径
* @param {string} path - 路径
* @returns {string} 规范化后的路径
*/
normalizePath(path) {
try {
if (!path) return path;
// 规范化路径分隔符
let normalized = path.replace(/\\/g, '/');
// 解决相对路径
if (normalized.includes('..')) {
const parts = normalized.split('/');
const resolvedParts = [];
for (const part of parts) {
if (part === '..') {
resolvedParts.pop();
} else if (part !== '.') {
resolvedParts.push(part);
}
}
normalized = resolvedParts.join('/');
}
return normalized;
} catch (error) {
this.log(`规范化路径失败: ${error.message}`, 'error');
return path;
}
}验证和进度管理
javascript
/**
* 验证导入条件
* @param {Object} options - 导入选项
* @returns {Object} 验证结果
*/
validateImportConditions(options = {}) {
try {
// 检查AE项目状态
const projectValid = this.projectStatusChecker.validateProjectStatus({
requireProject: true,
requireActiveComposition: false, // 导入不一定需要活动合成
showWarning: true
});
if (!projectValid) {
return {
valid: false,
error: 'AE项目状态不满足导入条件'
};
}
// 检查Eagle连接
const eagleConnected = this.eagleConnectionManager.validateConnection();
if (!eagleConnected) {
return {
valid: false,
error: 'Eagle连接不可用'
};
}
// 检查导入模式设置
const importMode = this.settingsManager.getField('import.mode');
if (!['direct', 'project_adjacent', 'custom_folder'].includes(importMode)) {
return {
valid: false,
error: `无效的导入模式: ${importMode}`
};
}
// 检查项目旁文件夹设置(如果使用该模式)
if (importMode === 'project_adjacent') {
const folderName = this.settingsManager.getField('import.projectAdjacentFolder');
if (!folderName || folderName.trim() === '') {
return {
valid: false,
error: '项目旁文件夹名称不能为空'
};
}
}
// 检查自定义文件夹设置(如果使用该模式)
if (importMode === 'custom_folder' && !options.customFolder) {
const customPath = this.settingsManager.getField('import.customFolderPath');
if (!customPath || customPath.trim() === '') {
return {
valid: false,
error: '自定义文件夹路径未设置'
};
}
}
return {
valid: true
};
} catch (error) {
this.log(`验证导入条件失败: ${error.message}`, 'error');
return {
valid: false,
error: error.message
};
}
}
/**
* 验证单个文件
* @param {Object} file - 文件对象
* @returns {Object} 验证结果
*/
validateFileForImport(file) {
try {
if (!file) {
return {
valid: false,
error: '文件对象不能为空'
};
}
if (!file.path) {
return {
valid: false,
error: '文件路径不能为空'
};
}
if (!file.name) {
return {
valid: false,
error: '文件名不能为空'
};
}
// 检查文件路径长度
if (file.path.length > 260) {
return {
valid: false,
error: '文件路径过长'
};
}
// 检查文件名是否包含非法字符
const invalidChars = /[<>:"|?*\x00-\x1f]/;
if (invalidChars.test(file.name)) {
return {
valid: false,
error: '文件名包含非法字符'
};
}
// 检查文件类型(如果需要)
const allowedExtensions = this.getAllowedFileExtensions();
const fileExt = this.getFileExtension(file.path).toLowerCase();
if (allowedExtensions && allowedExtensions.length > 0) {
if (!allowedExtensions.includes(fileExt)) {
return {
valid: false,
error: `不支持的文件类型: ${fileExt},支持的类型: ${allowedExtensions.join(', ')}`
};
}
}
return {
valid: true
};
} catch (error) {
this.log(`验证文件失败: ${error.message}`, 'error');
return {
valid: false,
error: error.message
};
}
}
/**
* 获取允许的文件扩展名
* @returns {Array} 允许的文件扩展名数组
*/
getAllowedFileExtensions() {
// 根据AE支持的格式返回允许的扩展名
// 这里可以根据需要进行配置
return [
'.png', '.jpg', '.jpeg', '.gif', '.psd', '.tga', '.tiff', '.tif',
'.ai', '.pdf', '.eps', '.svg', '.mov', '.mp4', '.avi', '.mkv',
'.wav', '.mp3', '.aif', '.aiff'
];
}
/**
* 更新导入进度
*/
updateProgress() {
try {
const total = this.currentImport.total;
const completed = this.currentImport.completed + this.currentImport.failed;
const progress = total > 0 ? Math.min(100, (completed / total) * 100) : 0;
this.currentImport.progress = progress;
// 计算剩余时间
if (this.currentImport.startTime && completed > 0) {
const elapsed = Date.now() - this.currentImport.startTime;
const rate = completed / (elapsed / 1000); // items per second
const remaining = total - completed;
const remainingTime = remaining / rate; // seconds
this.currentImport.estimatedTime = remainingTime;
}
this.log(`📈 导入进度: ${progress.toFixed(1)}% (${completed}/${total})`, 'debug');
// 调用进度回调
if (this.callbacks.onProgress) {
this.callbacks.onProgress({
progress: progress,
completed: completed,
total: total,
currentFile: this.currentImport.currentFile,
estimatedTime: this.currentImport.estimatedTime
});
}
} catch (error) {
this.log(`更新进度失败: ${error.message}`, 'error');
}
}
/**
* 估算导入时间
* @param {number} fileCount - 文件数量
* @returns {number} 估算时间(秒)
*/
estimateImportTime(fileCount) {
// 基于经验估算,可以结合历史数据进行更准确的估算
const avgTimePerFile = 2; // 平均每文件2秒
return fileCount * avgTimePerFile;
}
/**
* 分块数组
* @param {Array} array - 原数组
* @param {number} chunkSize - 块大小
* @returns {Array} 分块后的数组
*/
chunkArray(array, chunkSize) {
const chunks = [];
for (let i = 0; i < array.length; i += chunkSize) {
chunks.push(array.slice(i, i + chunkSize));
}
return chunks;
}
/**
* 取消导入
*/
cancelImport() {
try {
this.log('🚫 取消导入操作', 'warning');
this.currentImport.active = false;
// 调用取消回调
if (this.callbacks.onCancel) {
this.callbacks.onCancel();
}
} catch (error) {
this.log(`取消导入失败: ${error.message}`, 'error');
}
}错误处理和恢复
javascript
/**
* 处理导入错误
* @param {Object} file - 出错的文件
* @param {Error} error - 错误对象
* @param {number} retryCount - 重试次数
* @returns {Object} 处理结果
*/
async handleImportError(file, error, retryCount = 0) {
try {
this.log(`⚠️ 处理导入错误 (尝试 ${retryCount + 1}/${this.config.retryAttempts}): ${error.message}`, 'warning', {
file: file.name,
error: error.message,
retryCount: retryCount
});
// 如果重试次数未达到上限,尝试重试
if (retryCount < this.config.retryAttempts) {
// 等待重试延迟
await new Promise(resolve => setTimeout(resolve, this.config.retryDelay * (retryCount + 1)));
// 递增重试次数
retryCount++;
// 重新尝试导入
const result = await this.importFile(file, { retryCount: retryCount });
if (result.success) {
this.log(`✅ 重试成功: ${file.name}`, 'success');
return result;
} else {
// 重试失败,继续处理错误
return await this.handleImportError(file, new Error(result.error), retryCount);
}
} else {
// 重试次数达到上限,记录错误
this.log(`❌ 导入失败,已达到最大重试次数: ${file.name}`, 'error', {
file: file.name,
error: error.message
});
return {
success: false,
file: file,
error: error.message,
retryCount: retryCount
};
}
} catch (handlingError) {
this.log(`处理导入错误时发生异常: ${handlingError.message}`, 'error');
return {
success: false,
file: file,
error: handlingError.message,
retryCount: retryCount
};
}
}
/**
* 清理导入过程中产生的临时文件
* @param {Array} tempFiles - 临时文件列表
*/
async cleanupTempFiles(tempFiles) {
try {
if (!tempFiles || tempFiles.length === 0) {
return;
}
this.log(`🧹 清理 ${tempFiles.length} 个临时文件`, 'debug');
for (const tempFile of tempFiles) {
try {
if (await this.checkFileExists(tempFile.path)) {
if (window.__DEMO_MODE_ACTIVE__) {
// Demo模式:模拟删除
this.log(`🎭 Demo模式:模拟删除临时文件: ${tempFile.path}`, 'debug');
} else {
// CEP模式:使用ExtendScript删除文件
const script = `
try {
var file = new File("${this.normalizePath(tempFile.path)}");
if (file.exists) {
file.remove();
}
true;
} catch (e) {
false;
}
`;
await this.aeExtension.executeExtendScript(script);
}
this.log(`🗑️ 临时文件已删除: ${tempFile.path}`, 'debug');
}
} catch (deleteError) {
this.log(`删除临时文件失败: ${tempFile.path} - ${deleteError.message}`, 'warning');
}
}
} catch (error) {
this.log(`清理临时文件时发生异常: ${error.message}`, 'error');
}
}
/**
* 回滚导入操作(当导入失败时清理已导入的文件)
* @param {Array} importedFiles - 已导入的文件列表
*/
async rollbackImport(importedFiles) {
try {
if (!importedFiles || importedFiles.length === 0) {
return;
}
this.log(`🔄 回滚导入操作,清理 ${importedFiles.length} 个已导入的文件`, 'warning');
// 在DEMO模式下模拟回滚
if (window.__DEMO_MODE_ACTIVE__) {
this.log('🎭 Demo模式:模拟回滚操作', 'debug');
return;
}
// CEP模式:在AE中移除已导入的项目
for (const importedFile of importedFiles) {
try {
if (importedFile.itemId) {
const script = `
try {
var project = app.project;
var item = project.item(${importedFile.itemId});
if (item) {
item.remove();
}
true;
} catch (e) {
false;
}
`;
await this.aeExtension.executeExtendScript(script);
this.log(`🗑️ 已回滚导入的文件: ${importedFile.name}`, 'debug');
}
} catch (rollbackError) {
this.log(`回滚导入失败: ${importedFile.name} - ${rollbackError.message}`, 'warning');
}
}
} catch (error) {
this.log(`回滚导入操作时发生异常: ${error.message}`, 'error');
}
}API参考
核心方法
ImportManager
导入管理器主类
javascript
/**
* 导入管理器
* 负责处理从Eagle素材库到After Effects的素材导入流程
*/
class ImportManagerconstructor()
构造函数
javascript
/**
* 构造函数
* @param {Object} aeExtension - AE扩展实例
*/
constructor(aeExtension)importFiles()
导入文件到AE项目
javascript
/**
* 导入文件到AE项目
* @param {Array} files - 要导入的文件列表
* @param {Object} options - 导入选项
* @returns {Object} 导入结果
*/
async importFiles(files, options = {})importFile()
导入单个文件
javascript
/**
* 导入单个文件
* @param {Object} file - 要导入的文件
* @param {Object} options - 导入选项
* @returns {Object} 导入结果
*/
async importFile(file, options)importToProject()
直接导入到项目
javascript
/**
* 直接导入到项目
* @param {Object} file - 文件对象
* @param {Object} options - 选项
* @returns {Object} 导入结果
*/
async importToProject(file, options = {})importToProjectAdjacent()
项目旁复制后导入
javascript
/**
* 项目旁复制后导入
* @param {Object} file - 文件对象
* @param {Object} options - 选项
* @returns {Object} 导入结果
*/
async importToProjectAdjacent(file, options = {})importToCustomFolder()
自定义文件夹导入
javascript
/**
* 自定义文件夹导入
* @param {Object} file - 文件对象
* @param {Object} options - 选项
* @returns {Object} 导入结果
*/
async importToCustomFolder(file, options = {})addToComposition()
添加到合成
javascript
/**
* 添加到合成
* @param {Object} importedFile - 导入的文件
* @param {Object} options - 选项
* @returns {Object} 添加结果
*/
async addToComposition(importedFile, options = {})checkFileExists()
检查文件是否存在
javascript
/**
* 检查文件是否存在
* @param {string} filePath - 文件路径
* @returns {Promise<boolean>} 文件是否存在
*/
async checkFileExists(filePath)createFolder()
创建文件夹
javascript
/**
* 创建文件夹
* @param {string} folderPath - 文件夹路径
* @returns {Promise<Object>} 创建结果
*/
async createFolder(folderPath)copyFile()
复制文件
javascript
/**
* 复制文件
* @param {string} sourcePath - 源路径
* @param {string} targetPath - 目标路径
* @returns {Promise<Object>} 复制结果
*/
async copyFile(sourcePath, targetPath)resolveFileNameConflict()
解决文件名冲突
javascript
/**
* 解决文件名冲突
* @param {string} targetPath - 目标路径
* @returns {Promise<string>} 解决冲突后的路径
*/
async resolveFileNameConflict(targetPath)browseFolder()
浏览文件夹
javascript
/**
* 浏览文件夹
* @returns {Promise<Object>} 浏览结果
*/
async browseFolder()extractFolderPath()
提取文件夹路径
javascript
/**
* 提取文件夹路径
* @param {string} filePath - 文件路径
* @returns {string} 文件夹路径
*/
extractFolderPath(filePath)extractFileName()
提取文件名
javascript
/**
* 提取文件名
* @param {string} filePath - 文件路径
* @returns {string} 文件名
*/
extractFileName(filePath)getFileExtension()
获取文件扩展名
javascript
/**
* 获取文件扩展名
* @param {string} filePath - 文件路径
* @returns {string} 扩展名
*/
getFileExtension(filePath)removeFileExtension()
移除文件扩展名
javascript
/**
* 移除文件扩展名
* @param {string} filePath - 文件路径
* @returns {string} 移除扩展名后的路径
*/
removeFileExtension(filePath)joinPath()
连接路径
javascript
/**
* 连接路径
* @param {...string} paths - 路径片段
* @returns {string} 连接后的路径
*/
joinPath(...paths)normalizePath()
规范化路径
javascript
/**
* 规范化路径
* @param {string} path - 路径
* @returns {string} 规范化后的路径
*/
normalizePath(path)validateImportConditions()
验证导入条件
javascript
/**
* 验证导入条件
* @param {Object} options - 导入选项
* @returns {Object} 验证结果
*/
validateImportConditions(options = {})validateFileForImport()
验证单个文件
javascript
/**
* 验证单个文件
* @param {Object} file - 文件对象
* @returns {Object} 验证结果
*/
validateFileForImport(file)updateProgress()
更新导入进度
javascript
/**
* 更新导入进度
*/
updateProgress()cancelImport()
取消导入
javascript
/**
* 取消导入
*/
cancelImport()handleImportError()
处理导入错误
javascript
/**
* 处理导入错误
* @param {Object} file - 出错的文件
* @param {Error} error - 错误对象
* @param {number} retryCount - 重试次数
* @returns {Object} 处理结果
*/
async handleImportError(file, error, retryCount = 0)cleanupTempFiles()
清理临时文件
javascript
/**
* 清理导入过程中产生的临时文件
* @param {Array} tempFiles - 临时文件列表
*/
async cleanupTempFiles(tempFiles)rollbackImport()
回滚导入操作
javascript
/**
* 回滚导入操作(当导入失败时清理已导入的文件)
* @param {Array} importedFiles - 已导入的文件列表
*/
async rollbackImport(importedFiles)导入选项
javascript
/**
* 导入选项
* @typedef {Object} ImportOptions
* @property {string} mode - 导入模式 ('direct', 'project_adjacent', 'custom_folder')
* @property {string} customFolder - 自定义文件夹路径(mode为'custom_folder'时使用)
* @property {boolean} addToComposition - 是否添加到当前合成
* @property {boolean} stopOnError - 遇到错误时是否停止导入
* @property {boolean} forceOverwrite - 是否强制覆盖同名文件
* @property {Object} timelineOptions - 时间轴选项
* @property {string} timelineOptions.placement - 放置位置 ('current_time', 'timeline_start')
* @property {number} timelineOptions.sequenceInterval - 序列帧间隔(秒)
* @property {boolean} timelineOptions.enabled - 是否启用时间轴选项
*/导入结果
javascript
/**
* 导入结果
* @typedef {Object} ImportResult
* @property {boolean} success - 是否成功
* @property {Array} importedFiles - 成功导入的文件列表
* @property {Array} failedFiles - 导入失败的文件列表
* @property {number} totalProcessed - 总处理数量
* @property {number} totalErrors - 总错误数量
* @property {number} importTime - 导入耗时(毫秒)
* @property {string} error - 错误信息(失败时)
*/使用示例
基本使用
导入单个文件
javascript
// 创建导入管理器实例
const importManager = new ImportManager(aeExtension);
// 准备文件对象
const fileToImport = {
name: 'example.png',
path: 'C:/Eagle/Assets/example.png',
size: 1024000, // 1MB
type: 'image',
extension: '.png'
};
// 执行导入(使用默认设置)
const result = await importManager.importFiles([fileToImport]);
if (result.success) {
console.log(`✅ 成功导入 ${result.importedFiles.length} 个文件`);
console.log('导入的文件:', result.importedFiles);
} else {
console.log(`❌ 导入失败,错误数量: ${result.totalErrors}`);
console.log('失败的文件:', result.failedFiles);
}导入多个文件
javascript
// 准备多个文件
const filesToImport = [
{
name: 'image1.png',
path: 'C:/Eagle/Assets/image1.png',
size: 512000,
type: 'image'
},
{
name: 'image2.jpg',
path: 'C:/Eagle/Assets/image2.jpg',
size: 768000,
type: 'image'
},
{
name: 'video.mp4',
path: 'C:/Eagle/Assets/video.mp4',
size: 5242880,
type: 'video'
}
];
// 使用项目旁复制模式导入
const result = await importManager.importFiles(filesToImport, {
mode: 'project_adjacent',
addToComposition: true
});
console.log('批量导入结果:', result);监听导入进度
javascript
// 设置进度回调
importManager.callbacks.onProgress = (progressData) => {
console.log(`导入进度: ${progressData.progress.toFixed(1)}%`);
console.log(`已处理: ${progressData.completed}/${progressData.total}`);
console.log(`当前文件: ${progressData.currentFile?.name || 'N/A'}`);
// 更新UI进度条
updateProgressBar(progressData.progress);
};
// 设置完成回调
importManager.callbacks.onComplete = (result) => {
console.log('导入完成:', result);
showImportSummary(result);
};
// 设置错误回调
importManager.callbacks.onError = (error) => {
console.error('导入过程中发生错误:', error);
showErrorNotification(error.message);
};
// 执行导入
await importManager.importFiles(filesToImport);高级使用
自定义导入模式
javascript
// 使用自定义文件夹模式
const customImportResult = await importManager.importFiles(filesToImport, {
mode: 'custom_folder',
customFolder: 'D:/AE_Projects/Imported_Assets',
addToComposition: true,
timelineOptions: {
enabled: true,
placement: 'current_time',
sequenceInterval: 1.0
}
});
console.log('自定义文件夹导入结果:', customImportResult);
// 使用直接导入模式
const directImportResult = await importManager.importFiles(filesToImport, {
mode: 'direct',
addToComposition: false // 不添加到合成,仅导入到项目面板
});
console.log('直接导入结果:', directImportResult);错误处理和重试
javascript
// 创建带错误处理的导入函数
async function robustImport(files) {
try {
const result = await importManager.importFiles(files, {
stopOnError: false, // 遇到错误不停止,继续处理其他文件
retryAttempts: 2, // 设置重试次数
forceOverwrite: true // 强制覆盖同名文件
});
if (!result.success) {
console.log(`⚠️ 部分文件导入失败 (${result.totalErrors}/${result.totalProcessed})`);
// 处理失败的文件
for (const failedFile of result.failedFiles) {
console.error(`❌ ${failedFile.file.name}: ${failedFile.error}`);
// 根据错误类型采取相应措施
if (failedFile.error.includes('权限')) {
console.log('💡 建议检查文件权限');
} else if (failedFile.error.includes('路径')) {
console.log('💡 建议检查文件路径');
}
}
}
return result;
} catch (error) {
console.error('导入过程异常:', error);
return {
success: false,
error: error.message
};
}
}
// 使用带错误处理的导入
const robustResult = await robustImport(filesToImport);性能优化的批量导入
javascript
// 创建高性能导入助手
class HighPerformanceImporter {
constructor(importManager) {
this.importManager = importManager;
this.batchSize = 20; // 批处理大小
this.concurrentImports = 3; // 并发导入数
}
/**
* 批量导入大量文件
* @param {Array} files - 文件列表
* @returns {Object} 导入结果
*/
async importLargeBatch(files) {
console.log(`🔄 开始批量导入 ${files.length} 个文件...`);
// 按文件类型分组,优化导入顺序
const groupedFiles = this.groupFilesByType(files);
const results = {
success: true,
importedFiles: [],
failedFiles: [],
totalProcessed: 0,
totalErrors: 0
};
// 按组处理文件
for (const [fileType, fileGroup] of Object.entries(groupedFiles)) {
console.log(`📁 处理 ${fileType} 类型文件: ${fileGroup.length} 个`);
// 分批处理
const batches = this.chunkArray(fileGroup, this.batchSize);
for (const batch of batches) {
const batchResult = await this.importManager.importFiles(batch, {
mode: 'project_adjacent',
addToComposition: false // 大批量导入时先不添加到合成
});
results.importedFiles.push(...batchResult.importedFiles);
results.failedFiles.push(...batchResult.failedFiles);
results.totalProcessed += batchResult.totalProcessed;
results.totalErrors += batchResult.totalErrors;
if (batchResult.totalErrors > 0) {
results.success = false;
}
// 批次间暂停,避免系统负载过高
await new Promise(resolve => setTimeout(resolve, 100));
}
}
// 如果需要,统一添加到合成
if (results.importedFiles.length > 0 && this.shouldAddToComposition()) {
console.log('🎬 统一添加到合成...');
await this.addToCompositionBatch(results.importedFiles);
}
console.log(`✅ 批量导入完成: ${results.totalProcessed} 个文件,${results.totalErrors} 个失败`);
return results;
}
/**
* 按文件类型分组
* @param {Array} files - 文件列表
* @returns {Object} 分组结果
*/
groupFilesByType(files) {
const grouped = {};
for (const file of files) {
const extension = this.importManager.getFileExtension(file.path).toLowerCase();
const fileType = this.getFileType(extension);
if (!grouped[fileType]) {
grouped[fileType] = [];
}
grouped[fileType].push(file);
}
return grouped;
}
/**
* 获取文件类型
* @param {string} extension - 文件扩展名
* @returns {string} 文件类型
*/
getFileType(extension) {
const imageExts = ['.png', '.jpg', '.jpeg', '.gif', '.tga', '.tiff', '.tif', '.psd'];
const videoExts = ['.mp4', '.mov', '.avi', '.mkv', '.m4v'];
const audioExts = ['.wav', '.mp3', '.aif', '.aiff'];
const vectorExts = ['.ai', '.pdf', '.eps', '.svg'];
if (imageExts.includes(extension)) return 'image';
if (videoExts.includes(extension)) return 'video';
if (audioExts.includes(extension)) return 'audio';
if (vectorExts.includes(extension)) return 'vector';
return 'other';
}
/**
* 分块数组
* @param {Array} array - 原数组
* @param {number} chunkSize - 块大小
* @returns {Array} 分块后的数组
*/
chunkArray(array, chunkSize) {
const chunks = [];
for (let i = 0; i < array.length; i += chunkSize) {
chunks.push(array.slice(i, i + chunkSize));
}
return chunks;
}
/**
* 是否需要添加到合成
* @returns {boolean} 是否添加到合成
*/
shouldAddToComposition() {
// 根据设置或用户选择决定
return this.importManager.settingsManager.getField('import.addToComposition');
}
/**
* 批量添加到合成
* @param {Array} importedFiles - 已导入的文件
*/
async addToCompositionBatch(importedFiles) {
// 这里实现批量添加到合成的逻辑
// 可以根据文件类型和数量优化添加策略
console.log(`🎬 批量添加 ${importedFiles.length} 个文件到合成`);
for (const importedFile of importedFiles) {
try {
await this.importManager.addToComposition(importedFile);
} catch (error) {
console.error(`添加到合成失败 ${importedFile.name}: ${error.message}`);
}
}
}
}
// 使用高性能导入
const highPerformanceImporter = new HighPerformanceImporter(importManager);
const largeFileList = generateLargeFileList(); // 假设有大量文件
const largeImportResult = await highPerformanceImporter.importLargeBatch(largeFileList);
console.log('大量文件导入结果:', largeImportResult);导入状态监控
javascript
// 创建导入状态监控器
class ImportStatusMonitor {
constructor(importManager) {
this.importManager = importManager;
this.monitorInterval = null;
this.statusHistory = [];
this.maxHistory = 100;
}
/**
* 开始监控导入状态
*/
startMonitoring() {
if (this.monitorInterval) {
console.log('📊 导入监控已在运行');
return;
}
this.monitorInterval = setInterval(() => {
this.checkImportStatus();
}, 1000); // 每秒检查一次
console.log('📊 开始监控导入状态');
}
/**
* 停止监控导入状态
*/
stopMonitoring() {
if (this.monitorInterval) {
clearInterval(this.monitorInterval);
this.monitorInterval = null;
console.log('📊 停止监控导入状态');
}
}
/**
* 检查导入状态
*/
checkImportStatus() {
if (this.importManager.currentImport.active) {
const status = {
...this.importManager.currentImport,
timestamp: Date.now(),
active: true
};
this.statusHistory.push(status);
// 限制历史记录大小
if (this.statusHistory.length > this.maxHistory) {
this.statusHistory = this.statusHistory.slice(-this.maxHistory);
}
// 计算导入速度
this.calculateImportSpeed(status);
// 输出监控信息
this.logStatus(status);
}
}
/**
* 计算导入速度
* @param {Object} status - 当前状态
*/
calculateImportSpeed(status) {
if (this.statusHistory.length > 1) {
const previousStatus = this.statusHistory[this.statusHistory.length - 2];
const timeDiff = (status.timestamp - previousStatus.timestamp) / 1000; // 秒
const itemDiff = (status.completed + status.failed) - (previousStatus.completed + previousStatus.failed);
if (timeDiff > 0) {
const speed = itemDiff / timeDiff;
status.importSpeed = speed;
}
}
}
/**
* 输出状态信息
* @param {Object} status - 状态对象
*/
logStatus(status) {
const progress = status.progress.toFixed(1);
const speed = status.importSpeed ? status.importSpeed.toFixed(2) : '0.00';
const eta = status.estimatedTime ? (status.estimatedTime).toFixed(0) : 'unknown';
console.log(`📊 进度: ${progress}% | 速度: ${speed} items/s | 预计剩余: ${eta}s`);
}
/**
* 获取导入统计
* @returns {Object} 统计信息
*/
getStats() {
if (this.statusHistory.length === 0) {
return null;
}
const latest = this.statusHistory[this.statusHistory.length - 1];
const first = this.statusHistory[0];
return {
currentStatus: latest,
totalDuration: (latest.timestamp - first.timestamp) / 1000,
itemsPerSecond: latest.completed / ((latest.timestamp - first.timestamp) / 1000)
};
}
/**
* 导出监控数据
* @returns {string} JSON格式的监控数据
*/
exportData() {
return JSON.stringify({
timestamp: Date.now(),
history: this.statusHistory,
stats: this.getStats()
}, null, 2);
}
}
// 使用导入状态监控器
const monitor = new ImportStatusMonitor(importManager);
monitor.startMonitoring();
// 执行导入
await importManager.importFiles(filesToImport);
// 停止监控
monitor.stopMonitoring();
// 获取统计信息
const stats = monitor.getStats();
console.log('导入统计:', stats);最佳实践
使用建议
导入前准备
javascript
// 创建导入准备助手
class ImportPreparationHelper {
constructor(importManager) {
this.importManager = importManager;
}
/**
* 准备导入操作
* @param {Array} files - 文件列表
* @returns {Object} 准备结果
*/
async prepareImport(files) {
console.log('🔍 准备导入操作...');
// 1. 验证导入条件
const conditionsValid = await this.validateImportConditions();
if (!conditionsValid) {
return {
success: false,
error: '导入条件验证失败'
};
}
// 2. 验证所有文件
const validationResults = await this.validateAllFiles(files);
if (!validationResults.allValid) {
return {
success: false,
error: '部分文件验证失败',
validationResults: validationResults
};
}
// 3. 检查磁盘空间(如果使用复制模式)
const hasEnoughSpace = await this.checkDiskSpace(files);
if (!hasEnoughSpace) {
return {
success: false,
error: '磁盘空间不足'
};
}
// 4. 获取导入设置
const importSettings = await this.getImportSettings();
console.log('✅ 导入准备完成');
return {
success: true,
validFiles: validationResults.validFiles,
invalidFiles: validationResults.invalidFiles,
importSettings: importSettings
};
}
/**
* 验证导入条件
* @returns {boolean} 条件是否有效
*/
async validateImportConditions() {
try {
// 检查项目是否存在
const projectValid = await this.importManager.projectStatusChecker.validateProjectStatus({
requireProject: true,
requireActiveComposition: false,
showWarning: false
});
if (!projectValid) {
console.error('❌ AE项目状态无效');
return false;
}
// 检查Eagle连接
const eagleConnected = await this.importManager.eagleConnectionManager.validateConnection();
if (!eagleConnected) {
console.error('❌ Eagle连接无效');
return false;
}
return true;
} catch (error) {
console.error('验证导入条件失败:', error.message);
return false;
}
}
/**
* 验证所有文件
* @param {Array} files - 文件列表
* @returns {Object} 验证结果
*/
async validateAllFiles(files) {
const validFiles = [];
const invalidFiles = [];
for (const file of files) {
const validation = this.importManager.validateFileForImport(file);
if (validation.valid) {
validFiles.push(file);
} else {
invalidFiles.push({
file: file,
error: validation.error
});
}
}
return {
allValid: invalidFiles.length === 0,
validFiles: validFiles,
invalidFiles: invalidFiles
};
}
/**
* 检查磁盘空间
* @param {Array} files - 文件列表
* @returns {boolean} 空间是否充足
*/
async checkDiskSpace(files) {
try {
// 计算总文件大小
const totalSize = files.reduce((sum, file) => sum + (file.size || 0), 0);
// 获取可用空间(这里简化处理,实际需要根据目标位置检查)
// 在实际实现中,需要检查目标文件夹的可用空间
console.log(`📊 需要空间: ${(totalSize / (1024 * 1024)).toFixed(2)} MB`);
// 假设空间充足(实际应用中需要真实检查)
return true;
} catch (error) {
console.error('检查磁盘空间失败:', error.message);
return false;
}
}
/**
* 获取导入设置
* @returns {Object} 导入设置
*/
async getImportSettings() {
return {
mode: this.importManager.settingsManager.getField('import.mode'),
addToComposition: this.importManager.settingsManager.getField('import.addToComposition'),
timelineOptions: this.importManager.settingsManager.getField('import.timelineOptions'),
projectAdjacentFolder: this.importManager.settingsManager.getField('import.projectAdjacentFolder'),
customFolderPath: this.importManager.settingsManager.getField('import.customFolderPath')
};
}
}
// 使用导入准备助手
const preparationHelper = new ImportPreparationHelper(importManager);
const preparationResult = await preparationHelper.prepareImport(filesToImport);
if (preparationResult.success) {
console.log('✅ 导入准备成功,开始导入文件');
// 使用验证后的文件列表进行导入
const importResult = await importManager.importFiles(preparationResult.validFiles);
console.log('导入结果:', importResult);
} else {
console.log('❌ 导入准备失败:', preparationResult.error);
if (preparationResult.validationResults) {
console.log('无效文件:', preparationResult.validationResults.invalidFiles);
}
}内存管理
javascript
// 创建导入内存管理器
class ImportMemoryManager {
constructor(importManager) {
this.importManager = importManager;
this.monitorInterval = null;
this.gcThreshold = 50 * 1024 * 1024; // 50MB
}
/**
* 开始内存监控
*/
startMonitoring() {
if (this.monitorInterval) {
return;
}
this.monitorInterval = setInterval(() => {
this.checkMemoryUsage();
}, 5000); // 每5秒检查一次
console.log('📊 开始内存监控');
}
/**
* 停止内存监控
*/
stopMonitoring() {
if (this.monitorInterval) {
clearInterval(this.monitorInterval);
this.monitorInterval = null;
console.log('📊 停止内存监控');
}
}
/**
* 检查内存使用
*/
checkMemoryUsage() {
if (performance.memory) {
const memory = performance.memory;
const usage = memory.usedJSHeapSize;
const total = memory.totalJSHeapSize;
const limit = memory.jsHeapSizeLimit;
console.log(`📊 内存使用: ${(usage / 1024 / 1024).toFixed(2)}MB / ${(total / 1024 / 1024).toFixed(2)}MB`);
if (usage > this.gcThreshold) {
console.warn(`⚠️ 内存使用过高: ${(usage / 1024 / 1024).toFixed(2)}MB`);
// 执行内存清理
this.performCleanup();
}
}
}
/**
* 执行清理操作
*/
performCleanup() {
console.log('🧹 执行内存清理...');
// 清理不必要的临时数据
if (this.importManager.activeImports && this.importManager.activeImports.size === 0) {
// 清理导入队列中的完成项
this.importManager.importQueue = this.importManager.importQueue.filter(
item => item.status !== 'complete' && item.status !== 'error'
);
}
// 强制垃圾回收(如果可用)
if (window.gc) {
window.gc();
console.log('🧹 手动垃圾回收完成');
}
}
}
// 使用内存管理器
const memoryManager = new ImportMemoryManager(importManager);
memoryManager.startMonitoring();性能优化
批量处理优化
javascript
// 创建批量处理优化器
class BatchImportOptimizer {
constructor(importManager) {
this.importManager = importManager;
this.batchSize = 10;
this.concurrentBatches = 3;
}
/**
* 优化批量导入
* @param {Array} files - 文件列表
* @param {Object} options - 导入选项
* @returns {Object} 优化的导入结果
*/
async optimizeBatchImport(files, options = {}) {
console.log(`🔄 优化批量导入 ${files.length} 个文件...`);
// 1. 按文件类型分组
const groupedFiles = this.groupFilesByType(files);
// 2. 按大小排序(小文件优先,提高初始反馈速度)
for (const group of Object.values(groupedFiles)) {
group.sort((a, b) => (a.size || 0) - (b.size || 0));
}
// 3. 执行优化的批量导入
const results = await this.executeOptimizedImport(groupedFiles, options);
return results;
}
/**
* 按文件类型分组
* @param {Array} files - 文件列表
* @returns {Object} 分组结果
*/
groupFilesByType(files) {
const grouped = {
images: [],
videos: [],
audios: [],
vectors: [],
others: []
};
for (const file of files) {
const extension = this.importManager.getFileExtension(file.path).toLowerCase();
if (this.isImageFile(extension)) {
grouped.images.push(file);
} else if (this.isVideoFile(extension)) {
grouped.videos.push(file);
} else if (this.isAudioFile(extension)) {
grouped.audios.push(file);
} else if (this.isVectorFile(extension)) {
grouped.vectors.push(file);
} else {
grouped.others.push(file);
}
}
return grouped;
}
/**
* 判断是否为图片文件
* @param {string} extension - 文件扩展名
* @returns {boolean} 是否为图片
*/
isImageFile(extension) {
return ['.png', '.jpg', '.jpeg', '.gif', '.tga', '.tiff', '.tif', '.psd', '.bmp'].includes(extension);
}
/**
* 判断是否为视频文件
* @param {string} extension - 文件扩展名
* @returns {boolean} 是否为视频
*/
isVideoFile(extension) {
return ['.mp4', '.mov', '.avi', '.mkv', '.m4v', '.wmv', '.flv'].includes(extension);
}
/**
* 判断是否为音频文件
* @param {string} extension - 文件扩展名
* @returns {boolean} 是否为音频
*/
isAudioFile(extension) {
return ['.wav', '.mp3', '.aif', '.aiff', '.flac', '.m4a'].includes(extension);
}
/**
* 判断是否为矢量文件
* @param {string} extension - 文件扩展名
* @returns {boolean} 是否为矢量
*/
isVectorFile(extension) {
return ['.ai', '.pdf', '.eps', '.svg'].includes(extension);
}
/**
* 执行优化的导入
* @param {Object} groupedFiles - 分组的文件
* @param {Object} options - 导入选项
* @returns {Object} 导入结果
*/
async executeOptimizedImport(groupedFiles, options) {
const allResults = {
success: true,
importedFiles: [],
failedFiles: [],
totalProcessed: 0,
totalErrors: 0
};
// 按优先级处理不同类型的文件
const importOrder = ['images', 'audios', 'vectors', 'videos', 'others'];
for (const fileType of importOrder) {
const fileGroup = groupedFiles[fileType];
if (fileGroup && fileGroup.length > 0) {
console.log(`🔍 处理 ${fileType} 类型文件: ${fileGroup.length} 个`);
const groupResult = await this.importFileGroup(fileGroup, options);
allResults.importedFiles.push(...groupResult.importedFiles);
allResults.failedFiles.push(...groupResult.failedFiles);
allResults.totalProcessed += groupResult.totalProcessed;
allResults.totalErrors += groupResult.totalErrors;
if (groupResult.totalErrors > 0) {
allResults.success = false;
}
}
}
return allResults;
}
/**
* 导入文件组
* @param {Array} fileGroup - 文件组
* @param {Object} options - 导入选项
* @returns {Object} 导入结果
*/
async importFileGroup(fileGroup, options) {
const results = {
success: true,
importedFiles: [],
failedFiles: [],
totalProcessed: 0,
totalErrors: 0
};
// 分批处理
const batches = this.chunkArray(fileGroup, this.batchSize);
for (const batch of batches) {
const batchResult = await this.importManager.importFiles(batch, options);
results.importedFiles.push(...batchResult.importedFiles);
results.failedFiles.push(...batchResult.failedFiles);
results.totalProcessed += batchResult.totalProcessed;
results.totalErrors += batchResult.totalErrors;
if (batchResult.totalErrors > 0) {
results.success = false;
}
// 批次间小延迟,避免系统过载
await new Promise(resolve => setTimeout(resolve, 50));
}
return results;
}
/**
* 分块数组
* @param {Array} array - 原数组
* @param {number} chunkSize - 块大小
* @returns {Array} 分块后的数组
*/
chunkArray(array, chunkSize) {
const chunks = [];
for (let i = 0; i < array.length; i += chunkSize) {
chunks.push(array.slice(i, i + chunkSize));
}
return chunks;
}
}
// 使用批量处理优化器
const optimizer = new BatchImportOptimizer(importManager);
const optimizedResult = await optimizer.optimizeBatchImport(filesToImport, {
mode: 'project_adjacent',
addToComposition: true
});
console.log('优化批量导入结果:', optimizedResult);故障排除
常见问题
导入失败
- 症状:调用
importFiles后文件未成功导入AE项目 - 解决:
- 检查AE项目是否打开且处于活动状态
- 验证Eagle连接是否正常
- 检查文件路径和权限
- 查看详细的错误日志
路径解析错误
- 症状:文件路径包含特殊字符或过长导致解析失败
- 解决:
- 检查文件路径格式
- 验证路径长度限制
- 确认路径中无非法字符
- 使用规范化路径函数
内存不足
- 症状:大量文件导入时出现内存溢出错误
- 解决:
- 减少批量导入数量
- 实施分批处理策略
- 增加批量处理间隔
- 监控内存使用情况
权限错误
- 症状:无法创建文件夹或复制文件
- 解决:
- 检查目标文件夹权限
- 验证源文件访问权限
- 确认AE扩展权限设置
- 尝试使用不同的目标路径
调试技巧
启用详细日志
javascript
// 在控制台中启用详细日志
importManager.config.enableLogging = true;
importManager.logManager.config.logLevel = 'debug';
// 监控所有导入操作
importManager.callbacks.onProgress = (progressData) => {
importManager.log(`📊 进度: ${progressData.progress.toFixed(1)}%`, 'debug');
};
importManager.callbacks.onError = (error) => {
importManager.log(`❌ 错误: ${error.message}`, 'error', { stack: error.stack });
};导入诊断工具
javascript
// 创建导入诊断工具
class ImportDiagnostics {
constructor(importManager) {
this.importManager = importManager;
}
/**
* 运行导入诊断
*/
async runDiagnostics() {
console.log('🔍 开始导入诊断...');
// 1. 检查环境状态
console.log('1. 环境状态检查...');
await this.checkEnvironment();
// 2. 检查配置
console.log('2. 配置检查...');
await this.checkConfiguration();
// 3. 测试基本功能
console.log('3. 功能测试...');
await this.testBasicOperations();
// 4. 检查性能
console.log('4. 性能检查...');
await this.checkPerformance();
console.log('✅ 导入诊断完成');
}
/**
* 检查环境状态
*/
async checkEnvironment() {
// 检查AE项目状态
const projectStatus = await this.importManager.projectStatusChecker.getProjectStatus();
console.log(` AE项目: ${projectStatus.projectExists ? '已打开' : '未打开'}`);
// 检查Eagle连接状态
const eagleStatus = await this.importManager.eagleConnectionManager.getConnectionInfo();
console.log(` Eagle连接: ${eagleStatus.connected ? '已连接' : '未连接'}`);
// 检查内存使用
if (performance.memory) {
const memory = performance.memory;
console.log(` 内存使用: ${(memory.usedJSHeapSize / 1024 / 1024).toFixed(2)}MB`);
}
}
/**
* 检查配置
*/
async checkConfiguration() {
const settings = this.importManager.settingsManager.getSettings();
console.log(` 导入模式: ${settings.import.mode}`);
console.log(` 添加到合成: ${settings.import.addToComposition}`);
console.log(` 项目旁文件夹: ${settings.import.projectAdjacentFolder}`);
console.log(` 自定义路径: ${settings.import.customFolderPath}`);
}
/**
* 测试基本操作
*/
async testBasicOperations() {
// 测试路径操作
const testPath = 'C:/Test/Path/file.png';
console.log(` 路径提取: ${this.importManager.extractFolderPath(testPath)}`);
console.log(` 文件名提取: ${this.importManager.extractFileName(testPath)}`);
// 测试文件验证
const testFile = { name: 'test.png', path: testPath };
const validation = this.importManager.validateFileForImport(testFile);
console.log(` 文件验证: ${validation.valid ? '通过' : '失败'}`);
}
/**
* 检查性能
*/
async checkPerformance() {
const startTime = performance.now();
// 模拟简单操作
for (let i = 0; i < 1000; i++) {
const path = this.importManager.normalizePath(`C:\\Test\\Path\\file${i}.png`);
}
const endTime = performance.now();
console.log(` 路径处理性能: 1000次操作耗时 ${(endTime - startTime).toFixed(2)}ms`);
}
}
// 运行诊断
const diagnostics = new ImportDiagnostics(importManager);
await diagnostics.runDiagnostics();扩展性
自定义扩展
扩展导入管理器
javascript
// 创建自定义导入管理器
class CustomImportManager extends ImportManager {
constructor(aeExtension) {
super(aeExtension);
// 添加自定义属性
this.cloudStorageEnabled = false;
this.cloudStorageConfig = null;
this.metadataExtractor = null;
this.previewGenerator = null;
this.importValidationRules = new Set();
// 初始化扩展功能
this.initCustomFeatures();
}
initCustomFeatures() {
// 初始化元数据提取器
this.initMetadataExtractor();
// 初始化预览生成器
this.initPreviewGenerator();
// 添加自定义验证规则
this.addCustomValidationRules();
}
initMetadataExtractor() {
this.metadataExtractor = {
extract: async (filePath) => {
// 模拟元数据提取
return {
dimensions: { width: 1920, height: 1080 },
duration: null,
frameRate: null,
colorSpace: 'RGB',
bitDepth: 8,
createdDate: new Date().toISOString()
};
}
};
}
initPreviewGenerator() {
this.previewGenerator = {
generate: async (filePath, options = {}) => {
// 模拟预览生成
return {
thumbnail: null, // 实际应用中会返回缩略图数据
previewVideo: null,
metadata: await this.metadataExtractor.extract(filePath)
};
}
};
}
addCustomValidationRules() {
// 添加文件大小验证规则
this.importValidationRules.add(async (file) => {
if (file.size > 100 * 1024 * 1024) { // 100MB
return {
valid: false,
error: '文件大小超过100MB限制'
};
}
return { valid: true };
});
// 添加路径长度验证规则
this.importValidationRules.add((file) => {
if (file.path.length > 200) {
return {
valid: false,
error: '文件路径过长,超过200字符限制'
};
}
return { valid: true };
});
}
/**
* 重写文件验证方法,添加自定义验证
* @param {Object} file - 文件对象
* @returns {Object} 验证结果
*/
async validateFileForImport(file) {
// 首先执行父类的基本验证
const basicValidation = super.validateFileForImport(file);
if (!basicValidation.valid) {
return basicValidation;
}
// 执行自定义验证规则
for (const rule of this.importValidationRules) {
try {
const ruleResult = await rule(file);
if (!ruleResult.valid) {
return ruleResult;
}
} catch (error) {
this.log(`自定义验证规则执行失败: ${error.message}`, 'warning');
// 验证规则执行失败不应该阻止导入
continue;
}
}
return { valid: true };
}
/**
* 从云端存储导入
* @param {string} cloudPath - 云端路径
* @param {Object} options - 选项
* @returns {Object} 导入结果
*/
async importFromCloud(cloudPath, options = {}) {
if (!this.cloudStorageEnabled) {
return {
success: false,
error: '云端存储功能未启用'
};
}
try {
this.log(`📥 从云端导入: ${cloudPath}`, 'debug');
// 下载云端文件到临时位置
const downloadResult = await this.downloadFromCloud(cloudPath);
if (!downloadResult.success) {
throw new Error(downloadResult.error);
}
// 导入下载的文件
const importResult = await this.importFile({
name: downloadResult.fileName,
path: downloadResult.localPath,
size: downloadResult.fileSize,
type: 'downloaded'
}, options);
// 清理临时文件
setTimeout(() => {
this.cleanupTempFiles([{ path: downloadResult.localPath }]);
}, 5000); // 5秒后清理
if (importResult.success) {
return {
success: true,
importedFile: {
...importResult.importedFile,
cloudPath: cloudPath,
downloadTime: downloadResult.downloadTime
}
};
} else {
throw new Error(importResult.error);
}
} catch (error) {
this.log(`❌ 云端导入失败: ${error.message}`, 'error');
return {
success: false,
error: error.message
};
}
}
/**
* 下载云端文件
* @param {string} cloudPath - 云端路径
* @returns {Object} 下载结果
*/
async downloadFromCloud(cloudPath) {
try {
// 模拟云端下载
if (window.__DEMO_MODE_ACTIVE__) {
// Demo模式:模拟下载
const fileName = this.extractFileName(cloudPath);
const localPath = `C:/Temp/${fileName}`;
return {
success: true,
fileName: fileName,
localPath: localPath,
fileSize: 1024000, // 1MB
downloadTime: 1000 // 1秒
};
}
// 实际云端下载逻辑
// 这里会根据具体的云存储服务实现
const response = await fetch(cloudPath);
if (!response.ok) {
throw new Error(`下载失败: ${response.status} ${response.statusText}`);
}
const blob = await response.blob();
const fileName = this.extractFileName(cloudPath);
// 保存到本地临时位置
const localPath = await this.saveBlobToFile(blob, fileName);
return {
success: true,
fileName: fileName,
localPath: localPath,
fileSize: blob.size,
downloadTime: Date.now() - startTime
};
} catch (error) {
return {
success: false,
error: error.message
};
}
}
/**
* 保存Blob到文件
* @param {Blob} blob - Blob对象
* @param {string} fileName - 文件名
* @returns {string} 本地文件路径
*/
async saveBlobToFile(blob, fileName) {
// 在实际实现中,这会将Blob保存到本地文件
// 由于浏览器限制,这通常需要用户手动保存
return `C:/Temp/${fileName}`;
}
/**
* 生成文件预览
* @param {string} filePath - 文件路径
* @param {Object} options - 选项
* @returns {Object} 预览结果
*/
async generatePreview(filePath, options = {}) {
try {
this.log(`🔍 生成预览: ${filePath}`, 'debug');
const preview = await this.previewGenerator.generate(filePath, options);
this.log(`✅ 预览生成完成`, 'success');
return {
success: true,
preview: preview
};
} catch (error) {
this.log(`❌ 预览生成失败: ${error.message}`, 'error');
return {
success: false,
error: error.message
};
}
}
/**
* 提取文件元数据
* @param {string} filePath - 文件路径
* @returns {Object} 元数据
*/
async extractMetadata(filePath) {
try {
this.log(`🔍 提取元数据: ${filePath}`, 'debug');
const metadata = await this.metadataExtractor.extract(filePath);
this.log(`✅ 元数据提取完成`, 'success');
return {
success: true,
metadata: metadata
};
} catch (error) {
this.log(`❌ 元数据提取失败: ${error.message}`, 'error');
return {
success: false,
error: error.message
};
}
}
}
// 使用自定义导入管理器
const customImportManager = new CustomImportManager(aeExtension);
// 生成文件预览
const previewResult = await customImportManager.generatePreview('C:/test/image.png');
console.log('预览结果:', previewResult);
// 提取文件元数据
const metadataResult = await customImportManager.extractMetadata('C:/test/video.mp4');
console.log('元数据结果:', metadataResult);插件化架构
javascript
// 创建导入插件系统
class ImportPluginManager {
constructor(importManager) {
this.importManager = importManager;
this.plugins = new Map();
this.hooks = new Map();
}
/**
* 注册导入插件
* @param {string} pluginId - 插件ID
* @param {Object} plugin - 插件对象
*/
registerPlugin(pluginId, plugin) {
this.plugins.set(pluginId, plugin);
// 绑定插件钩子
if (plugin.onBeforeImport) {
this.addHook('beforeImport', plugin.onBeforeImport.bind(plugin));
}
if (plugin.onAfterImport) {
this.addHook('afterImport', plugin.onAfterImport.bind(plugin));
}
if (plugin.onImportError) {
this.addHook('importError', plugin.onImportError.bind(plugin));
}
if (plugin.onFileValidate) {
this.addHook('fileValidate', plugin.onFileValidate.bind(plugin));
}
this.importManager.log(`🔌 插件已注册: ${pluginId}`, 'debug');
}
/**
* 添加钩子函数
* @param {string} hookName - 钩子名称
* @param {Function} callback - 回调函数
*/
addHook(hookName, callback) {
if (!this.hooks.has(hookName)) {
this.hooks.set(hookName, []);
}
const hooks = this.hooks.get(hookName);
hooks.push(callback);
}
/**
* 移除钩子函数
* @param {string} hookName - 钩子名称
* @param {Function} callback - 回调函数
*/
removeHook(hookName, callback) {
if (this.hooks.has(hookName)) {
const hooks = this.hooks.get(hookName);
const index = hooks.indexOf(callback);
if (index !== -1) {
hooks.splice(index, 1);
}
}
}
/**
* 触发钩子
* @param {string} hookName - 钩子名称
* @param {...any} args - 参数
* @returns {Array} 执行结果
*/
async triggerHook(hookName, ...args) {
const results = [];
if (this.hooks.has(hookName)) {
const hooks = this.hooks.get(hookName);
for (const hook of hooks) {
try {
const result = await hook(...args);
results.push(result);
} catch (error) {
this.importManager.log(`钩子执行失败 ${hookName}: ${error.message}`, 'error');
}
}
}
return results;
}
/**
* 执行导入前的插件逻辑
* @param {Array} files - 文件列表
* @param {Object} options - 选项
* @returns {Object} 处理结果
*/
async executeBeforeImport(files, options) {
const pluginResults = await this.triggerHook('beforeImport', files, options);
return { success: true, pluginResults };
}
/**
* 执行导入后的插件逻辑
* @param {Object} importResult - 导入结果
* @returns {Object} 处理结果
*/
async executeAfterImport(importResult) {
const pluginResults = await this.triggerHook('afterImport', importResult);
return { success: true, pluginResults };
}
}
// 示例插件:水印添加插件
const watermarkPlugin = {
id: 'watermark',
name: 'Watermark Plugin',
onBeforeImport: async (files, options) => {
console.log('🖼️ 水印插件: 准备添加水印');
// 这里可以实现水印添加逻辑
// 例如:修改文件路径以指向水印处理后的副本
return { success: true };
},
onAfterImport: async (importResult) => {
console.log('🖼️ 水印插件: 导入完成,应用水印设置');
// 这里可以实现导入后的水印处理
return { success: true };
}
};
// 示例插件:格式转换插件
const formatConverterPlugin = {
id: 'formatConverter',
name: 'Format Converter Plugin',
onFileValidate: async (file) => {
console.log(`🔄 格式转换插件: 验证文件 ${file.name}`);
// 检查是否需要格式转换
const supportedFormats = this.importManager.getAllowedFileExtensions();
const fileExt = this.importManager.getFileExtension(file.path).toLowerCase();
if (!supportedFormats.includes(fileExt)) {
// 可以尝试转换格式
console.log(`📁 不支持的格式 ${fileExt},可能需要转换`);
}
return { success: true };
}
};
// 使用插件管理器
const pluginManager = new ImportPluginManager(importManager);
// 注册插件
pluginManager.registerPlugin('watermark', watermarkPlugin);
pluginManager.registerPlugin('formatConverter', formatConverterPlugin);
// 在导入前后执行插件
const testFiles = [{ name: 'test.png', path: 'C:/test/test.png' }];
const testOptions = { mode: 'project_adjacent' };
// 执行导入前插件
await pluginManager.executeBeforeImport(testFiles, testOptions);
// 执行实际导入
const importResult = await importManager.importFiles(testFiles, testOptions);
// 执行导入后插件
await pluginManager.executeAfterImport(importResult);