Voltar para o blog
BunTypeScriptOpen SourceHacker News

HackerTube e Bun: um YouTube feito com a curadoria do HN

01 de novembro de 2026·6 min de leitura·Diego Horvatti

Você assiste a um vídeo sobre Postgres e, no dia seguinte, o YouTube te oferece "10 frameworks que vão morrer em 2027". O HackerTube parte desse incômodo com uma resposta simples: um YouTube montado com vídeos recomendados pela comunidade do Hacker News. A ideia aparece no blog do Andrej Karpathy, e os detalhes do projeto estão lá. Aqui eu quero falar do que ela ensina e de como montar a sua versão com Bun.

O que é o HackerTube?

O conceito cabe em uma frase: em vez de um modelo decidir o que você vê, quem decide são as pessoas que postam, votam e comentam no HN.

Repare no que muda. O vídeo continua no YouTube. O player continua no YouTube. Só a camada de descoberta foi trocada.

E essa é a parte interessante. A gente costuma tratar "o feed" como se fosse o produto inteiro. Não é. O feed é uma consulta, e consulta dá para reescrever.

O feed é só uma consulta. E consulta dá para reescrever.

Por que a curadoria do Hacker News importa para quem programa

O algoritmo do YouTube otimiza para o que ele consegue medir, como clique e tempo de tela. Faz sentido para o negócio dele, mas tempo de tela não é a mesma coisa que aprender.

O HN oferece um sinal diferente. Alguém achou o vídeo bom o bastante para postar. Outras pessoas votaram. Depois, muita gente discutiu. Uma palestra de 50 minutos sobre alocador de memória tem chance real de subir ali, mesmo sem thumbnail de boca aberta.

Tem outro ganho que eu acho subestimado: o sinal é legível. Você vê os pontos e abre os comentários. Muitas vezes, a discussão vale tanto quanto o vídeo, porque alguém que trabalhou no assunto aparece para corrigir o palestrante.

Agora, sem romantizar. O HN tem seus vícios:

  • Tem viés forte: inglês, Vale do Silício, startup, Rust e nostalgia de computador antigo.
  • Voto não prova que a pessoa assistiu. Muita gente vota pelo título.
  • Pontos dependem de horário e sorte. Vídeo ótimo pode morrer na fila de novos.

Ou seja, você troca um viés opaco por um viés visível. Eu fico com o visível, porque dá para compensar um viés que você enxerga.

Como montar sua versão com Bun

O que segue é a minha versão, não a implementação do projeto original. Escolhi Bun por um motivo sem glamour: ele roda TypeScript direto, já traz fetch, servidor HTTP e SQLite, e dispensa configuração. Para um projeto de tarde, isso é o que importa.

A API pública de busca do HN, mantida pela Algolia, não pede chave. Dá para filtrar stories por URL e pontuação:

// coleta.ts
const url = new URL("https://hn.algolia.com/api/v1/search");
url.searchParams.set("query", "youtube.com");
url.searchParams.set("restrictSearchableAttributes", "url");
url.searchParams.set("tags", "story");
url.searchParams.set("numericFilters", "points>=100");
url.searchParams.set("hitsPerPage", "200");

function extrairId(link: string): string | null {
  if (!URL.canParse(link)) return null;
  const u = new URL(link);
  if (u.hostname === "youtu.be") return u.pathname.slice(1) || null;
  return u.searchParams.get("v");
}

const { hits } = await fetch(url).then((r) => r.json());

const videos = hits.flatMap((h: any) => {
  const id = extrairId(h.url ?? "");
  if (!id) return [];
  return [{
    id,
    titulo: h.title,
    pontos: h.points,
    comentarios: h.num_comments,
    criadoEm: h.created_at_i,
    hn: `https://news.ycombinator.com/item?id=${h.objectID}`,
  }];
});

await Bun.write("videos.json", JSON.stringify(videos, null, 2));

Rode com bun coleta.ts e pronto, você tem um arquivo JSON.

Três armadilhas aparecem logo de cara:

  • Links curtos: a busca por youtube.com não pega youtu.be. Faça uma segunda consulta e junte os resultados.
  • Duplicatas: o mesmo vídeo é postado várias vezes ao longo dos anos. Agrupe por id e some os pontos.
  • Paginação: a busca não devolve o histórico inteiro de uma vez. Fatie por período, filtrando created_at_i em numericFilters.

Ranking: pontos sozinhos não bastam

Se você ordenar só por pontos, o topo vira um museu. Os mesmos clássicos ficam lá para sempre.

Dá para roubar a ideia do próprio HN: uma fórmula que penaliza a idade. Para um arquivo de vídeos, porém, a penalidade precisa ser bem mais leve, porque palestra boa não estraga como notícia:

function nota(v: { pontos: number; comentarios: number; criadoEm: number }) {
  const anos = (Date.now() / 1000 - v.criadoEm) / (365 * 24 * 3600);
  const engajamento = v.pontos + v.comentarios * 0.5;
  return engajamento / Math.pow(anos + 2, 0.5);
}

Os pesos aqui são chute, e eu falo isso sem vergonha. Comentário vale meio ponto porque indica discussão, mas também pode indicar briga. O expoente 0.5 só impede que 2012 domine a página. Ajuste olhando o resultado, não uma planilha.

Esse é o melhor efeito colateral de ter o próprio feed: a regra de recomendação vira dez linhas que você lê, muda e versiona no Git. Tente fazer isso com o algoritmo do YouTube.

Servindo tudo: você precisa de backend?

Na maioria dos casos, não. Um videos.json gerado por um cron e uma página estática resolvem. Se quiser um servidor local, o Bun faz isso sem dependência:

// server.ts
const videos = await Bun.file("videos.json").json();

Bun.serve({
  port: 3000,
  fetch() {
    const cards = videos.map((v: any) => `
      <article>
        <a href="https://www.youtube.com/watch?v=${v.id}">
          <img loading="lazy" alt=""
            src="https://img.youtube.com/vi/${v.id}/hqdefault.jpg">
        </a>
        <h2>${Bun.escapeHTML(v.titulo)}</h2>
        <a href="${v.hn}">${v.pontos} pontos, ${v.comentarios} comentários</a>
      </article>`);

    return new Response(cards.join(""), {
      headers: { "Content-Type": "text/html; charset=utf-8" },
    });
  },
});

Note o Bun.escapeHTML no título. Esse conteúdo vem da internet, então nunca confie nele ao montar HTML. Até um projeto de brinquedo merece esse cuidado.

Se a brincadeira crescer, com filtro por tema ou marcação de "já vi", o bun:sqlite entra sem você instalar nada. Antes disso, é coleção de JSON e felicidade.

Um cuidado: não baixe vídeo, não hospede vídeo e não raspe o YouTube. Linke ou use o player embutido oficial. Assim o criador continua recebendo a visualização, e o seu projeto não vira dor de cabeça com os termos de uso.

O que isso muda no seu fluxo de trabalho

Ninguém vai largar o YouTube por causa disso. Eu também não. O ganho real é outro.

Primeiro, você ganha uma fila de estudo melhor. Em vez de abrir o app e aceitar o que vier, você abre uma lista filtrada por gente que se importa com o tema.

Segundo, o padrão serve para outras fontes. Troque o HN por Lobsters, pelos links do seu time no Slack ou pela newsletter que você assina. O desenho é o mesmo: coletar links, extrair sinal, ranquear, exibir.

Terceiro, a barreira de entrada caiu. Uma ferramenta pessoal desse tipo tem menos de 100 linhas. Com um assistente de código, sai em uma tarde. Software feito para um usuário só, você mesmo, finalmente compensa o esforço.

Minha opinião sobre o HackerTube

Gosto do HackerTube pelo que ele questiona, mais do que pelo que ele é. A recomendação que a gente consome todo dia foi desenhada para o objetivo de outra empresa. Trocar essa camada não exige um produto novo nem uma equipe de machine learning. Exige uma API pública, um arquivo JSON e meia dúzia de decisões suas.

Isso não vai consertar a internet. Vai só te poupar de "10 frameworks que vão morrer". Para uma tarde de trabalho, já é um ótimo negócio. Se você curte esse tipo de ferramenta pequena e útil, tem mais coisa assim nos meus projetos.

Resumo para o LinkedIn

O YouTube te mostra um vídeo sobre Postgres e, no dia seguinte, oferece "10 frameworks que vão morrer em 2027".

O feed parece o produto inteiro, mas é só uma consulta. E consulta dá para reescrever.

Foi o que me chamou atenção no HackerTube, ideia que apareceu no blog do Andrej Karpathy: quem decide o que você vê são as pessoas que postam, votam e discutem no Hacker News, não um modelo otimizado para tempo de tela.

O HN tem seus vícios, claro. Mas eu prefiro um viés que enxergo a um viés escondido, porque o visível dá para compensar.

Montei minha versão com Bun: API pública, um arquivo JSON e um ranking de dez linhas que eu leio, mudo e versiono no Git. Menos de 100 linhas, uma tarde de trabalho.

Escrevi o passo a passo com o código completo no artigo. Se você também quer recuperar o controle do que assiste, vale a leitura.

#Bun #TypeScript #HackerNews #Programação #Desenvolvimento