NULL on error flipping bits whilst updating pixels

Empreendendo sem dinheiro

Criei uma espécie de desafio mental, esse desafio consiste em resolver problemas já enfrentados ou de outras origens de forma criativa, em pouco tempo e se possível evolvendo pouco ou nenhum custo!

E o desafio da semana foi criar um daqueles projetos de “media indoor”.

Então pensei: “já sei vou usar grpc, s3 rsync, elastic transcoder, fastly, frontend do admin em react e é claro kubernetes!”

Já no cliente, que é responsável por tocar os vídeos…: “vou escrever em Qt, criar a interface em QML, usar boost.asio para o meu downloader e criar uma distribuição de Linux embarcado usando o yocto !“.

“The best code is no code at all.”

Painel administrativo

As pessoas, e desse ramo principalmente, estão acostumadas a usar planilhas para as mais variadas tarefas, então por que não usar uma planilha como “admin”?

Google Sheets

Envio, armazenamento, transcodificação e distribuição de vídeos

Ao invés de desenvolver um complexo sistema de gerenciamento de arquivos de vídeo, com transcodificação usando as ferramentas que citei acima, usando lambda e outras coisas, vamos usar o YouTube.

Quê?

É isso mesmo, no nosso protótipo vamos usar o YouTube, pois não tem nenhum custo e já faz a conversão e distribuição do vídeo nos mais variados formatos e tamanhos.

Atenção: De acordo com os termos de uso do YouTube não é permitido reproduzir o conteúdo da plataforma fora do player do youtube, o que estou demonstrando é apenas para fins educacionais.

O nosso work é playá

Nada mais do que um pequeno script em bash será necessário para executar as tarefas de baixar a playlist, os vídeos, a remoção de vídeos não mais usados entre outras coisas.

Já o player propriamente dito é o omxplayer, que é capaz de decodificar vídeos usando aceleração por hardware; omxplayer foi escrito especialmente para a GPU do Raspberry Pi e faz uso da API OpenMAX, é extremamente eficiente.

O trecho abaixo é de um apps script que transforma a primeira coluna da planilha num array de objetos e serializa a reposta num JSON.

function doGet(request) {
  var app = SpreadsheetApp;
  var worksheet = app.getActiveSpreadsheet();
  var sheet = worksheet.getSheetByName(request.parameters.sheet);
  if (sheet) {
    var range = sheet.getDataRange();
    var values = range.getValues();
    var headers = values.shift();
    var playlist = values.map(function (el) {
      return { url: el[0] };
    });
    return ContentService.createTextOutput(JSON.stringify({ playlist: playlist })).setMimeType(
      ContentService.MimeType.JSON,
    );
  }
}

É possível publicar o script acima num endereço público e de acesso anônimo, de modo que seja possível baixar o JSON até mesmo pelo cURL, e é com essa reposta que iremos usar para saber quais arquivos baixar e gerar a playlist:

$ curl -sL "https://script.google.com/macros/s/${...}/exec?sheet=Sheet1" | jq
{
  "result": [
    {
      "url": "https://www.youtube.com/watch?v=..."
    },
    ...
  ]
}

Com uma simples entrada no cron é possível criar um agendamento para baixar a playlist de tempos em tempos:

*/30 * * * * user curl -sL "https://script.google.com/macros/s/${...}/exec?sheet=Sheet1" > playlists/1.json

A função download usa a ferramenta jq para gerar uma lista de urls a serem baixadas pelo youtube-dl que por sua vez executa um pequeno script (--exec) para adicionar o arquivo recém baixado para a playlist, tomando cuidado para não duplicar:

download() {
  cat playlists/*.json | jq '.[].url' \
    | xargs youtube-dl --exec "grep -sqxF '{}' $playlist || echo '{}' >> $playlist"
}

Alguns parâmetros do youtube-dl foram omitidos pois foram movidos para o arquivo de configuração global.

Com o entr é possível monitorar se algum arquivo foi modificado ou mesmo adicionado novos arquivos no diretório; se isso acontecer, a função download será chamada:

watch() {
  while :; do
    ls -d playlists/*.json | entr -d "$0" download
  done
}

De tempos em tempos é necessário remover os arquivos antigos e downloads incompletos; a função recycle remove todos os arquivos do tipo vídeo modificados pela última vez há mais de 7 dias e que não estão sendo usados:

A razão de ser alguns dias depois e não imediatamente é de ser maleável caso tenha sido algum equívoco.

recycle() {
  declare -a args=(
    -mtime +7
    -type f
  )

  while read -r uri; do
    args+=(-not -name "$uri")
  done <<< "$(cat $playlist)"

  find "$PWD" "${args[@]}" -exec bash -c "file -b --mime-type {} | grep -q ^video/" \; -delete
}

Todas essas funções podem ser chamadas inúmeras vezes sem efeitos indesejados.

Tocar a playlist é a parte mais fácil:

play() {
  setterm -cursor off

  export DISPLAY=":0.0"

  while :; do
    while read -r uri; do
      omxplayer --refresh --no-osd --no-keys "$uri"
      xrefresh -display :0
    done <<< "$(cat $playlist)"
  done
}

Graças ao omxplayer o consumo de CPU fica bem baixo, mesmo em 1080@60fps, algo em torno de ~0.5% num Raspberry 3.

O próximo passo é contabilizar algumas estatísticas, como o número de vezes que um vídeo foi tocado, se teve alguma interrupção por falta de energia ou por problemas técnicos, etc.

Para isso uma boa pedida é o papertrail, com essa ferramenta é possível centralizar todos os logs da máquina, exportar para o BigQuery e executar as consultas na mesma planilha que ficam os vídeos.

Ops… Acho que minha febre por cloud computing voltou :-)