feat: author support and UI support for multi-category
This commit is contained in:
@@ -57,6 +57,7 @@ export const media = sqliteTable('media', {
|
||||
title: text('title'),
|
||||
alt: text('alt'),
|
||||
caption: text('caption'),
|
||||
author: text('author'),
|
||||
filePath: text('file_path').notNull(),
|
||||
sidecarPath: text('sidecar_path').notNull(),
|
||||
createdAt: integer('created_at', { mode: 'timestamp' }).notNull(),
|
||||
|
||||
@@ -36,6 +36,8 @@ import type { WxrPost, WxrMedia } from './WxrParser';
|
||||
export interface ImportExecutionOptions {
|
||||
/** Path to the WordPress uploads folder for media files */
|
||||
uploadsFolder?: string;
|
||||
/** Default author to use when WXR post/media has no author */
|
||||
defaultAuthor?: string;
|
||||
/** Progress callback */
|
||||
onProgress?: (phase: string, current: number, total: number, detail?: string) => void;
|
||||
}
|
||||
@@ -461,7 +463,7 @@ export class ImportExecutionEngine extends EventEmitter {
|
||||
excerpt: wxrPost.excerpt || undefined,
|
||||
content: transformedContent,
|
||||
status,
|
||||
author: wxrPost.creator || undefined,
|
||||
author: wxrPost.creator || options.defaultAuthor || undefined,
|
||||
createdAt,
|
||||
updatedAt,
|
||||
publishedAt,
|
||||
@@ -634,6 +636,7 @@ export class ImportExecutionEngine extends EventEmitter {
|
||||
title: wxrMedia.title || undefined,
|
||||
alt: wxrMedia.description || undefined,
|
||||
mimeType: wxrMedia.mimeType,
|
||||
author: options.defaultAuthor,
|
||||
tags: [],
|
||||
linkedPostIds,
|
||||
createdAt,
|
||||
|
||||
@@ -30,6 +30,7 @@ export interface MediaData {
|
||||
title?: string;
|
||||
alt?: string;
|
||||
caption?: string;
|
||||
author?: string;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
tags: string[];
|
||||
@@ -46,6 +47,7 @@ export interface MediaMetadata {
|
||||
title?: string;
|
||||
alt?: string;
|
||||
caption?: string;
|
||||
author?: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
tags: string[];
|
||||
@@ -317,6 +319,7 @@ export class MediaEngine extends EventEmitter {
|
||||
title: mediaData.title,
|
||||
alt: mediaData.alt,
|
||||
caption: mediaData.caption,
|
||||
author: mediaData.author,
|
||||
createdAt: mediaData.createdAt.toISOString(),
|
||||
updatedAt: mediaData.updatedAt.toISOString(),
|
||||
tags: mediaData.tags,
|
||||
@@ -337,6 +340,7 @@ export class MediaEngine extends EventEmitter {
|
||||
if (metadata.title) lines.push(`title: "${metadata.title}"`);
|
||||
if (metadata.alt) lines.push(`alt: "${metadata.alt}"`);
|
||||
if (metadata.caption) lines.push(`caption: "${metadata.caption}"`);
|
||||
if (metadata.author) lines.push(`author: "${metadata.author}"`);
|
||||
|
||||
lines.push(`createdAt: ${metadata.createdAt}`);
|
||||
lines.push(`updatedAt: ${metadata.updatedAt}`);
|
||||
@@ -410,6 +414,9 @@ export class MediaEngine extends EventEmitter {
|
||||
case 'caption':
|
||||
metadata.caption = value;
|
||||
break;
|
||||
case 'author':
|
||||
metadata.author = value;
|
||||
break;
|
||||
case 'createdAt':
|
||||
metadata.createdAt = value;
|
||||
break;
|
||||
@@ -514,6 +521,7 @@ export class MediaEngine extends EventEmitter {
|
||||
title: metadata?.title,
|
||||
alt: metadata?.alt,
|
||||
caption: metadata?.caption,
|
||||
author: metadata?.author,
|
||||
createdAt,
|
||||
updatedAt,
|
||||
tags: metadata?.tags || [],
|
||||
@@ -541,6 +549,7 @@ export class MediaEngine extends EventEmitter {
|
||||
title: mediaData.title,
|
||||
alt: mediaData.alt,
|
||||
caption: mediaData.caption,
|
||||
author: mediaData.author,
|
||||
filePath: destPath,
|
||||
sidecarPath,
|
||||
createdAt: mediaData.createdAt,
|
||||
@@ -591,6 +600,7 @@ export class MediaEngine extends EventEmitter {
|
||||
title: updated.title,
|
||||
alt: updated.alt,
|
||||
caption: updated.caption,
|
||||
author: updated.author,
|
||||
updatedAt: updated.updatedAt,
|
||||
tags: JSON.stringify(updated.tags),
|
||||
})
|
||||
@@ -740,6 +750,7 @@ export class MediaEngine extends EventEmitter {
|
||||
title: dbMedia.title || undefined,
|
||||
alt: dbMedia.alt || undefined,
|
||||
caption: dbMedia.caption || undefined,
|
||||
author: dbMedia.author || undefined,
|
||||
createdAt: dbMedia.createdAt,
|
||||
updatedAt: dbMedia.updatedAt,
|
||||
tags: JSON.parse(dbMedia.tags || '[]'),
|
||||
@@ -766,6 +777,7 @@ export class MediaEngine extends EventEmitter {
|
||||
title: dbMedia.title || undefined,
|
||||
alt: dbMedia.alt || undefined,
|
||||
caption: dbMedia.caption || undefined,
|
||||
author: dbMedia.author || undefined,
|
||||
createdAt: dbMedia.createdAt,
|
||||
updatedAt: dbMedia.updatedAt,
|
||||
tags: JSON.parse(dbMedia.tags || '[]'),
|
||||
@@ -827,6 +839,7 @@ export class MediaEngine extends EventEmitter {
|
||||
title: dbMedia.title || undefined,
|
||||
alt: dbMedia.alt || undefined,
|
||||
caption: dbMedia.caption || undefined,
|
||||
author: dbMedia.author || undefined,
|
||||
createdAt: dbMedia.createdAt,
|
||||
updatedAt: dbMedia.updatedAt,
|
||||
tags: JSON.parse(dbMedia.tags || '[]'),
|
||||
|
||||
@@ -14,6 +14,7 @@ export interface ProjectMetadata {
|
||||
description?: string;
|
||||
dataPath?: string; // Custom path for project data
|
||||
mainLanguage?: string; // Main language for AI-generated content (ISO code, e.g., 'en', 'de', 'es')
|
||||
defaultAuthor?: string; // Default author for new posts and media
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -138,6 +138,16 @@ export function registerIpcHandlers(): void {
|
||||
|
||||
safeHandle('posts:create', async (_, data: Partial<PostData>) => {
|
||||
const engine = getPostEngine();
|
||||
|
||||
// If no author provided, use default author from project settings
|
||||
if (!data.author) {
|
||||
const metaEngine = getMetaEngine();
|
||||
const metadata = await metaEngine.getProjectMetadata();
|
||||
if (metadata?.defaultAuthor) {
|
||||
data.author = metadata.defaultAuthor;
|
||||
}
|
||||
}
|
||||
|
||||
return engine.createPost(data);
|
||||
});
|
||||
|
||||
@@ -279,6 +289,17 @@ export function registerIpcHandlers(): void {
|
||||
|
||||
safeHandle('media:import', async (_, sourcePath: string, metadata?: Partial<MediaData>) => {
|
||||
const engine = getMediaEngine();
|
||||
|
||||
// If no author provided, use default author from project settings
|
||||
if (!metadata?.author) {
|
||||
const metaEngine = getMetaEngine();
|
||||
const projectMetadata = await metaEngine.getProjectMetadata();
|
||||
if (projectMetadata?.defaultAuthor) {
|
||||
metadata = metadata || {};
|
||||
metadata.author = projectMetadata.defaultAuthor;
|
||||
}
|
||||
}
|
||||
|
||||
return engine.importMedia(sourcePath, metadata);
|
||||
});
|
||||
|
||||
@@ -309,9 +330,14 @@ export function registerIpcHandlers(): void {
|
||||
|
||||
const imported: MediaData[] = [];
|
||||
|
||||
// Get default author from project settings
|
||||
const metaEngine = getMetaEngine();
|
||||
const projectMetadata = await metaEngine.getProjectMetadata();
|
||||
const defaultAuthor = projectMetadata?.defaultAuthor;
|
||||
|
||||
for (const filePath of result.filePaths) {
|
||||
try {
|
||||
const media = await engine.importMedia(filePath);
|
||||
const media = await engine.importMedia(filePath, defaultAuthor ? { author: defaultAuthor } : undefined);
|
||||
imported.push(media);
|
||||
} catch (error) {
|
||||
console.error(`Failed to import ${filePath}:`, error);
|
||||
@@ -881,8 +907,14 @@ export function registerIpcHandlers(): void {
|
||||
executionEngine.setProjectContext(activeProject.id, activeProject.dataPath);
|
||||
}
|
||||
|
||||
// Get default author from project settings
|
||||
const metaEngine = getMetaEngine();
|
||||
const projectMetadata = await metaEngine.getProjectMetadata();
|
||||
const defaultAuthor = projectMetadata?.defaultAuthor;
|
||||
|
||||
const result = await executionEngine.executeImport(report, {
|
||||
uploadsFolder,
|
||||
defaultAuthor,
|
||||
onProgress: (phase, current, total, detail) => {
|
||||
// Update processed items count based on phase progress
|
||||
processedItems++;
|
||||
|
||||
Reference in New Issue
Block a user