O SlideMeme foi hack que eu e a Bani apresentamos no Yahoo! Open Hack Day Brasil 2010. O objetivo dele é oferecer uma forma visualmente agradável e conveniente para postar apresentações do SlideShare no Yahoo! Meme, e claro que ficamos muito contentes por ele ter sido premiado como melhor hack na categoria Meme!
UPDATE:Um dos grandes problemas que o hack teve foi o fato de ser baseado num componente ActiveX, o que exigia um servidor Windows – para o qual nós não tínhamos recursos (ou vontade) para manter no ar. Em Jun/2010 eu reescrevi a parte da conversão usando swftools e dei uma condensada na coisa toda, centralizando o back-end em um único script. O post foi mantido, pois os detalhes de funcionamento ainda valem, mas a última versão de tudo está no github para quem quiser.
Mas eu falo do evento em outro post – esse aqui é para documentar o processo e publicar o código do hack (até o final vai ficar claro que ele é curto e grosso demais para merecer um github/sourceforge, e que o maior valor está em detalhar o que foi feito e como). UPDATE: com o fim do experimento, o código fonte está disponível no github, incluindo os arquivos do site.
Para saber mais, veja a apresentação no SlideShare, assista à Bani explicando e demonstrando a coisa toda em dois minutos cravados no relógio – uma proeza digna de nota), e, claro, continue lendo este post.
Tivemos a idéia olhando para os Memes mais populares: ali predominam as fotos e ilustrações “fofinhas” no mesmo estilo daquelas que recebemos por email das tias e mães, invariavelmente no formato do Microsoft PowerPoint (.ppt). Como o SlideShare é o site-referência para a publicação de apresentações neste e em outros formatos (à semelhança do que o YouTube se tornou para arquivos de vídeo), foi natural evoluir o conceito nessa direção.
Claro que as pessoas podem simplesmente copiar e colar o endereço do SlideShare num post, mas o Meme funciona melhor com mídias visuais (a maior prova disso é a quase ausência de MP3). Na nossa cabeça, o leitor teria que ver algo que o estimulasse a clicar, à semelhança do que ocorre com vídeos e fotos maiores. Além disso, podíamos usar a idéia de bookmarklet (que deu certo no MemeThis) para simplificar o processo de copiar e colar.
Após tentativas infrutíferas de embutir o player do SlideShare em um post de texto, decidimos converter as apresentações para algum formato que o Meme suportasse. Ficamos entre o vídeo (que permitiria um controle limitado de navegação, mas ficaria pesado) e o GIF animado (que é muito eficiente para dar uma amostra de alguns slides). Optamos pelo último, e não nos arrependemos.
O próximo passo seria analisar o formato das apresentações do SlideShare. Além de uma tabela YQL, o serviço possui uma API que revela algumas informações sobre as apresentações, mas não permite recuperar slides individuais. Pensamos em colocar um sniffer/proxy no meio do caminho e ver como o player recupera cada slide, mas Hasin Hayder teve a idéia primeiro e já deixou um script PHP prontinho, que gerava a URL de cada slide.
Nossa surpresa foi perceber que os slides não são arquivos de imagem: cada um deles é um arquivinho Flash (.swf), que o player do SlideShare embute. Converter isso para um GIF é bem mais complicado do que pode parecer. A maior parte das soluções para isso depende de um browser e/ou exige uma sessão X rodando – o que tornaria difícil escalar (e até mesmo hospedar de forma barata).
Achamos um software que conversava direto com o Flash para fazer isso, o SWF To Image ActiveX Freeware Library, cujo nome já ilustra as duas coisas que não nos agradam nele: não é open source e exige um servidor Windows. Entretanto, ele funciona muito bem, e entre os amigos e a cloud, não é impossível arrumar um servidor pra fazer o hack funcionar, então mandamos ver. Com um ASP minimalista disponibilizamos sua funcionalidade num serviço web, que recebe a URL de um .swf e devolve o primeiro (único no nosso caso) slide em .gif:
strFileURL = Request.QueryString("url") if Request.ServerVariables("REMOTE_ADDR")<>"ip.of.the.php.server" Then Response.Write("Invalid Caller") Response.End End If Set SWFToImage = CreateObject("SWFToImage.SWFToImageObject") SWFToImage.InitLibrary "demo", "demo" SWFToImage.InputSWFFileName = strFileUrl SWFToImage.FrameIndex= SWFToImage.ImageOutputType = 2 ' (GIF) SWFToImage.Execute Response.ContentType = "image/gif" Response.BinaryWrite(SWFToImage.BinaryImage) Set SWFToImage = Nothing
Fazendo dessa forma, essa parte fica isolada para o dia em que conseguirmos outra solução (o Gnash é bastante promissor – a versão dev já tem a opção de screenshot). Com isso, restou a tarefa de juntar os slides usando o ImageMagick – optamos por fazer isso no mesmo PHP que identifica a URL de cada um deles e chama o serviço acima para converetr um a um (e manter esse PHP no mesmo host, para otimizar):
$CONVERT = " "; $URL_PREFIX = "http://img.slide.memethis.com/"; $ASP_HOST = ""; $slideshowUrl = $_REQUEST["url"]; if (strpos($slideshowUrl, "http://www.slideshare.net/")!==) { die("Invalid parameter"); } $slideshowPageContent = file_get_contents($slideshowUrl); $pattern = "~doc=([\w-]+)~"; preg_match($pattern,$slideshowPageContent,$matches); $xmlurl = "http://s3.amazonaws.com/slideshare/{$matches[1]}.xml"; preg_match('/\(.*)\<\/title\>/',$slideshowPageContent,$matches); $title = $matches[1]; $sxml = simplexml_load_file($xmlurl); $tempprefix = "img/img".uniqid(); $i = 1; foreach ($sxml->Slide as $slide) { $filename = $tempprefix . $i . ".gif"; $ch = curl_init($ASP_HOST . "swf2gif.asp?url=" . $slide['Src']); $fp = fopen($filename, "w"); curl_setopt($ch, CURLOPT_FILE, $fp); curl_setopt($ch, CURLOPT_HEADER, ); curl_exec($ch); curl_close($ch); fclose($fp); $i++; if ($i>5) break; } $cmd = $CONVERT." -delay 200 -loop 0 ".$tempprefix."*.gif img/end.gif ".$tempprefix.".gif"; $WshShell = new COM("WScript.Shell"); $output = $WshShell->Exec($cmd)->StdOut->ReadAll; for($i=1; $i<=5; $i++) { unlink($tempprefix . $i . ".gif"); } echo $_REQUEST["callback"].'({"gif":"'.$URL_PREFIX.$tempprefix.'.gif","title":"'.$title.'","url":"'.$slideshowUrl.'"})';
Dessa vez (e ao contrário de como fizemos o MemeThis) não usamos oAuth ou YQL para postar – queríamos algo mais dinâmico, e a API semi-oficial de pré-preenchimento de formulários do Meme veio a calhar. A bookmarklet simplesmente injeta na página o código abaixo, que:
- Escurece a tela;
- Aciona o PHP (note que ele retorna JSON[P], justamente pra isso);
- Posta o resultado no Meme.
(tudo isso é apoiado em outra malandragem: aproveitamos o JQuery do próprio SlideShare, tomando o cuidado de só referenciar quando temos certeza que estamos no site certo)
var SLIDEMEME_PATH = "http://slide.memethis.com/"; function slideMeme() { if (location.href.indexOf("http://www.slideshare.net/")!=) { alert("This only works with SlideShare"); return false; } if (!$("#svPlayerId").length) { alert("You need to be on a presentation to use this"); return false; } $(' <div id="darkBackgroundLayer" class="darkenBackground"><img src="'+SLIDEMEME_PATH+'js/ajax-loader.gif" alt="" /></div> ').appendTo('body'); $.ajax({ url: 'http://img.slide.memethis.com/slidememe.php?callback=?', data: { url: location.href }, dataType: "jsonp", success: function (data, status) { location.href = 'http://meme.yahoo.com/dashboard/?photo='+data.gif+'&caption=<a href="'+data.url+'">'+data.title+'</a>'; }, error: function (xOptions, textStatus) { $("#darkBackgroundLayer").hide(); alert("Error! :-("); } }); } slideMeme();
Sim, o código acima pode e deve ser bem melhorado. Por exmeplo, o PHP 5 suporta objetos COM de forma muito natural, dispensando o ASP só para isso. E também poderíamos ter experimentado as bibliotecas do ImageMagick para PHP (ao invés de chamar o convert “na veia”).
Acima de tudo, todos os fontes deveriam ter um tratamento de erros e edge cases mais apropriado, e provavelmente menos redundância – mas nada disso cabe na realidade de um hack de 24h (que nós começamos um ou dois dias antes – dentro das regras – mas na soma final levou mais ou menos esse tempo mesmo).
Ah, teve um sub-hack extra: depois da correria (mas ainda no evento) eu olhei com mais carinho o PHP, percebendo que o processo de separar os slides era baixar um arquivo, aplicar uma expressão regular nele e iterar. Aí resolvi criar uma tabela YQL que faz isso. Ainda precisa ser melhorada (ex.: colocando os slides em itens-raiz para facilitar cruzamentos) mas já dá pra brincar com ela no YQL Console.
Ufa, foi um fim-de-semana movimentado… :-)
UPDATE: O Gleicon Moraes me passou a dica de que o swfrender (componente do swftools) consegue fazer a conversão. Eu cheguei a considerar ele na época, mas a documentação dá a entender que ele só funcionaria com arquivos previamente criados pelo swftools (o que pode ou não ser o caso dos slides do SlideShare). O fato é: funcionou em um ou dois testes preliminares, e pode ser a chave para migrar o hack para usar ferramentas livres e hospedagem mais acessível. Assim que eu tiver tempo vou olhar isso com carinho.
Comments
Willian Fernandes
E cade o Python ou Java? :P
Zuera!
A idéia ficou bem legal!
abraço,
@willian
Fabio FZero
Duas coisas:
1. Eu discordo que isso não mereça github ou sourceforge. Pode até ser um "hack grosseiro", mas colocando em um repositório mais gente vai ter chance de transformá-lo em algo elegante no futuro.
2. Isso também vai abrir a possibilidade de transportá-lo para alguma rede social que alguém efetivamente use. Afinal só a Bani gosta do Meme. :-D
chester
@willian: Hehehe, é verdade. Eu tentei incluir no texto essa piada interna (a gente queria fazer algo com Python/Java e terminou com ASP/PHP), mas ela funciona melhor ao vivo. :-D
chester
@fzero:
1. É verdade. Uma sugestão que eu ouvi já no brhackday foi que isso seria uma porta para viabilizar o SlideShare no iPhone (mas aí tem que ver a questão legal, cinco slides com link é "fair use").
2. Sobre outras redes, se forem não-textuais e expansíveis, é umas sim - a única que me veio à cabeça foi o Facebook (aliás, um outro hack que eu paguei um pau foi o Meme on Facebook).
2.1. Eu também gosto do Meme! O Twitter é legal porque limita a zona, o Meme o é justamente porque permite.
De qualquer forma, você me convenceu, vou subir esses cacos pro github (afinal, o que é meu flime queimado em mais um lugar, né? :-D #tôdebrinks).