O Medium ficava descartando minha imagem de capa, então a bridge agora cola ela direto
Meu pipeline de publicação no Medium finalmente funcionou de ponta a ponta na semana passada. O blog-manager avisa o Postiz para publicar, o Postiz chama um sidecar que eu chamo de medium-bridge, e a bridge conduz a página oficial “Import a story” do Medium com uma sessão de navegador salva (o Medium matou a própria API em janeiro de 2025, então um Chromium furtivo clicando em botões é a interface com a máquina agora). O post importado ficou correto: título, corpo, link canônico de volta pro meu site.
Uma coisa estava faltando. A imagem de capa. A história era importada sem imagem nenhuma.
Teoria 1: é o webp
Meu site serve imagens de capa em webp pros navegadores. O editor do Medium só aceita JPG, PNG e GIF. A imagem de capa na página também era uma URL relativa. Teoria fácil: o importador busca a capa, engasga no webp, e descarta.
Então mudei o template do blog pra um elemento <picture>: <source> webp pros navegadores, <img> jpeg com URL absoluta como fallback pra qualquer coisa que lê o atributo src.
<picture>
<source srcset={heroImageWebp} type="image/webp" />
<img src={heroImageFallback} alt={entry.data.title} />
</picture>
Fiz o deploy, reimportei, ainda sem imagem. E quando vasculhei o histórico da bridge desde junho, a teoria caiu de vez: o importador do Medium costumava puxar a logo do meu site, um webp de URL relativa, então ele lida bem com os dois casos. Até tinha código de limpeza na bridge pra apagar aquela logo de todo rascunho.
Teoria 2: é o posicionamento
Próximo palpite: o importador roda um extrator no estilo readability, mantém só o bloco de texto principal, e minha capa vivia fora dele (irmã da div de conteúdo). Imagens dentro do corpo tinham sido importadas bem em junho, o que combinava direitinho. Movi a capa pra ser o primeiro filho do bloco de conteúdo.
Reimportei. Ainda sem imagem. Pior: a importação nova ficou com zero imagens, incluindo a logo pra qual existia aquele código de limpeza. O que quer que o Medium tenha mudado desde junho, o importador dele agora descarta toda imagem da página, e cada uma das minhas teorias custou uma importação manual pra ser derrubada.
Parar de chutar, inserir a imagem nós mesmos
A bridge já compensa outros hábitos do importador: ela reaplica o título (o importador salva ele vazio) e reinsere a lista de referências (o importador engole elementos <ul>) disparando um ClipboardEvent sintético no handler de paste do ProseMirror. Então: buscar o og:image do post no servidor, já é um jpeg absoluto, e colar os bytes como um File.
Isso quase funcionou, e o modo de falha foi traiçoeiro. O editor renderizou uma prévia, o autosave rodou, e o documento salvo até registrou as dimensões reais da imagem (data-width="940" data-height="627"). Mas o <img> no documento persistido não tinha src. O upload nunca anexou de fato. Na visualização publicada você tem uma moldura cinza vazia com o tamanho perfeito, o que é meio pior do que nenhuma imagem.
O caminho do input nativo
O que finalmente funcionou foi conduzir a interface de upload real do Medium em vez de simular um paste. Com o cursor num parágrafo vazio, o editor mostra um menu inline ”+”; o botão de imagem dele abre um seletor de arquivo, e o Playwright consegue interceptar isso e entregar um buffer em memória:
const [chooser] = await Promise.all([
page.waitForEvent('filechooser', { timeout: 15000 }),
page.evaluate(() => {
document.querySelector('button[data-action="inline-menu-image"]').click();
}),
]);
await chooser.setFiles({ name: 'hero.jpeg', mimeType: hero.type,
buffer: Buffer.from(hero.base64, 'base64') });
Duas pegadinhas no caminho. Apertar Enter no fim do título cria um heading vazio, não um parágrafo, e o menu ”+” ignora headings, mas documentos importados já vêm com um parágrafo vazio logo abaixo do título, então a bridge clica nesse. E o menu nunca chega num estado que o Playwright considera “visível”, então os cliques passam por el.click() dentro de page.evaluate, o mesmo truque usado na correção do título.
A verificação agora é paranoica, porque a falha do paste me ensinou a ser assim: espera a figura carregar um data-image-id, espera o autosave, reconfere se o atributo sobreviveu, depois busca a URL do CDN no servidor e exige um HTTP 200.
hero: file handed to native upload input
hero: inserted, image-id 1*L8wXdarItJ0XEnVbEm19TA.jpeg - CDN check HTTP 200
Já que a capa é a primeira imagem do conteúdo, o Medium também usa ela como imagem do card de prévia da história. Esse era o truque da era da API antiga também; ainda continua valendo.
O que eu diria pra mim mesmo do passado
As duas correções do lado do site que fiz enquanto perseguia teorias erradas ainda valeram a pena manter, o fallback jpeg deixa qualquer scraper mais feliz, não só o do Medium. Mas depurar uma caixa preta mudando sua própria saída é caro: uma teoria por importação manual, e a caixa pode mudar debaixo de você entre um experimento e outro, o que de fato aconteceu. A evidência de junho dizia “imagens inline importam bem” e em julho isso já não era mais verdade. Quando um terceiro descarta seu conteúdo silenciosamente e você já tem uma sessão de navegador no editor dele, pare de negociar com o importador e coloque o conteúdo lá você mesmo.
Próximo passo: nada, espero. A lista de compensações do pipeline agora é título, referências e capa. Tenho opiniões sobre uma plataforma de publicação em que essa lista sequer precisa existir.
Leitura relacionada
Debugando um checkbox escondido na UI real do Medium
Um input invisível de tamanho zero atrás de um toggle estilizado, um estado disabled que engole cliques silenciosamente, e verificação contra a página real em vez dos meus próprios logs.
Um toggle por blog, e quando não "corrigir" um bug
Um booleano atravessando quatro camadas, e três passadas de revisão independentes concordando com um achado que eu deliberadamente não corrigi - porque o cache é o mecanismo de dedup.
O blog que não conseguia publicar num cronograma
Um design de pipeline em lote que começou com uma checagem de cinco minutos na base do sistema: posts com data futura não ficavam escondidos, viravam 404, e nada nunca reconstruía o site.
Você também pode achar útil
Proton Pass
Gerenciador de senhas focado em privacidade, da equipe por trás do Proton Mail.
Como parceiro da Proton, ganho com compras qualificadas dos serviços de privacidade e segurança da Proton (Pass, Mail, VPN, Drive).
Saiba maisProton Drive
Armazenamento em nuvem criptografado, da equipe por trás do Proton Mail.
Como parceiro da Proton, ganho com compras qualificadas dos serviços de privacidade e segurança da Proton (Pass, Mail, VPN, Drive).
Saiba maisProton Mail
E-mail criptografado de ponta a ponta, com arquitetura de acesso zero.
Como parceiro da Proton, ganho com compras qualificadas dos serviços de privacidade e segurança da Proton (Pass, Mail, VPN, Drive).
Saiba mais