feat: AI Brand Voice Translator integration and Mesh Content fix
This commit is contained in:
@@ -0,0 +1,271 @@
|
||||
import { getDB } from './database';
|
||||
|
||||
export function saveTemplateToDB(template: any) {
|
||||
const db = getDB();
|
||||
const tx = db.transaction(() => {
|
||||
// 1. Save Project
|
||||
db.prepare(`
|
||||
INSERT OR REPLACE INTO projects (id, brand_id, name, is_template, format, duration)
|
||||
VALUES (?, ?, ?, ?, ?, ?)
|
||||
`).run(
|
||||
template.id,
|
||||
template.brandId || null,
|
||||
template.name || template.id,
|
||||
1,
|
||||
template.format || 'video',
|
||||
template.duration || 0
|
||||
);
|
||||
|
||||
// 2. Save Layers
|
||||
if (template.layers && Array.isArray(template.layers)) {
|
||||
const insertLayer = db.prepare(`
|
||||
INSERT OR REPLACE INTO timeline_layers (id, project_id, name, type, is_visible, is_locked, is_muted, opacity, volume)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
`);
|
||||
for (const layer of template.layers) {
|
||||
insertLayer.run(
|
||||
layer.id,
|
||||
template.id,
|
||||
layer.name,
|
||||
layer.type || 'visual',
|
||||
layer.isVisible === false ? 0 : 1,
|
||||
layer.isLocked ? 1 : 0,
|
||||
layer.isMuted ? 1 : 0,
|
||||
layer.opacity ?? 1,
|
||||
layer.volume ?? 1
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Save Elements
|
||||
if (template.elements && Array.isArray(template.elements)) {
|
||||
const insertElement = db.prepare(`
|
||||
INSERT OR REPLACE INTO timeline_elements (
|
||||
id, layer_id, type, content, start_frame, end_frame, x, y, w, h,
|
||||
scale, rotation, opacity, color, font_family, font_size, text_align,
|
||||
filter, chroma_key_enabled, chroma_key_color
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
`);
|
||||
|
||||
for (const el of template.elements) {
|
||||
insertElement.run(
|
||||
el.id,
|
||||
el.layerId,
|
||||
el.type,
|
||||
el.content || '',
|
||||
el.startFrame || 0,
|
||||
el.endFrame || 0,
|
||||
el.x || 0,
|
||||
el.y || 0,
|
||||
el.w || null,
|
||||
el.h || null,
|
||||
el.scale ?? 1,
|
||||
el.rotation ?? 0,
|
||||
el.opacity ?? 1,
|
||||
el.color || null,
|
||||
el.fontFamily || null,
|
||||
el.fontSize || null,
|
||||
el.textAlign || null,
|
||||
el.filter || null,
|
||||
el.chromaKeyEnabled ? 1 : 0,
|
||||
el.chromaKeyColor || null
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
tx();
|
||||
}
|
||||
|
||||
export function getTemplatesFromDB() {
|
||||
const db = getDB();
|
||||
const projects = db.prepare('SELECT * FROM projects WHERE is_template = 1').all() as any[];
|
||||
|
||||
return projects.map(p => {
|
||||
const layers = db.prepare('SELECT * FROM timeline_layers WHERE project_id = ?').all(p.id) as any[];
|
||||
const elements = db.prepare(`
|
||||
SELECT e.* FROM timeline_elements e
|
||||
JOIN timeline_layers l ON l.id = e.layer_id
|
||||
WHERE l.project_id = ?
|
||||
`).all(p.id) as any[];
|
||||
|
||||
// Map DB rows back to application models
|
||||
const mappedLayers = layers.map(l => ({
|
||||
id: l.id,
|
||||
name: l.name,
|
||||
type: l.type,
|
||||
isVisible: Boolean(l.is_visible),
|
||||
isLocked: Boolean(l.is_locked),
|
||||
isMuted: Boolean(l.is_muted),
|
||||
opacity: l.opacity,
|
||||
volume: l.volume
|
||||
}));
|
||||
|
||||
const mappedElements = elements.map(e => ({
|
||||
id: e.id,
|
||||
layerId: e.layer_id,
|
||||
type: e.type,
|
||||
content: e.content,
|
||||
startFrame: e.start_frame,
|
||||
endFrame: e.end_frame,
|
||||
x: e.x,
|
||||
y: e.y,
|
||||
w: e.w,
|
||||
h: e.h,
|
||||
scale: e.scale,
|
||||
rotation: e.rotation,
|
||||
opacity: e.opacity,
|
||||
color: e.color,
|
||||
fontFamily: e.font_family,
|
||||
fontSize: e.font_size,
|
||||
textAlign: e.text_align,
|
||||
filter: e.filter,
|
||||
chromaKeyEnabled: Boolean(e.chroma_key_enabled),
|
||||
chromaKeyColor: e.chroma_key_color
|
||||
}));
|
||||
|
||||
return {
|
||||
id: p.id,
|
||||
brandId: p.brand_id,
|
||||
name: p.name,
|
||||
format: p.format,
|
||||
duration: p.duration,
|
||||
layers: mappedLayers,
|
||||
elements: mappedElements
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
export function deleteTemplateFromDB(templateId: string) {
|
||||
const db = getDB();
|
||||
db.prepare('DELETE FROM projects WHERE id = ?').run(templateId);
|
||||
return true;
|
||||
}
|
||||
|
||||
// ═══ Brand DAOs ═══
|
||||
|
||||
export function saveBrandToDB(brand: any) {
|
||||
const db = getDB();
|
||||
const tx = db.transaction(() => {
|
||||
db.prepare('INSERT OR REPLACE INTO brands (id, name, tagline, industry) VALUES (?, ?, ?, ?)').run(
|
||||
brand.id, brand.name || brand.id, brand.tagline || '', brand.industry || ''
|
||||
);
|
||||
|
||||
if (brand.design) {
|
||||
const d = brand.design;
|
||||
db.prepare(`
|
||||
INSERT OR REPLACE INTO brand_design (
|
||||
brand_id, primary_color, secondary_color, text_color, base_font,
|
||||
logo_url, frame_thickness, title_font, title_size, title_color,
|
||||
subtitle_font, subtitle_size, subtitle_color, paragraph_font, paragraph_size, paragraph_color
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
`).run(
|
||||
brand.id,
|
||||
d.primaryColor || '#000000',
|
||||
d.secondaryColor || '#FFFFFF',
|
||||
d.textColor || '#FFFFFF',
|
||||
d.baseFont || 'Inter',
|
||||
d.logoUrl || '',
|
||||
d.frameThickness || 0,
|
||||
d.titleFont || '', d.titleSize || null, d.titleColor || '',
|
||||
d.subtitleFont || '', d.subtitleSize || null, d.subtitleColor || '',
|
||||
d.paragraphFont || '', d.paragraphSize || null, d.paragraphColor || ''
|
||||
);
|
||||
}
|
||||
});
|
||||
tx();
|
||||
return true;
|
||||
}
|
||||
|
||||
export function getBrandsFromDB() {
|
||||
const db = getDB();
|
||||
const brands = db.prepare('SELECT * FROM brands').all() as any[];
|
||||
|
||||
return brands.map(b => {
|
||||
const design = db.prepare('SELECT * FROM brand_design WHERE brand_id = ?').get(b.id) as any;
|
||||
const mappedDesign = design ? {
|
||||
primaryColor: design.primary_color,
|
||||
secondaryColor: design.secondary_color,
|
||||
textColor: design.text_color,
|
||||
baseFont: design.base_font,
|
||||
logoUrl: design.logo_url,
|
||||
frameThickness: design.frame_thickness,
|
||||
titleFont: design.title_font,
|
||||
titleSize: design.title_size,
|
||||
titleColor: design.title_color,
|
||||
subtitleFont: design.subtitle_font,
|
||||
subtitleSize: design.subtitle_size,
|
||||
subtitleColor: design.subtitle_color,
|
||||
paragraphFont: design.paragraph_font,
|
||||
paragraphSize: design.paragraph_size,
|
||||
paragraphColor: design.paragraph_color,
|
||||
brandAssets: []
|
||||
} : {};
|
||||
|
||||
const assets = db.prepare("SELECT * FROM brand_assets WHERE brand_id = ? AND source = 'uploaded'").all(b.id) as any[];
|
||||
if (mappedDesign) {
|
||||
mappedDesign.brandAssets = assets.map(a => ({
|
||||
id: a.id,
|
||||
type: a.type,
|
||||
url: a.url,
|
||||
path: a.path
|
||||
}));
|
||||
}
|
||||
|
||||
return {
|
||||
id: b.id,
|
||||
name: b.name,
|
||||
tagline: b.tagline,
|
||||
industry: b.industry,
|
||||
design: mappedDesign
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
export function deleteBrandFromDB(brandId: string) {
|
||||
const db = getDB();
|
||||
db.prepare('DELETE FROM brands WHERE id = ?').run(brandId);
|
||||
return true;
|
||||
}
|
||||
|
||||
// ═══ Generated Media DAOs ═══
|
||||
|
||||
export function registerGeneratedMediaDB(brandId: string, type: 'video' | 'image', filePath: string) {
|
||||
const db = getDB();
|
||||
db.prepare(`
|
||||
INSERT OR IGNORE INTO brand_assets (id, brand_id, type, source, path, name)
|
||||
VALUES (?, ?, ?, ?, ?, ?)
|
||||
`).run(filePath, brandId, type, 'generated', filePath, filePath);
|
||||
return true;
|
||||
}
|
||||
|
||||
export function renameGeneratedMediaDB(brandId: string, type: 'video' | 'image', filePath: string, newName: string) {
|
||||
const db = getDB();
|
||||
db.prepare('UPDATE brand_assets SET name = ? WHERE path = ? AND brand_id = ? AND source = ?').run(newName, filePath, brandId, 'generated');
|
||||
return true;
|
||||
}
|
||||
|
||||
export function getGeneratedMediaDB(brandId: string, type: 'video' | 'image') {
|
||||
const db = getDB();
|
||||
const media = db.prepare('SELECT * FROM brand_assets WHERE brand_id = ? AND type = ? AND source = ?').all(brandId, type, 'generated') as any[];
|
||||
return media.map(m => ({
|
||||
path: m.path,
|
||||
name: m.name,
|
||||
date: m.created_at
|
||||
}));
|
||||
}
|
||||
|
||||
// ═══ Content Mesh DAOs ═══
|
||||
|
||||
export function saveContentMeshDB(data: any) {
|
||||
const db = getDB();
|
||||
db.prepare('INSERT OR REPLACE INTO settings (key, value) VALUES (?, ?)').run('content_mesh', JSON.stringify(data));
|
||||
return true;
|
||||
}
|
||||
|
||||
export function getContentMeshDB() {
|
||||
const db = getDB();
|
||||
const record = db.prepare('SELECT value FROM settings WHERE key = ?').get('content_mesh') as any;
|
||||
if (record) return JSON.parse(record.value);
|
||||
return {};
|
||||
}
|
||||
Reference in New Issue
Block a user