Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion plugins/media-agent/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "media-agent",
"displayName": "Media Agent",
"version": "0.4.0",
"version": "0.4.1",
"author": {
"name": "4nrry",
"url": "https://github.com/4nrry"
Expand Down
51 changes: 50 additions & 1 deletion plugins/media-agent/skills/geracao-ia/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,48 @@ a montagem esta em `references/montagem.md`.
Nao amplie o clipe do Grok para usar em 1080p: DAT2 leva ~9,5 s por quadro
(23 min para 6 s). Se o plano precisa de 1080p, e Veo.

## Porte do modelo: rapido, medio, grande

Escolha pelo uso do plano na peca, nao pelo ranking. **Medido** = rodou aqui, uma vez (n=1), em
13/10/2026, a partir do mesmo still de leiras ao amanhecer; **so lista** = o
modelo aparece na `models.list` da chave, mas nada foi gerado com ele; **so
docs** = so fonte externa.
Precos da pagina de precos do Gemini API e da xAI em 13/10/2026; ranking da
Artificial Analysis no mesmo dia (a pagina nao data o snapshot).

**Imagem**

| porte | quando | use | estado |
|---|---|---|---|
| rapido | explorar direcoes, folha de contato, descartavel | `grok-imagem.sh` (assinatura) | medido |
| medio | still final de carrossel, capa, fundo de video | `agy-imagem.sh` (assinatura) | medido; o modelo por tras do `generate_image` do agy nao foi identificado |
| medio, pela API | quando a assinatura do agy esgotar, ou para lote | `nano.py gemini-nano-banana-2.1` (US$ 0,034 por imagem 1K; #4 em texto→imagem, empatado com o Grok Image 2.0) | medido: 12 s, 1376x768, still forte, luz e neblina coerentes |
| grande | edicao fina de um still existente, detalhe que o medio erra | `nano.py gemini-3-pro-image --image still.png` | medido: 17 s; manteve composicao e enquadramento exatos, mas exagerou o que foi pedido ("vapor" saiu como labareda): peca a mudanca com moderacao |

**Video**

| porte | quando | use | estado |
|---|---|---|---|
| rapido | fundo desfocado, card pequeno, Reels com tipografia por cima | `grok-video.sh --image` (480p, padrao da API) ou `--res 720p` quando o clipe fica visivel | medido: 480p como antes; com `--res 720p`, 1280x704, 6 s, 86 s |
| rascunho barato | testar movimento de camera antes de gastar no definitivo; tambem plano final em 720p | `veo.py --model veo-3.1-lite-generate-preview --res 720p --negative ""` (US$ 0,05/s) | medido: 44 s, 8 s em 720p com audio; manteve as leiras continuas. **Recusa `negativePrompt`** (HTTP 400): passe `--negative ""` |
| medio | plano que aparece inteiro na tela, 1080p | `veo.py` (padrao `veo-3.1-fast-generate-preview`) | medido (~110 s, 8 s em 1080p) |
| grande | plano principal, close, onde artefato aparece | `veo.py --model veo-3.1-generate-preview` (US$ 0,40/s em 1080p) | medido: 107 s, 8 s em 1080p com audio. **1080p so com `--seconds 8`** (4 s recusa). Neste still avancou mais que o pedido e quebrou as leiras em montes separados: pior fidelidade que o Lite, n=1 |

No unico teste, o Veo mais caro nao foi o melhor: o Lite seguiu o still, o
Standard reinventou a geometria. Antes de pagar o Standard num plano, rode o
Lite com o mesmo prompt e so suba se o Lite falhar no que importa (detalhe em
1080p, artefato no meio do clipe).

Candidato que o `veo.py` ainda nao chama: `gemini-omni-1.1-flash` aparece na
lista da chave e fica acima do Veo 3.1 no ranking de video com audio (#7-8,
contra o Veo fora do top 10), a ~US$ 0,10/s em 720p. A API e outra (por token,
nao `predictLongRunning`), e imagem→video com ele nao foi testado: nao troque o
Veo por ele numa peca antes de um teste.

O Grok Video 1.5 gera 1080p nativo de texto ou imagem, mas fica em 720p quando
segue um video de referencia, e o 1.5-lite so chega a 1080p por upscale (so
docs). Na imagem→video o `aspect_ratio` e ignorado: sai na proporcao do still.

## O fluxo que controla a composicao

Video gerado direto do texto escolhe o enquadramento sozinho: pedindo "plano
Expand Down Expand Up @@ -67,7 +109,14 @@ volume nao foram medidos.
aceita `--aspect 9:16` para Reels.
- **Veo em ingles e com verbo de camera**: "slow push forward", "gentle orbit
left", "static shot, mist drifting". Descreva o movimento, nao a cena: a cena
ja esta no still.
ja esta no still. **Um** movimento de camera por plano, com o enquadramento
("medium shot"); dois movimentos concorrentes saem tremidos (guia de prompt
da DeepMind para o Veo).
- **Som do Veo numa linha propria**, "Audio: vento leve, passaros ao longe".
Fala com dois-pontos depois de quem fala, nao entre aspas: aspas podem virar
texto na tela (so docs, nao testado aqui).
- **Grok sem negativo no still**: a API de imagem nao tem campo de negativo;
descreva o material que quer em vez de listar o que evitar.
- **Uma mudanca por rodada.** Mudou a hora e a altura juntas, nao sabe qual
melhorou.

Expand Down
19 changes: 15 additions & 4 deletions plugins/media-agent/skills/geracao-ia/scripts/grok-video.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
# SuperGrok, e confere que o MP4 saiu. Com --image, anima uma imagem existente como
# quadro inicial.
#
# grok-video.sh "descricao do movimento" saida.mp4 [--image quadro.png]
# grok-video.sh "descricao do movimento" saida.mp4 [--image quadro.png] [--res 720p]
#
# Sem --res a API gera em 480p (o padrao dela). --res vai no pedido ao agente.
#
# Sob privacidade ZDR, o video so sai se ~/.grok/managed_config.toml tiver a secao
# [tools.zdr_video_output_s3] apontando para um bucket seu (ver SKILL.md).
Expand All @@ -13,17 +15,26 @@ desc=$1
out=$(realpath -m "$2")
shift 2
image=""
if [ "${1:-}" = "--image" ]; then image=$(realpath "$2"); fi
res=""
while [ "$#" -gt 0 ]; do
case "$1" in
--image) image=$(realpath "$2"); shift 2 ;;
--res) res=$2; shift 2 ;;
*) echo "grok-video: opcao desconhecida $1" >&2; exit 2 ;;
esac
done
quality=""
[ -n "$res" ] && quality=" Gere em resolucao $res, nao no padrao de 480p."
command -v grok >/dev/null || { echo "grok-video: grok nao esta no PATH" >&2; exit 1; }
dir=$(dirname "$out")
mkdir -p "$dir"

if [ -n "$image" ]; then
prompt="Anime a imagem existente $image com sua ferramenta nativa de video, usando ESSA imagem como \
quadro inicial (nao gere um quadro novo). Movimento: $desc. Salve como $out. \
quadro inicial (nao gere um quadro novo). Movimento: $desc.$quality Salve como $out. \
Nao escreva codigo para criar o video. No fim responda so com o caminho do arquivo."
else
prompt="Use sua ferramenta nativa de geracao de video para gerar UM video curto: $desc. \
prompt="Use sua ferramenta nativa de geracao de video para gerar UM video curto: $desc.$quality \
Salve como $out. Nao escreva codigo para criar o video. No fim responda so com o caminho do arquivo."
fi

Expand Down
44 changes: 44 additions & 0 deletions plugins/media-agent/skills/geracao-ia/scripts/nano.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#!/usr/bin/env python3
"""Gera ou edita uma imagem com um modelo de imagem do Gemini (generateContent). So stdlib.

nano.py MODELO "prompt" -o saida.png [--image entrada.png] [--aspect 16:9]

MODELO: gemini-nano-banana-2.1 (gerar) ou gemini-3-pro-image (editar um still).
GEMINI_API_KEY vem do ambiente (injete com o `run` do seu gerenciador de segredos).
"""
import argparse, base64, json, mimetypes, os, sys, time, urllib.error, urllib.request

API = "https://generativelanguage.googleapis.com/v1beta"

p = argparse.ArgumentParser()
p.add_argument("model")
p.add_argument("prompt")
p.add_argument("-o", "--out", required=True)
p.add_argument("--image")
p.add_argument("--aspect", default="16:9")
a = p.parse_args()
key = os.environ["GEMINI_API_KEY"]
parts = [{"text": a.prompt}]
if a.image:
mime = mimetypes.guess_type(a.image)[0] or "image/png"
parts.append({"inlineData": {"mimeType": mime, "data": base64.b64encode(open(a.image, "rb").read()).decode()}})
body = {"contents": [{"parts": parts}],
"generationConfig": {"responseModalities": ["IMAGE"], "imageConfig": {"aspectRatio": a.aspect}}}
t0 = time.time()
req = urllib.request.Request(f"{API}/models/{a.model}:generateContent", data=json.dumps(body).encode(),
headers={"x-goog-api-key": key, "Content-Type": "application/json"})
try:
with urllib.request.urlopen(req, timeout=300) as r:
resp = json.load(r)
except urllib.error.HTTPError as e:
sys.exit(f"HTTP {e.code}: {e.read().decode(errors='replace')[:800]}")
for c in resp.get("candidates", []):
for part in c.get("content", {}).get("parts", []):
if "inlineData" in part:
data = base64.b64decode(part["inlineData"]["data"])
open(a.out, "wb").write(data)
usage = resp.get("usageMetadata", {})
print(f"{a.out} {len(data)/1e6:.2f} MB {time.time()-t0:.0f}s model={a.model} "
f"tokens_in={usage.get('promptTokenCount')} tokens_out={usage.get('candidatesTokenCount')}")
sys.exit(0)
sys.exit("no image in response: " + json.dumps(resp)[:800])
9 changes: 6 additions & 3 deletions plugins/media-agent/skills/geracao-ia/scripts/veo.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,19 @@ def main():
p.add_argument("--res", default="1080p", choices=["720p", "1080p"])
p.add_argument("--aspect", default="16:9", choices=["16:9", "9:16"])
p.add_argument("--seconds", type=int, default=8)
p.add_argument("--negative", default="texto, legenda, marca d'agua, pessoas")
p.add_argument("--negative", default="texto, legenda, marca d'agua, pessoas",
help='"" omite o campo; o veo-3.1-lite nao o aceita')
a = p.parse_args()
key = os.environ.get("GEMINI_API_KEY") or sys.exit("GEMINI_API_KEY ausente (injete pelo gerenciador de segredos)")

inst = {"prompt": a.prompt}
if a.image:
mime = mimetypes.guess_type(a.image)[0] or "image/png"
inst["image"] = {"bytesBase64Encoded": base64.b64encode(open(a.image, "rb").read()).decode(), "mimeType": mime}
body = {"instances": [inst], "parameters": {"aspectRatio": a.aspect, "resolution": a.res,
"durationSeconds": a.seconds, "negativePrompt": a.negative}}
params = {"aspectRatio": a.aspect, "resolution": a.res, "durationSeconds": a.seconds}
if a.negative: # veo-3.1-lite recusa o campo (HTTP 400): passe --negative ""
params["negativePrompt"] = a.negative
body = {"instances": [inst], "parameters": params}
t0 = time.time()
op = call("POST", f"{API}/models/{a.model}:predictLongRunning", key, body)
name = op["name"]
Expand Down
Loading