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
| Campo | Tipo | Obrig. | Notas |
|---|---|---|---|
schemaVersion | número | sim | Sempre 1. |
id | string | sim | Nã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. |
version | string | sim | Ex.: "1.0.0". Precisa ser string (não número). |
title | string | sim | Não vazio. Nome mostrado no app. |
author | objeto | não | { "id": string?, "handle": string? }. |
type | string | sim | "metal", "web" ou "video". |
entry | string | sim | Caminho do arquivo principal a partir da raiz do pacote, ex. content/shader.metal. Precisa existir. |
minMacOS | string | não | Ex.: "26.0". Se o Mac for mais antigo, a importação falha. |
checksum | string | recomendado | sha256-… sobre content/ (algoritmo abaixo). Se presente, precisa bater, na importação e a cada carregamento. |
config | array | não | Ajustes do usuário (tabela abaixo). |
capabilities | objeto | não | Tabela 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[]
| Campo | Tipo | Notas |
|---|---|---|
key | string | Obrigatório. Nome do valor entregue ao wallpaper. |
type | string | float, int, bool, color. Outros (inclusive enum) são ignorados. |
label | string | Texto do controle. Padrão: key. |
min, max | número | float/int. Padrão 0 e 1. |
default | número / bool / string | float/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
| Campo | Tipo | Notas |
|---|---|---|
network | array de strings | Declare []. Hoje toda rede é bloqueada para wallpapers web, mesmo hosts listados aqui. Inclua tudo no pacote. |
audio | bool | Reservado. Declare com honestidade. Vídeo sempre toca mudo. |
nowPlaying | bool | Só web. Se true, a página recebe a música tocando no Music/Spotify (ver abaixo). |
Algoritmo do checksum (normativo)
- Todos os arquivos regulares sob
content/, com caminho relativo acontent/usando/. - Ordene por bytes UTF-8 crescentes.
- SHA-256 alimentado, para cada arquivo, com:
caminho UTF-8+ byte0x00+bytes do arquivo. - 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
- Arquivo
.metalúnico com código-fonte MSL, compilado no Mac do usuário. - Funções obrigatórias:
vertex … v_mainefragment float4 f_main. - Uniforms no
buffer(0)do fragmento, com a struct exatamente assim (32 bytes):
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
};
- Não existem
frame,mouse, texturas nem outros valores deconfigno shader. - O vértice desenha 3 vértices (triângulo de tela cheia); use o
v_maindo guia. fragCoord.xyem pixels, origem no canto superior esquerdo.- Saída em
bgra8Unorm, 8 bits, opaca. - Taxa de quadros: segue a tela (ProMotion); o app reduz ou pausa por bateria/visibilidade.
Proibido (checagem textual, sensível a maiúsculas, comentários ignorados):
| Trecho | Motivo |
|---|---|
kernel / [[kernel | shaders 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
entrydeve ser um.html. A pasta doentryvira a raiz servida emlwp://wallpaper/; só arquivos dentro dela (e subpastas) são acessíveis.- Tipos servidos com MIME correto: html, js/mjs, css, json, png, jpg, gif, webp, svg, mp4/m4v,
wasm, woff/woff2, ttf. Outros saem como
application/octet-stream. - Bloqueado: toda rede,
file://, navegação para outra página, câmera/microfone/localização. - Dados não persistem (armazenamento efêmero). Sem mouse/teclado.
console.error/console.warne erros não tratados vão para o log do app (ver Depuração).- Na importação, o app registra avisos (não bloqueiam) se encontrar
fetch(,XMLHttpRequest,WebSocket,EventSource,importScripts,http://,https://,eval(,new Function(,atob(.
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
.mp4,.movou.m4v, decodificado pelo macOS (HEVC/H.264). Loop sem emenda, mudo, preenche a tela (corta bordas). Semconfig.- Importar um vídeo solto cria o pacote automaticamente (
idderivado do conteúdo: importar o mesmo vídeo de novo substitui em vez de duplicar).
Limites
| Limite | Valor |
|---|---|
| Tamanho por arquivo (descompactado) | 256 MB |
| Arquivos por pacote | 4096 |
| Caminhos | sem .. e sem / inicial |
Planos
| Grátis | Premium | |
|---|---|---|
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…”).
| Mensagem | Causa / 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):
- Metal não compila, ou falta
v_main→ preto. Teste pequenas mudanças de cada vez. typenão corresponde ao arquivo → preto.- Web: erro de JavaScript, asset referenciado fora da pasta do
entry, ou algo buscado na internet (bloqueado).
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).