Files
2026-06-07 20:43:31 -07:00

174 lines
5.3 KiB
TypeScript

import * as fs from 'node:fs';
import * as path from 'node:path';
import { DEFAULT_LOCALE } from './constants';
import { CONTENT_DIR, THUMBNAILS_DIR } from './paths';
import { logger } from './logger';
import { parseArgs } from './args';
import { loadTemplateIndex, flattenTemplates, getTopByUsage } from './index-reader';
import { readWorkflowJson } from './workflow-reader';
import {
extractAuthorNotes,
estimateGenerationTime,
extractRequiredNodes,
extractWorkflowModels,
} from './extract';
import {
findThumbnails,
copyThumbnails,
copyWorkflowJson,
ensureDirectories,
syncLogos,
getOutputPath,
} from './filesystem';
import type { TemplateInfo, SyncedTemplate } from './types';
function createSyncedTemplate(template: TemplateInfo, locale: string): SyncedTemplate {
const thumbnails = findThumbnails(template.name);
const workflow = readWorkflowJson(template.name);
let estimatedTime: string | undefined;
let requiredNodes: SyncedTemplate['requiredNodes'];
let authorNotes: string | undefined;
let workflowModels: SyncedTemplate['workflowModels'];
if (workflow) {
estimatedTime = estimateGenerationTime(workflow, template.mediaType, template.name);
const nodes = extractRequiredNodes(workflow);
requiredNodes = nodes.length > 0 ? nodes : undefined;
const notes = extractAuthorNotes(workflow);
authorNotes = notes || undefined;
const models = extractWorkflowModels(workflow);
workflowModels = models.length > 0 ? models : undefined;
}
return {
...template,
extendedDescription: template.description,
howToUse: ['Load the template', 'Configure inputs', 'Run the workflow'],
metaDescription: template.description.slice(0, 160),
suggestedUseCases: [],
thumbnails,
locale: locale === DEFAULT_LOCALE ? undefined : locale,
estimatedTime,
requiredNodes,
authorNotes,
workflowModels,
};
}
export function runSync(): void {
const { limit, locales } = parseArgs();
logger.info('Syncing templates...\n');
logger.info(`Locales: ${locales.join(', ')}`);
ensureDirectories(locales);
const enCategories = loadTemplateIndex(DEFAULT_LOCALE);
if (!enCategories) {
logger.error('Error: Could not load English index.json');
process.exit(1);
}
const allEnTemplates = flattenTemplates(enCategories);
const templatesToProcess = getTopByUsage(allEnTemplates, limit);
const templateNames = new Set(templatesToProcess.map((t) => t.name));
logger.info(`Found ${allEnTemplates.length} total templates in English`);
if (limit) {
logger.info(`Processing top ${templatesToProcess.length} templates by usage\n`);
} else {
logger.info(`Processing ALL ${templatesToProcess.length} templates\n`);
}
let syncedCount = 0;
const stats: Record<string, number> = {};
for (const locale of locales) {
logger.info(`\n--- Processing locale: ${locale} ---`);
stats[locale] = 0;
const categories = loadTemplateIndex(locale);
if (!categories) continue;
const templates = flattenTemplates(categories);
const templateMap = new Map<string, TemplateInfo>();
for (const t of templates) {
templateMap.set(t.name, t);
}
for (const templateName of templateNames) {
const template = templateMap.get(templateName);
if (!template) {
continue;
}
const synced = createSyncedTemplate(template, locale);
const outputPath = getOutputPath(templateName, locale);
fs.writeFileSync(outputPath, JSON.stringify(synced, null, 2));
if (locale === DEFAULT_LOCALE) {
copyThumbnails(templateName);
copyWorkflowJson(templateName);
}
syncedCount++;
stats[locale]++;
}
logger.info(` Synced ${stats[locale]} templates for ${locale}`);
}
const allIndexNames = new Set(allEnTemplates.map((t) => t.name));
let orphansRemoved = 0;
for (const locale of locales) {
const dir = locale === DEFAULT_LOCALE ? CONTENT_DIR : path.join(CONTENT_DIR, locale);
if (!fs.existsSync(dir)) continue;
for (const file of fs.readdirSync(dir)) {
if (!file.endsWith('.json')) continue;
const name = file.replace(/\.json$/, '');
if (!allIndexNames.has(name)) {
const filePath = path.join(dir, file);
fs.unlinkSync(filePath);
logger.info(` Removed orphan: ${locale === DEFAULT_LOCALE ? '' : locale + '/'}${file}`);
orphansRemoved++;
}
}
}
let orphanThumbsRemoved = 0;
if (fs.existsSync(THUMBNAILS_DIR)) {
for (const file of fs.readdirSync(THUMBNAILS_DIR)) {
const match = file.match(/^(.+)-\d+\.\w+$/);
if (match && !allIndexNames.has(match[1])) {
fs.unlinkSync(path.join(THUMBNAILS_DIR, file));
logger.info(` Removed orphan thumbnail: ${file}`);
orphanThumbsRemoved++;
}
}
}
const logosSynced = syncLogos();
logger.info(`\n=== Sync complete ===`);
logger.info(`Total: ${syncedCount} template files synced`);
for (const [locale, count] of Object.entries(stats)) {
logger.info(` ${locale}: ${count}`);
}
if (logosSynced > 0) {
logger.info(`Synced ${logosSynced} logos`);
}
if (orphansRemoved > 0) {
logger.info(`Removed ${orphansRemoved} orphan content files`);
}
if (orphanThumbsRemoved > 0) {
logger.info(`Removed ${orphanThumbsRemoved} orphan thumbnails`);
}
logger.info(`Content dir: ${CONTENT_DIR}`);
logger.info(`Thumbnails dir: ${THUMBNAILS_DIR}`);
}