Ürünereklam
ProntoCole o link de um produto e receba anúncios em vídeo e imagem prontos, em turco e em inglês, cada um com uma nota dada por um modelo de previsão antes de qualquer dinheiro ser gasto. Começou no BTK Akademi Hackathon de 2026.
- Meu papel
- Design e desenvolvimento, sozinho
- Ano
- 2026
- Feito com
- Next.js · Inngest · Supabase · Gemini · Veo · ElevenLabs · Remotion · PyTorch on Modal · Polar
Role para o lado para ver o diagrama inteiro.
O problema
Anúncios em dois idiomas, no volume de que as lojas online precisam, normalmente significam uma agência com fee mensal. Mesmo assim, ninguém sabe qual versão vai funcionar antes de o orçamento ser gasto.
Como foi construído
Um app em Next.js orquestra jobs duráveis do Inngest. Um scraper em Python lê a página do produto, com um caminho de fetch reforçado para lojas difíceis. O Gemini escreve a estratégia, o copy e os frames, o Veo ou o Seedance renderiza o vídeo, o ElevenLabs coloca a voz e o Remotion adiciona legendas sincronizadas palavra por palavra, a marca d'água e a chamada para ação. Um estúdio separado produz anúncios de imagem estática e textos de anúncio. Depois, cada variação passa por um modelo self-hosted que prevê como as pessoas vão reagir a ela (TRIBE v2) em GPUs da Modal; o job é disparado e depois consultado por polling, porque um render dura mais do que o timeout de uma edge function. O Supabase guarda o estado protegido por row-level security, com URLs assinadas de curta duração para as mídias. Os créditos ficam em um ledger append-only com cobranças e reembolsos atômicos, e o checkout roda no Polar, com preços em liras e dólares.
Decisões importantes
Uma etapa que falha se reembolsa sozinha
O pipeline é feito de jobs duráveis com um handler de falha para cada etapa. Uma etapa que morre reembolsa seus créditos exatamente uma vez e libera a execução em vez de deixá-la travada.
As mídias são privadas por padrão
Os renders brutos ficam em um bucket privado, o SELECT em nível de coluna é revogado e os links de mídia são URLs assinadas de curta duração. Nenhum endereço de mídia pode ser adivinhado, plano gratuito incluído.
Todo anúncio recebe uma nota antes de alguém pagar por ele
Nenhum anúncio sai do estúdio sem nota. O modelo ranqueia as variações antes de alguém colocar orçamento em uma delas.
Código do repositório
async function safeRefund(
opts: Parameters<typeof refundCredits>[0],
): Promise<void> {
try {
await refundCredits(opts);
} catch (err) {
console.error(
`[billing][RECONCILE-NEEDED] refund failed user=${opts.userId} ` +1
`amount=${opts.amount} ref=${opts.refType}:${opts.refId} ` +
`key=${opts.idempotencyKey ?? "-"}`,
err,
);
}
}
// …
await safeRefund({
supa,
userId: campaign.user_id,
amount: campaignCreditCost({
videoCount: brief.videoCount ?? 1,
videoModel: brief.videoModel ?? "seedance",
durationSec: brief.durationSec,
}),
refType: "campaign",
refId: campaignId,
idempotencyKey: `refund:campaign-all:${campaignId}`,2
description: "Kampanya başlatılamadı — otomatik kredi iadesi",3
});
Linha 8. Um reembolso que falha registra um marcador RECONCILE-NEEDED em vez de lançar uma exceção, então nenhum crédito se perde em silêncio e o pipeline ainda libera a execução.
Linha 27. Uma chave de idempotência por evento: o handler de falha pode rodar de novo, mas o ledger registra o reembolso uma única vez.
Linha 28. O motivo do reembolso está em turco porque o produto é bilíngue de ponta a ponta.
Como está hoje
Deixou de ser uma demo de hackathon e virou um produto bilíngue com pontuação em GPU. Agora é código fechado, e o lançamento pago está em preparação.