Gerenciador de emulador retrô para Linux baseado no OpenEmu

@mozertdev , como sempre, excelentes sugestões e feedbacks!
já anotei todos aqui, vou documentar as issues pra sair esses ajustes na milestone v1.9.0. Adicionei uma opção secundária pra fazer scrap das capas pelo screenscraper.fr, já embiti minhas chaves que os mantenedores liberaram pra OpenEmux, ai caso tenha cota livre de uso, vai poder fazer o sync das capas por essa api, sem precisar usar credencial propria, mas se quiser usar a prorpia conta, deixei configurável em ajustes, e uma seção de avançados para usar a propria credencial de desenvolvedor e sobreescrever a do app. Usei a mesma abordagem que o pessoal usou no batousera e outros front-ends de emulação. vlw demais! abração!

1 curtida

próximos passos:

1 curtida

Obrigado pela explicação, @mozertdev :+1:

1 curtida

Opa @Guilherme_Feitoza , vai distribuir em Flatpak também? To esperando pela release da v1.9.0 para eu fazer a review! A idéia desses testes é simular um usuário comum que quer apenas adicionar sua biblioteca, arrumar as suas preferências sem muita fricção na hora de configurar as coisas, dar play no jogo e ter uma boa experiência fluida e sem travamentos. Por isso eu acabo iniciando e finalizando os games (não adiantaria a experiência de configuração estar funcionando bem se o jogo trava, corrompe ou para por algum motivo). A maioria desses jogos eu finalizo entre 50 minutos e 2 horas de gameplay, então acaba sendo o momento de lazer do dia e ainda aproveito pra ajudar no desenvolvimento do projeto.

Acho que talvez, no futuro, seja interessante ter outros perfis de testes como, por exemplo, o do “speedrunner” que quer ter organizado os jogos e save state para treinar as runs, o “colecionador” que gosta de ter tudo perfeitamente organizado com as coleções de roms separadas como gosta e etc e também o casual que não quer ter que baixar 300 emuladores diferentes e só quer entrar rápido alí e simplesmente tudo funcionar.

Acredito que v1.9.0 vai trazer uma experiência bem interessante para o perfil do usuário comum. Só de não ter que configurar aquele monte de emuladores diferentes já é um adianto e tanto kkkkkkk

1 curtida

@mozertdev, eu tentei publicar no flathub no início, mas tive uns problemas com a curadoria, aí adiei, quero fazer em fases, distribuir o arquivo .flatpak e criar um novo repo pra ser a “loja” do flatpak do OpenEmux, os ter atualização automática… A última fase vai ser tentar subir subir no flathub.

Curti muito aquela ideia de ter meu próprio server pra baixar as mídias, tive uns ideia de fazer o dump de alguma base e servir estaticamente como um CDN, pelo próprio Github pra não ter custos. As labels dos cartuchos ainda tão sendo um desafio, no libretro não tem, e no screenscraper tem, mas é a foto do cartucho inteiro, se eu fizer um dump, vou tentar automatizar o crop desses cartuchos pra ter somente a label pura (to estudando como fazer)

Boa ideia assumir perfil de usuário pra testar hehehehehe, hj vc é o principal QA do projeto hehehehehe. Tu tens ótimas sacadas. Bom final de semana!

Quando sair a 1.9.0 dou um ping aqui. Flw!

1 curtida

Ah, queria marcar teu usuário do Github no projeto, pra creditar as melhorias que vc propôs. hehehehe, se puder/tiver interesse compartilha aí :right_facing_fist:t2::left_facing_fist:t2:

1 curtida

E ai @Guilherme_Feitoza ! Tudo certo? Entendi. É melhor esperar mais um pouco e quando estiver mais propenso a aceitação lá você coloca o projeto pra apreciação. Bom que até lá tem muito polimento.

Acho que não tem muito segredo para fazer o recorte automatizado das labels não. É só codar um script CLI em Python usando Pillow + argparse e um script orquestrador em bash. No orquestrador você coloca a regra de consumo da API pra manter a base de dados bruta (as imagens dos cartuchos com as labels) deixa tudo organizado nas pastas (NES, SNES, GBA etc). Ainda no orquestrador você chama o script CLI em Python pra fazer o Crop das labels e salvar organizado nas pastas lembrando de ter uma pasta dedicada para os logs. Dai é só lançar como Cronjob.

Acredito que assim você tem versatilidade caso precise de novas funções e também facilidade de lidar com os dados baixados e os gerados.

Vou tentar exemplificar a idéia, a grosso modo, levando em consideração que todas as imagens chegam no mesmo padrão (tamanho, extensão e etc) quando faz a requisição via API:

Modelo do script CLI para crop das imagens

O script aceita imagens brutas de cartuchos de uma pasta de origem, realiza o corte com base em coordenadas de pixels específicas, converte opcionalmente o formato da imagem e salva o resultado em uma pasta de destino, registrando toda a operação em logs centralizados e individuais.

Parâmetro Obrigatório? Descrição Exemplo
–entrada Sim Caminho da pasta que contém as imagens brutas originais. –entrada ./dados_brutos/nes
–saida Sim Caminho da pasta onde as novas imagens recortadas serão salvas. –saida ./jogos_recortados/nes
–log Sim Pasta onde os arquivos de log (croplog_geral.log e individuais) serão salvos. –log ./logs_processamento
–box Sim Coordenadas do recorte no formato: esquerda,topo,direita,baixo. –box 30,50,180,200
–extensao Não Extensão(ões) de entrada aceitas (permite múltiplas separadas por vírgula). Padrão: .png –extensao .png,.jpg,.jpeg
–extensao-saida Não Formato/extensão da imagem final gerada. Padrão: .png –extensao-saida .jpg
–sobrescrever Não Define se sobrescreve arquivos existentes (true) ou roda no modo incremental apenas para arquivos novos (false). Padrão: false –sobrescrever false

Exemplos de uso:

Exemplo 1 (modo incremental):

python3 recortar.py \
    --entrada "./dados_brutos/nes" \
    --saida "./jogos_recortados/nes" \
    --log "./logs_processamento" \
    --box "30,50,180,200"

Exemplo 2 (modo de sobrescrita ou conversão de formato)

python3 recortar.py \
    --entrada "./dados_brutos/snes" \
    --saida "./jogos_recortados/snes" \
    --log "./logs_processamento" \
    --extensao ".png,.jpg,.jpeg" \
    --extensao-saida ".jpg" \
    --box "40,60,220,240" \
    --sobrescrever "true"

Modelo do script orquestrador em bash

O script orquestrador centraliza as regras globais de execução (como ligar ou desligar a sobrescrita para todos os consoles de uma só vez), executa o download das imagens brutas e dispara o script Python de recorte para cada sistema (NES, SNES, GBA e etc) com seus respectivos parâmetros de coordenadas.

#!/bin/bash

set -e

# VARIÁVEIS DE CONFIGURAÇÃO GERAL
PASTA_LOGS="path/completo/para/logs_processamento"
PASTA_ENTRADA_BASE="path/completo/para/dados_brutos"
PASTA_SAIDA_BASE="path/completo/para/jogos_recortados"
GLOBAL_SOBRESCREVER="false"

# ETAPA 1: Dump da API Externa
# Aqui você escreve tuda a lógica para fazer o download necessário da API
# Realiza dump das imagens da API externa e etc. Exemplo:"
# python3 api_downloader.py --output "./dados_brutos"


# ETAPA 2: Processamento dos dados brutos por Sistema (NES, SNES, GBA e etc)

# Processando pasta: NES"
python3 recortar.py \
    --entrada "$PASTA_ENTRADA_BASE/nes" \
    --saida "$PASTA_SAIDA_BASE/nes" \
    --log "$PASTA_LOGS" \
    --extensao ".png,.jpg,.jpeg" \
    --extensao-saida ".png" \
    --box "30,50,180,200" \
    --sobrescrever "$GLOBAL_SOBRESCREVER"

# Processando pasta: SNES"
python3 recortar.py \
    --entrada "$PASTA_ENTRADA_BASE/snes" \
    --saida "$PASTA_SAIDA_BASE/snes" \
    --log "$PASTA_LOGS" \
    --extensao ".png,.jpg,.jpeg" \
    --extensao-saida ".png" \
    --box "40,60,220,240" \
    --sobrescrever "true" # Exemplo de ajuste pontual para sobrescrever só o SNES

# Processando pasta: GBA"
python3 recortar.py \
    --entrada "$PASTA_ENTRADA_BASE/gba" \
    --saida "$PASTA_SAIDA_BASE/gba" \
    --log "$PASTA_LOGS" \
    --extensao ".png,.webp" \
    --extensao-saida ".jpg" \
    --box "20,30,150,180" \
    --sobrescrever "$GLOBAL_SOBRESCREVER"

PS: Depois eu entro no Github e abro uma issue lá no seu projeto, caso você queira creditar as melhorias anteriores, pode ser?

1 curtida

Hello!
Subi a pouco tempo a release 1.9.0

Agora em flatpak tmb.

1 curtida

Sim, sim com certeza! Acredito que consigo marcar nas issues, mesmo após fechadas.

1 curtida

Ah! Que da hora!
Eu fiz algo parecido com pillow também, fiz uma pequena POC pra detectar e cortar a label, até que funcionou bem. Mas aí percebi que tava usando a API errada pra pegar as imagens das labels no screenscraper, corrigi a API e ficou uma maravilha. Aí abortei essa feature do crop. Mais tarde posto um screenshot aqui.

1 curtida

3 curtidas

Peguei alguns pequenos bugs ontem, lancei um patch de correções.

2 curtidas

Opa @Guilherme_Feitoza , como estão as coisas? Então, fiz a análise da v1.9.2! Dessa vez testei um pouco mais a fundo. Escolhi o jogo “Aero Fighters (Sonic Wings)” para fazer esse teste, porquê a feature de Autofire se encaixa bem nele. Essa versão tá bem melhor que a v1.6.0 mas encontrei alguns bugs e vou relatar nesse post.

Como foi meu teste:

  1. Baixei e instalei o OpenEmux v1.9.2(AppImage) usando o AppImage Pool para integrar ao sistema (funcionou perfeitamente);

  2. O aplicativo já estava todo em português do Brasil reconhecendo “locale” (funcionou perfeitamente);
    2.1 Li todas as instruções no início e foram úteis. Desmarquei a caixa para reaparecer ao iniciar o OpenEmux (funcionou perfeitamente);

  3. Usei o botão de import na interface principal para importar todas as roms. Usei também o drag-and-drop com algumas roms. (funcionou perfeitamente);

  4. Não precisei utilizar o botão de sincronizar capas porquê o sistema já atualizou automáticamente quando importei as roms, mas o botão de sincronização segue funcionando. A API usada foi as da configuração padrão “a do próprio projeto + libretro” (funcionou muito bem para 95% das roms, porém alguns titulos ainda ficam com o ícone de fallback, acredito que seja os nomes, talvez se eu atualizar os nomes pode ser que a API reconheça);

  5. Alterei a apresentação das capas direto pelos botões na interface principal, testei todos os botões relacionados a apresentação das capas (funcionou perfeitamente);

  6. Alterei os filtros da lista de roms direto pelos botões na interface principal (funcionou perfeitamente);

  7. Utilizei a função de salvar favoritos. (funcionou perfeitamente);

  8. Utilizei a função de criar coleções. (funcionou perfeitamente);

  9. Em “Preferências > Controles” fiz o remap dos comandos do controle (Xbox Series S controller). Aceitou os comandos direto do controle sem conflito algum (funcionou perfeitamente);

  10. Ativei a feature de “Autofire” no controle usando o modo padrão. (funcionou perfeitamente);

  11. Ativei a feature de “Save State” no controle tanto para save quanto para load. (não funcionou);

  12. Iniciei e finalizei Aero Fighters do SNES sem problemas de emulação. O slowdown que tive é por conta da limitação do próprio SNES presente no console físico. (funcionou perfeitamente);
    12.1. Mas o controle analógico não funcionou por padrão em nenhuma das configurações. Tive que fechar o jogo, modificar os direcionais para os analógicos esquerdo e então ficou funcionando tanto o analógico esquerdo quanto os d-pads. (funcionou depois de modificações);
    12.2. O controle do som funcionou para mutar e desmutar mas o controle do volume não está funcionando. (funcionou parcialmente. Mas a parte principal não funcionou).

Sugestões:

  1. Notei que apresentação das capas está puxando muito processamento. Então testei enquanto observava as threads no BTOP e notei que todo o processamento está sendo executado em single thread. Talvez mudar a execução da lógica para multi-thread resolva o problema;

  2. Se estou com o jogo aberto e quero mudar os controles, eu faço as modificações porém elas só são lidas quando o jogo é fechado e aberto novamente. Seria interessante que essa atualização acontecesse sem precisar de fechar e abrir o jogo novamente;

  3. As vezes queremos apenas reiniciar o jogo e essa função não existe na interface. Seria bom ter um botão dedicado a reiniciar a rom sem precisar fecha-la e abri-la novamente;

  4. Atualmente, para acessar as Configurações / Preferências temos que clicar no botão de menu hamburguer para depois acessar as preferências. Seria interessante ter um botão de atalho na interface direto para as Configurações / Preferências tornaria a experiência mais fluida e intuítiva.

É isso. Acho que essa versão está muito boa (v.1.9.2), apesar desses pequenos bugs a experiência tá melhor do que na versão v1.6.0 (que já estava bacana).



2 curtidas

Opa! tudo na paz, @mozertdev!
Espero que esteja tudo ótimo por aí.

Vc trouxe excelentes pontos.
Já anotei aqui… vou redigir as issues, pra próxima milestone.

2 curtidas

Opa… Tudo bem Graças a Deus! Quando tiver mais novidades pode me chamar aqui que eu faço outra review!

Ainda bem que deu certo de resolver a API sem precisar de fazer crop.

PS: A escolha das suas roms ai tá top! Só jogo bom!

2 curtidas

E ai, @Guilherme_Feitoza ! Tudo certo?

Testei a hipotese da API não estar encontrando algumas capas por conta dos nomes e realmente era isso. Uma das minhas roms estava com o nome de Final Fantasy 2 e não achou a capa e quando renomeei para Final Fantasy II e pedi para atualizar as capas encontrou sem problemas.

Tendo constatado isso e sabendo que a atual solução com as APIs resolve 95% ou mais das capas, acredito que mudanças de API devem surtir pouco efeito para resolver esses 5% de ocorrencias.

A maior dificuldade de utilizar o método de renomear é que você nunca sabe qual é o nome correto que a API está reconhecendo, o que pode levar a inumeras tentativas até que dê certo.

Para resolver esse impasse pensei em uma solução: Se ao clicar com o botão direito sobre o item da rom sem capa existisse a opção “encontrar capa manualmente” ou algo similar. Essa opção abriria um “modal” onde ele teria uma buscador. Esse buscador é alimentado diretamente pelos nomes de capa validos registrados na API. O usuário escreve o nome do jogo de formar parcial e vai encontrar correspondências próximas, quando encontrar a correta ele a seleciona, no proprio “modal” já aparece um sample da capa para o usuário confirmar se é mesmo aquela, caso seja o usuário confirma e o próprio sistema já altera o nome da ROM para o correto da API e atualiza a capa.

Em um segundo momento essa feature pode ser refinada, adicionando um filtro para mostrar todas roms sem capa para facilitar realizar o procedimento de busca da capa e até mesmo fazer sugestões de capas.

Só um ponto de atenção aqui: O software está encontrando a capa segundo o nome do arquivo e encontrando a correspondencia na API segundo a alteração do nome do arquivo. Essa mudança de nome do arquivo pode acabar “quebrando” outras coisas como uma “Save State” ou “Save” que utiliza o nome do arquivo para exercer as funções. Talvez fosse interessante resolver essa lista de uma outra forma sem ter que alterar o nome do arquivo em si, mas só a representação dele no banco de dados.

2 curtidas

ah! essa opção existe, hehehehe já tá na v1.9.2
quando vai no menu de contexto da rom (com botão direito ou nos 3 pontos “…”) tem a opção
“gerenciar capa…” ou “gerencia rótulo…” dependendo do modo de visualização, ai aparece esse gestor aqui:

dá pra pesquisar pelo nome ou pelo hash da rom.

2 curtidas

realmente esse ponto me pegou, se renomear os arquivos, vai perder referencia da capa já baixada e o save state…
vou abrir uma issue sobre isso tmb…
me passaram outros bugs desapercebidos também de configuração de controle. alguns atalhos não tão funcionando, e no N64 não tava funcionando corretamente o direcional + analógico

vou tentar resolver esses bugs que incomodam mais nessa milestone 1.10.0

valeu, @mozertdev !

2 curtidas

Eu tentei usar dessa maneira mas aparentemente o buscador é muito acertivo e busca nomes muito precisos e também demora bastante até terminar a busca (acabei não conseguindo encontrar as capas que eu testei em varias tentativas de cada uma delas, mas ainda é melhor que renomear e pedir para sincronizar).

Talvez se funcionasse em duas etapas, a primeira como um Fuzzy Finder que lista somente os nomes registrados que podem ser o que você busca e uma segunda etapa que mostra a arte da capa depois de escolher uma das opções.

Por exemplo quero encontrar a capa de “Mega Man & Bass”

Busco por “Mega Man”;
Recebo uma lista com N possibilidades dentro do banco da API (Mega Man X, Mega Man X 2, Mega Man X 3 e etc);
Procuro pelo nome que mais se adequa;
O modal mostra a Sample da capa;
Confiro e confirmo ou procuro por outra se não for a que preciso.

1 curtida

Boa ideia… Eu vou ver se as outras API suporta busca fuzzy, posso normalizar os nomes na busca automática, mas no dump que fiz das capas do libretro, talvez eu consiga melhorar a busca. Não sei se vc testou ainda… Esse modal de gerenciar a capa tem 3 abas, a última é pra importar um arquivo, seria em último caso de não conseguir achar as imagens nos provedores de ArtWork, da pra pegar alguma na internet. Ah! e ainda dá pra fazer edição básica.

1 curtida