Referência: formato .livewallpaper v1

Versão do formato: 1 (congelada). Testado com PrimoEngine 0.4.11, macOS 26.

Estrutura do arquivo

Um ZIP (compressão store ou deflate) com:

manifest.json      obrigatório, na raiz (ou dentro de UMA única pasta de topo)
content/           obrigatório, o conteúdo do wallpaper
thumbnail.png      opcional, miniatura mostrada no app (fora do checksum)

Arquivos extras na raiz são ignorados. A extensão pode ser .livewallpaper ou .zip.

manifest.json

CampoTipoObrig.Notas
schemaVersionnúmerosimSempre 1.
idstringsimNão vazio. Identidade estável do wallpaper; importar outro pacote com o mesmo id substitui o instalado. Use domínio reverso (com.seunome.nome) ou um UUID.
versionstringsimEx.: "1.0.0". Precisa ser string (não número).
titlestringsimNão vazio. Nome mostrado no app.
authorobjetonão{ "id": string?, "handle": string? }.
typestringsim"metal", "web" ou "video".
entrystringsimCaminho do arquivo principal a partir da raiz do pacote, ex. content/shader.metal. Precisa existir.
minMacOSstringnãoEx.: "26.0". Se o Mac for mais antigo, a importação falha.
checksumstringrecomendadosha256-… sobre content/ (algoritmo abaixo). Se presente, precisa bater, na importação e a cada carregamento.
configarraynãoAjustes do usuário (tabela abaixo).
capabilitiesobjetonãoTabela abaixo.

Campos desconhecidos são ignorados. Um .metal precisa ser type: "metal", um .html type: "web", um vídeo type: "video". Tipo errado não dá erro na importação, mas o wallpaper fica preto.

config[]

CampoTipoNotas
keystringObrigatório. Nome do valor entregue ao wallpaper.
typestringfloat, int, bool, color. Outros (inclusive enum) são ignorados.
labelstringTexto do controle. Padrão: key.
min, maxnúmerofloat/int. Padrão 0 e 1.
defaultnúmero / bool / stringfloat/int: número (padrão = min); bool: true/false (padrão false); color: "#RRGGBB" (padrão "#FFFFFF").

int é mostrado como slider contínuo e entregue como número decimal: arredonde no seu código.

capabilities

CampoTipoNotas
networkarray de stringsDeclare []. Hoje toda rede é bloqueada para wallpapers web, mesmo hosts listados aqui. Inclua tudo no pacote.
audioboolReservado. Declare com honestidade. Vídeo sempre toca mudo.
nowPlayingboolSó web. Se true, a página recebe a música tocando no Music/Spotify (ver abaixo).

Algoritmo do checksum (normativo)

  1. Todos os arquivos regulares sob content/, com caminho relativo a content/ usando /.
  2. Ordene por bytes UTF-8 crescentes.
  3. SHA-256 alimentado, para cada arquivo, com: caminho UTF-8 + byte 0x00 + bytes do arquivo.
  4. Resultado: "sha256-" + hex minúsculo.
import hashlib, os
def checksum(content_dir):
    rels = sorted((os.path.relpath(os.path.join(r, f), content_dir).replace(os.sep, "/")
                   for r, _, fs in os.walk(content_dir) for f in fs if f != ".DS_Store"),
                  key=lambda p: p.encode())
    h = hashlib.sha256()
    for rel in rels:
        h.update(rel.encode()); h.update(b"\0")
        h.update(open(os.path.join(content_dir, rel), "rb").read())
    return "sha256-" + h.hexdigest()

Atenção: se o ZIP tiver um .DS_Store dentro de content/, ele entra no cálculo do app. Não o inclua.

Contrato Metal

struct Uniforms {
    float2 resolution;   // pixels do drawable (inclui Retina)
    float  time;         // segundos desde o início
    float  speed;        // config "speed" (float), padrão 1.0
    float4 tint;         // config "tint" (color) em RGB 0..1, a = 1, padrão branco
};

Proibido (checagem textual, sensível a maiúsculas, comentários ignorados):

TrechoMotivo
kernel / [[kernelshaders de computação não são permitidos
device buffers device graváveis não são permitidos
threadgroup memória threadgroup não é permitida
atomic_operações atômicas não são permitidas
#include "include local não é permitido (#include <metal_stdlib> pode)

Ambiente web

API da página:

window.LiveWallpaper.config          // { key: valor }, pronto antes dos seus scripts
window.LiveWallpaper.onConfig = cfg => { … }        // chamado quando o usuário muda ajustes

// Só com "capabilities": { "nowPlaying": true }
window.LiveWallpaper.onNowPlaying = np => { … }
// np = { isPlaying, title, artist, album, position, duration, source, artwork? }
// position/duration em segundos; artwork = data URI, enviado só uma vez por faixa; guarde-o.

Vídeo

Limites

LimiteValor
Tamanho por arquivo (descompactado)256 MB
Arquivos por pacote4096
Caminhossem .. e sem / inicial

Planos

GrátisPremium
Importar/compartilhar wallpapers metal e web✅✅
Wallpapers video❌✅

Erros

Mensagens exatas, como aparecem no alerta “Could not import wallpaper”, e como resolver. As duas mensagens “The data couldn’t be read…” vêm do macOS e podem aparecer no idioma do sistema (ex.: “Não foi possível ler os dados…”).

MensagemCausa / solução
Not a valid .livewallpaper (ZIP) file.O arquivo não é um ZIP (ou está truncado). Gere de novo com pack.py.
Corrupt archive: unsafe path '…'.Algum caminho tem .. ou começa com /.
Corrupt archive: unsupported compression method N.Use ZIP com store ou deflate (padrão do zip e do pack.py).
Corrupt archive: … (outros)ZIP danificado. Gere de novo.
Archive or an entry exceeds the size limit.Arquivo > 256 MB ou mais de 4096 arquivos.
Package has no manifest.json.manifest.json não está na raiz do ZIP. Zipe o conteúdo da pasta, não a pasta com lixo do Finder (__MACOSX).
The data couldn't be read because it is missing.Falta um campo obrigatório: schemaVersion, id, version, title, type, entry (ou key/type num item de config).
The data couldn't be read because it isn't in the correct format.JSON inválido ou tipo errado: vírgula sobrando, "schemaVersion": "1", "version": 1, type fora de metal/web/video, min como string…
Unsupported manifest schemaVersion N (expected 1).Use "schemaVersion": 1.
Manifest field 'id' is missing or empty. (ou title, entry)Preencha o campo.
Wallpaper requires macOS X, which is newer than this system.Diminua minMacOS (ex.: "26.0") ou remova.
Package checksum does not match its content (possibly corrupt or tampered).Você mudou content/ depois de calcular o checksum, ou há .DS_Store no ZIP. Rode pack.py de novo.
Package entry file '…' is missing.entry não aponta para um arquivo existente. Deve incluir content/ e respeitar maiúsculas.
Shader rejected: … .Um trecho proibido (tabela Metal acima). Remova/renomeie.
Shader is missing the required 'f_main' fragment function.Nomeie o fragmento f_main.
“.ext” isn't a supported video (use .mp4, .mov or .m4v).Converta o vídeo.
That video is N MB — the limit is 256 MB.Comprima (HEVC) ou encurte.

Falhas silenciosas (importa, mas fica preto/parado):

Depuração

Veja o log do app no Terminal enquanto o wallpaper roda:

log stream --level info --predicate 'subsystem == "com.livewallpaper.app"'

Procure Shader compile/pipeline failed (erro de compilação Metal, com linha) e mensagens da categoria WebConsole (erros de JavaScript).