As miniaturas estão faltando para nossos embeds externos ao usar topic-thumbnails

Estamos lançando um fórum para o nosso produto, cujos embeds externos já funcionam com o OneBox após permitir Iframes de nós.

Um link direto para um embed, é transformado de forma elegante no resultado do nosso endpoint oembed.
Agora, nossa preocupação é, antes de mais nada, que gostaríamos que as miniaturas aparecessem quando o Topic List Thumbnails estivesse ativo. Não tenho certeza por que a miniatura não está sendo capturada.

Precisaríamos adicionar uma integração ao lib/onebox/engine?

Para adicionar, o comportamento padrão funciona em sua maioria hoje, exceto por não suportar nossos embeds dinâmicos. Gostaríamos de carregar JS em vez de um Iframe. Admito que existe uma solução diferente para isso, que é redimensionar opcionalmente o iframe com algum JS, mas não planejamos implementar isso genericamente para oEmbeds no curto prazo.

Estou ciente de Using onebox images for topic thumbnails - #20 by david, que não achei útil no meu caso.

1 curtida

Após alguma discussão com a equipe do Discourse por e-mail, estou atualizando aqui.

A principal descoberta é que as miniaturas não são carregadas se um link que se torna um onebox for renderizado como um iframe.

Isso significa que o link:

https://app.everviz.com/embed/N0dDTJaOQ

Renderizará ou não uma miniatura dependendo se iframes são permitidos do domínio (allowed iframes). A solução para isso é injetar de alguma forma um iframe na postagem. Criei um plugin que faz isso e garante que display: none seja definido na imagem, para evitar que ela apareça na postagem.

Imagino que seja possível generalizar isso para:

  1. Para todos os oneboxes genéricos, carregar silenciosamente e ocultar uma imagem ao lado dela se get_oembed.thumbnail_url existir.
  2. Provavelmente uma solução melhor, adicionar suporte geral para extrair get_oembed.thumbnail_url e associá-lo à postagem, sem que ele faça parte da área cooked em si.

Locais relevantes:

  1. https://oembed.com/
  2. discourse/lib/onebox/engine at main · discourse/discourse · GitHub
  3. discourse/lib/onebox/engine/standard_embed.rb at main · discourse/discourse · GitHub

Não estou muito satisfeito com esse comportamento modal, ou seja, allowed iframes está habilitado ou não? Seria bom se o Discourse extraísse e utilizasse todas as propriedades de um determinado endpoint oembed quando ele envia um GET para ele pela primeira vez.

Atualização

Tive a ideia de usar a API de plugin do lado do cliente para carregar uma imagem na postagem.

api.decorateCooked($elem => {
    $elem[0].querySelectorAll('.my-iframe')
    .forEach(function (iframe) {

      // trabalho

      iframe.insertAdjacentElement('beforebegin', thumbnail);
    });

  }, {id: 'unique_string'});

Mas o Discourse não está captando nada.

Parece que uma de duas coisas está acontecendo:

  1. Estamos atrasados e o Discourse já fez a recuperação e geração de miniaturas.
  2. Estamos perdendo alguns atributos em nossa imagem que fazem com que o mecanismo de geração de miniaturas a perca.

Esperançosamente, é o último. Uma coisa a notar aqui é que a imagem nunca é carregada em nossa instância do Discourse, mas simplesmente referenciada de nosso próprio servidor.

Você já tentou isso? Escrever um onebox específico para o seu caso pode permitir que você forneça uma imagem onebox para a postagem cozida? Então as miniaturas funcionariam automaticamente.

Observo que os oneboxes do Youtube, acredito eu, são conteúdo estático no local, até que você clique no botão de reprodução e então ele mostra um iframe. O conteúdo apresentado antes do clique inclui uma imagem que é capturada e então miniatura. Obviamente, o Discourse não pode ler de um iframe, então essa técnica é uma boa abordagem.

Observo que seu exemplo inclui um og:image nas tags do cabeçalho, o que é perfeito.


Minha sugestão é se afastar do javascript aqui e cozinhar no Rails.

A única desvantagem disso será que sua imagem ficará estática até que você reconstrua a Postagem, assumindo que a imagem de destino também seja atualizada. Assim, se você espera mostrar uma miniatura que muda dinamicamente, talvez precise ser ainda mais criativo.

3 curtidas

Olá Robert, obrigado pela sua resposta!

Escrever um plugin foi a primeira coisa que fiz, e funcionou fantasticamente — não importa se o coloquei em lib/onebox/engine ou como um plugin separado. A ressalva é que estamos em um plano hospedado, onde os plugins, compreensivelmente, não são permitidos em planos multilocatários, deixando o “cooking” em Rails fora de questão.

Isso nos deixa com uma das três oportunidades:

  1. Executar nossa própria instância
  2. Fazer um hack no lado do cliente, se puder funcionar
  3. Enviar um PR para o Discourse principal

Para formular uma pergunta aqui. Não tenho certeza se tal contribuição seria aceita, especialmente se ela carrega uma imagem apenas para escondê-la. Como posso descobrir?

2 curtidas

Talvez eu tenha feito algum progresso nisso. Olhando para:\n\napi.composerBeforeSave\n\nO cujo callback é tratado em composer.js. A partir daí, podemos ver que os métodos createPost e editPost chamam getCookedHtml, que mais ou menos simplesmente retorna o innerHTML de:\n\njs\nconst editorPreviewNode = document.querySelector(\n \"#reply-control .d-editor-preview\"\n);\n\n\nO que significa que, se modificássemos esse seletor, poderíamos forçar a inserção de algum HTML equivalente a um upload de imagem normal. No entanto, parece que modificar editorPreviewNode.innerHTML não resulta em nenhuma alteração.\n\nPor que isso acontece, ou o que posso modificar em composerBeforeSave para fazer algo semelhante?

Isso faz sentido para mim, acho que estaríamos abertos a aceitar um PR para o core para um motor onebox para everviz.com (ou serviços de visualização semelhantes). No entanto, eu evitaria inserir e ocultar uma imagem na postagem cooked, existe uma opção mais leve. O processador de postagens do Discourse procurará por um upload os seguintes elementos:

Então adicionar a miniatura como uma âncora abaixo do iframe pode funcionar? Você pode mantê-la visível ou ocultá-la usando uma classe como “hidden”.

5 curtidas

Olá, Penar, obrigado pela resposta.

Usando

  <div class="everviz-box">
   #{get_oembed.html}
   <a class="hidden" href="https://app.everviz.com/thumbnails/#{match[:uuid]}.png"></a>
  </div>

como meu método to_html, produz a seguinte saída:

  <div class="...">
    <iframe>
      ...
    </iframe>
    <a class="hidden" href="https://app.everviz.com/thumbnails/[...].png" rel="nofollow ugc noopener"></a>
  </div>

Vincular uma imagem diretamente produz:

<a href="https://app.everviz.com/thumbnails/[...].png" target="_blank" rel="noopener" class="onebox">
   <img src="//localhost:3000/uploads/default/original/1X/[...].png"
        style="aspect-ratio: 690 / 459;" loading="lazy">
</a>

Fazendo parecer que o motor image_onebox.rb está sendo executado na entrada apenas se uma imagem for vinculada fora do contexto de outro motor onebox.
A consequência disso é que a técnica sugerida não funciona no momento e é necessário vincular e ocultar uma imagem, ou modificar o Discourse para levar isso em consideração.

Seria um PR aceitável neste caso inserir e ocultar uma imagem? Ou é necessário fazer algo mais complexo?

Não tenho certeza se estou entendendo, isso não deveria passar pelo mecanismo de onebox de imagem.

Isso:

<a class="hidden" href="https://app.everviz.com/thumbnails/[...].png" rel="nofollow ugc noopener"></a>

Não funciona? Ou seja, quando a primeira postagem em um tópico tem isso em sua coluna “cooked”, a imagem é reconhecida como uma miniatura após o processamento?

2 curtidas

Desculpe pela confusão, a saída parecia semelhante ao mecanismo onebox da imagem.

O link sugerido (devidamente preenchido com um UUID) não funciona. Nem esta imagem que retirei do Google (e estou aqui escrevendo como uma tag de âncora. Se colar algum HTML for equivalente a ter uma saída onebox, isso explicaria nossos resultados.

https://static-cse.canva.com/blob/1031184/1600w-wK95f3XNRaM.jpg

1 curtida

Ah, falha minha, você tem razão, essa abordagem não funciona. Quando geramos miniaturas, usamos apenas imagens baixadas, mas imagens vinculadas em uma tag de âncora não são baixadas.

Uma alternativa aqui pode ser adicionar ao core algo semelhante ao que @merefield recomendou, mas encapsulado como um onebox genérico para iframes de carregamento lento. Talvez adicionar uma nova configuração do site lazy_loaded_iframes, e o onebox pode inicialmente exibir a imagem da tag OG (que deve ser capturada pelas miniaturas) e, quando clicada, um pouco de JS substitui a imagem pelo iframe correto (semelhante à substituição do iframe do YouTube).

Um detalhe complicado aqui é que a imagem usada e o iframe devem ter a mesma altura, caso contrário, isso pode introduzir saltos indesejados ao rolar/navegar por posts.

3 curtidas