feat: additional metadata

This commit is contained in:
2026-02-13 15:23:32 +01:00
parent 1ddf0a05e8
commit 55f37f4dfa
6 changed files with 351 additions and 76 deletions

View File

@@ -84,6 +84,9 @@ export interface ImportAnalysisReport {
export class ImportAnalysisEngine {
private currentProjectId: string = '';
private turndown: TurndownService;
// Progress callback for reporting analysis steps
onProgress?: (step: string, detail?: string) => void;
constructor() {
this.turndown = new TurndownService({
@@ -100,6 +103,8 @@ export class ImportAnalysisEngine {
async analyzeWxr(wxrData: WxrData, sourceFile: string, uploadsFolder?: string): Promise<ImportAnalysisReport> {
const db = getDatabase().getLocal();
this.onProgress?.('Loading existing posts...');
// Fetch existing posts for this project
const existingPosts = await db
.select({
@@ -112,6 +117,8 @@ export class ImportAnalysisEngine {
.where(eq(posts.projectId, this.currentProjectId))
.all();
this.onProgress?.('Loading existing media...', `${existingPosts.length} posts in project`);
// Fetch existing media for this project
const existingMedia = await db
.select({
@@ -123,6 +130,8 @@ export class ImportAnalysisEngine {
.where(eq(media.projectId, this.currentProjectId))
.all();
this.onProgress?.('Loading existing tags...', `${existingMedia.length} media in project`);
// Fetch existing tags for this project
const existingTags = await db
.select({
@@ -155,13 +164,22 @@ export class ImportAnalysisEngine {
// Build tag set
const existingTagNames = new Set(existingTags.map(t => t.name.toLowerCase()));
this.onProgress?.('Analyzing posts...', `${wxrData.posts.length} posts to analyze`);
// Analyze posts
const analyzedPosts = this.analyzePostItems(wxrData.posts, slugToPost, checksumToPost);
this.onProgress?.('Analyzing pages...', `${wxrData.pages.length} pages to analyze`);
const analyzedPages = this.analyzePostItems(wxrData.pages, slugToPost, checksumToPost);
this.onProgress?.('Analyzing media files...', `${wxrData.media.length} media files to analyze`);
// Analyze media
const analyzedMedia = await this.analyzeMediaItems(wxrData.media, nameToMedia, checksumToMedia, uploadsFolder);
this.onProgress?.('Processing categories and tags...');
// Analyze categories
const analyzedCategories: AnalyzedCategory[] = wxrData.categories.map(cat => ({
name: cat.name,

View File

@@ -747,7 +747,14 @@ export function registerIpcHandlers(): void {
// ============ Import Analysis Handlers ============
// Helper to emit progress events
const emitImportProgress = (step: string, detail?: string) => {
ipcMain.emit('forward-to-renderer', 'import:progress', { step, detail });
};
safeHandle('import:selectAndAnalyze', async (_, uploadsFolder?: string) => {
emitImportProgress('Selecting file...');
const result = await dialog.showOpenDialog({
title: 'Select WordPress Export File (WXR)',
filters: [
@@ -762,12 +769,18 @@ export function registerIpcHandlers(): void {
}
const filePath = result.filePaths[0];
const fileName = filePath.split(/[/\\]/).pop() || filePath;
emitImportProgress('Parsing WXR file...', fileName);
const { WxrParser } = await import('../engine/WxrParser');
const { ImportAnalysisEngine } = await import('../engine/ImportAnalysisEngine');
const parser = new WxrParser();
const wxrData = await parser.parseFile(filePath);
emitImportProgress('Loading project data...', `Found ${wxrData.posts.length} posts, ${wxrData.media.length} media`);
const analysisEngine = new ImportAnalysisEngine();
const projectEngine = getProjectEngine();
const activeProject = await projectEngine.getActiveProject();
@@ -775,16 +788,33 @@ export function registerIpcHandlers(): void {
analysisEngine.setProjectContext(activeProject.id);
}
return analysisEngine.analyzeWxr(wxrData, filePath, uploadsFolder || undefined);
emitImportProgress('Analyzing posts...', `${wxrData.posts.length} posts`);
// Add progress callback to engine
analysisEngine.onProgress = (step: string, detail?: string) => {
emitImportProgress(step, detail);
};
const report = await analysisEngine.analyzeWxr(wxrData, filePath, uploadsFolder || undefined);
emitImportProgress('Analysis complete');
return report;
});
safeHandle('import:analyzeFile', async (_, filePath: string, uploadsFolder?: string) => {
const fileName = filePath.split(/[/\\]/).pop() || filePath;
emitImportProgress('Parsing WXR file...', fileName);
const { WxrParser } = await import('../engine/WxrParser');
const { ImportAnalysisEngine } = await import('../engine/ImportAnalysisEngine');
const parser = new WxrParser();
const wxrData = await parser.parseFile(filePath);
emitImportProgress('Loading project data...', `Found ${wxrData.posts.length} posts, ${wxrData.media.length} media`);
const analysisEngine = new ImportAnalysisEngine();
const projectEngine = getProjectEngine();
const activeProject = await projectEngine.getActiveProject();
@@ -792,7 +822,18 @@ export function registerIpcHandlers(): void {
analysisEngine.setProjectContext(activeProject.id);
}
return analysisEngine.analyzeWxr(wxrData, filePath, uploadsFolder || undefined);
emitImportProgress('Analyzing posts...');
// Add progress callback to engine
analysisEngine.onProgress = (step: string, detail?: string) => {
emitImportProgress(step, detail);
};
const report = await analysisEngine.analyzeWxr(wxrData, filePath, uploadsFolder || undefined);
emitImportProgress('Analysis complete');
return report;
});
safeHandle('import:selectUploadsFolder', async () => {

View File

@@ -155,6 +155,11 @@ contextBridge.exposeInMainWorld('electronAPI', {
selectAndAnalyze: (uploadsFolder?: string) => ipcRenderer.invoke('import:selectAndAnalyze', uploadsFolder),
analyzeFile: (filePath: string, uploadsFolder?: string) => ipcRenderer.invoke('import:analyzeFile', filePath, uploadsFolder),
selectUploadsFolder: () => ipcRenderer.invoke('import:selectUploadsFolder'),
onProgress: (callback: (data: { step: string; detail?: string }) => void) => {
const subscription = (_event: Electron.IpcRendererEvent, data: { step: string; detail?: string }) => callback(data);
ipcRenderer.on('import:progress', subscription);
return () => ipcRenderer.removeListener('import:progress', subscription);
},
},
// Import Definition CRUD
@@ -340,6 +345,7 @@ export interface ElectronAPI {
selectAndAnalyze: (uploadsFolder?: string) => Promise<unknown>;
analyzeFile: (filePath: string, uploadsFolder?: string) => Promise<unknown>;
selectUploadsFolder: () => Promise<string | null>;
onProgress: (callback: (data: { step: string; detail?: string }) => void) => () => void;
};
importDefinitions: {
create: (name?: string) => Promise<unknown>;