Sistema de Capas
Como o InkShelf gera, armazena e prioriza o carregamento das miniaturas de capa exibidas na Biblioteca, sem que isso trave a rolagem em acervos grandes.
Extração
A extração abaixo vale pra arquivos locais. Itens Komga/Kavita não são extraídos — a capa vem pronta da API do servidor: ensureKomgaCover busca a miniatura do livro (ou a página em resolução cheia, se "capas em alta qualidade" — hqCovers, desligado por padrão — estiver ligado naquela biblioteca) e ensureKavitaCover usa GET /api/Image/chapter-cover. Precisa de rede na primeira busca de cada item; depois de baixada, a capa cai no mesmo cache em disco descrito abaixo — sem diferença de tratamento a partir daí. Ver Fontes Remotas.
A capa de um arquivo local é sempre a primeira página/imagem dele, obtida sem precisar abrir o leitor completo:
- CBZ/ZIP: primeira imagem do arquivo, em ordem natural de nome (
Página 2vem antes dePágina 10). - CBR (RAR4) e RAR5: mesmo princípio, com o motor de extração correspondente a cada variante do formato.
- PDF: primeira página é renderizada via
PdfRenderernativo do Android. - EPUB: a imagem de capa declarada no OPF do livro é usada quando existe; se não houver, cai para a primeira imagem encontrada no HTML do primeiro capítulo — incluindo capas geradas por ferramentas como o Calibre, que embutem a imagem dentro de um elemento SVG em vez de uma tag
<img>comum.
O bitmap extraído é reduzido (amostragem por potência de 2 na decodificação, depois redimensionado) e salvo como JPEG no cache do app, em cache/covers/{id-do-arquivo}.jpg. O arquivo original nunca é modificado.
Capa de pasta
Uma pasta não tem página própria para extrair capa, então sua miniatura vem de um arquivo dentro dela:
- Por padrão, é o primeiro arquivo encontrado ao percorrer a árvore da pasta (primeiro na própria pasta; se vazia de arquivos diretos, desce nas subpastas).
- O usuário pode fixar manualmente qual arquivo representa a pasta, pela ação "definir como capa" no menu de contexto (ver Biblioteca) — essa escolha manual tem prioridade sobre a automática.
- Se uma pasta ainda não tem capa própria, ela herda a capa do primeiro arquivo/subpasta que a ganhar, e essa capa se propaga para cima: a pasta-mãe (e a mãe dela, e assim por diante) também adota a mesma miniatura, até encontrar uma pasta que já tem capa definida. Isso evita pastas com ícone genérico só porque seu conteúdo está um nível mais abaixo.
Carregamento e priorização
A extração de capa é um trabalho pesado (abrir um arquivo compactado, decodificar uma imagem) e a Biblioteca pode listar milhares de itens. Para não travar a rolagem nem duplicar trabalho:
- Existe uma única fila de pedidos de capa compartilhada pelo app inteiro, consumida por um número fixo de workers em segundo plano (4 workers de I/O — rede e disco) — não importa quantas telas estejam pedindo capas ao mesmo tempo, a concorrência total é sempre limitada. A parte de CPU (decodificar, redimensionar, comprimir) roda num pool próprio de 2 threads em prioridade de background, separado do
Dispatchers.IO, pra não disputar CPU de igual pra igual com a interface. - As gravações de
coverPathemfiles/folderssão acumuladas e aplicadas numa única transação a cada ~400 ms. CadaUPDATEisolado invalida a tabela inteira no Room e faz toda tela que observa a biblioteca reconsultar — em lote, uma rajada de capas vira uma invalidação. A tela Início ainda se protege do outro lado: seleciona as fileiras sobre projeções semcoverPath(estáveis enquanto só capas mudam) e hidrata só os itens exibidos — ver Tela Inicial. - A fila é LIFO, não FIFO: o pedido mais recente é atendido primeiro. Como cada card pede sua capa só quando entra na composição da tela, isso prioriza naturalmente os itens que estão sob o dedo do usuário durante um scroll longo, em vez de processar em ordem de uma fila que pode ter milhares de pedidos acumulados.
- Pedidos duplicados (duas telas pedindo a mesma capa ao mesmo tempo) são deduplicados por chave.
- Ao trocar de pasta, os pedidos pendentes daquela tela são descartados imediatamente — só o que já estava em execução termina.
- Antes de qualquer extração, o worker verifica se a capa já existe em cache (linha no banco + arquivo em disco); só faz o trabalho pesado se realmente faltar.
Atualização e limpeza
- "Atualizar capa" no menu de contexto força uma nova extração, ignorando o cache existente.
- Limpar o cache de capas (disponível nas configurações) apaga todas as miniaturas de uma vez.
- Miniaturas de arquivos que saem da biblioteca (apagados, movidos ou pasta raiz removida) são limpas automaticamente pelo próprio processo de sincronização — ver Scanner.