272 lines
8.1 KiB
TypeScript
272 lines
8.1 KiB
TypeScript
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 {};
|
|
}
|