Refactor: remove AGPL imgly dependency and migrate background removal to python backend

This commit is contained in:
2026-06-02 14:50:25 -05:00
parent 560a413c1e
commit f998e454fe
25 changed files with 601 additions and 97 deletions
+40 -6
View File
@@ -14,8 +14,8 @@ interface ExportModalProps {
durationInFrames: number;
brandVisibility?: { logo: boolean; frame: boolean; background: boolean };
outputFormat?: 'video' | 'image';
/** Template aspect ratio — used to filter resolution presets */
aspectRatio?: '9:16' | '16:9' | '1:1' | '4:5' | '4:3';
aspectRatio?: string;
onAssetSaved?: (url: string) => void;
}
const FORMAT_OPTIONS: { value: RenderFormat; label: string; icon: typeof Film; desc: string }[] = [
@@ -53,6 +53,7 @@ export const ExportModal: React.FC<ExportModalProps> = ({
brandVisibility,
outputFormat,
aspectRatio,
onAssetSaved,
}) => {
const { jobs, activeJobs, hasActiveJobs, isConnected, startExport, cancelJob, downloadJob } = useExportQueue();
@@ -68,7 +69,24 @@ export const ExportModal: React.FC<ExportModalProps> = ({
const filteredPresets = useMemo(() => {
if (!aspectRatio) return RESOLUTION_PRESETS;
const matching = RESOLUTION_PRESETS.filter(p => p.ratio === aspectRatio);
return matching.length > 0 ? matching : RESOLUTION_PRESETS;
if (matching.length > 0) return matching;
// Parse custom W:H
const parts = aspectRatio.split(':');
if (parts.length === 2) {
const w = parseInt(parts[0], 10);
const h = parseInt(parts[1], 10);
if (!isNaN(w) && !isNaN(h)) {
return [{
label: `${w}x${h}`,
w: w,
h: h,
desc: 'Resolución Original',
ratio: aspectRatio
}];
}
}
return RESOLUTION_PRESETS;
}, [aspectRatio]);
const [resIdx, setResIdx] = useState(0);
@@ -94,6 +112,22 @@ export const ExportModal: React.FC<ExportModalProps> = ({
return FORMAT_OPTIONS;
}, [outputFormat]);
// Ensure selected format is valid for the current mode
React.useEffect(() => {
if (!filteredFormats.find(f => f.value === format)) {
setFormat(filteredFormats[0].value);
}
}, [filteredFormats, format]);
// Track finished jobs to call onAssetSaved automatically
React.useEffect(() => {
if (!onAssetSaved) return;
const completedJob = jobs.find(j => j.status === 'completed' && j.resultUrl);
if (completedJob && completedJob.resultUrl) {
onAssetSaved(completedJob.resultUrl);
}
}, [jobs, onAssetSaved]);
const handleExport = async () => {
setIsExporting(true);
try {
@@ -128,8 +162,8 @@ export const ExportModal: React.FC<ExportModalProps> = ({
<Download size={18} className="text-violet-400" />
</div>
<div>
<h2 className="text-sm font-bold text-white">Exportar</h2>
<p className="text-[10px] text-neutral-500">Renderizar y descargar</p>
<h2 className="text-sm font-bold text-white">{onAssetSaved ? 'Guardar Activo de Marca' : 'Exportar'}</h2>
<p className="text-[10px] text-neutral-500">{onAssetSaved ? 'Renderizar y aplicar cambios' : 'Renderizar y descargar'}</p>
</div>
</div>
<div className="flex items-center gap-2">
@@ -284,7 +318,7 @@ export const ExportModal: React.FC<ExportModalProps> = ({
}`}
>
<Zap size={16} />
{isExporting ? 'Iniciando...' : `Exportar ${isStill ? 'Imagen' : 'Video'}`}
{isExporting ? 'Iniciando...' : (onAssetSaved ? 'Renderizar y Guardar' : `Exportar ${isStill ? 'Imagen' : 'Video'}`)}
<span className="text-[9px] opacity-60 font-mono">({estimatedSize})</span>
</button>