primocode 10.13.0 → 10.14.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (196) hide show
  1. package/README.md +66 -12
  2. package/bin/primocode.js +148 -8
  3. package/lib/act.js +56 -3
  4. package/lib/catalogo.js +12 -1
  5. package/lib/config.js +1 -0
  6. package/lib/estudio/cota.js +157 -0
  7. package/lib/estudio/dados/projetos/clube-ad5.json +105 -0
  8. package/lib/estudio/dados/projetos/clube-ae-sem-memoria.json +107 -0
  9. package/lib/estudio/dados/projetos/clube-agent.json +120 -0
  10. package/lib/estudio/dados/projetos/clube-antes-e-agora.json +86 -0
  11. package/lib/estudio/dados/projetos/clube-apresentando-clube.json +110 -0
  12. package/lib/estudio/dados/projetos/clube-bluevisa.json +106 -0
  13. package/lib/estudio/dados/projetos/clube-caught-in-the-fire.json +127 -0
  14. package/lib/estudio/dados/projetos/clube-chat-cliente-satisfeito.json +98 -0
  15. package/lib/estudio/dados/projetos/clube-chatgpt-anuncios.json +120 -0
  16. package/lib/estudio/dados/projetos/clube-claude-dj.json +140 -0
  17. package/lib/estudio/dados/projetos/clube-claude-mcp.json +142 -0
  18. package/lib/estudio/dados/projetos/clube-cleanui.json +106 -0
  19. package/lib/estudio/dados/projetos/clube-clinica-take-unico.json +128 -0
  20. package/lib/estudio/dados/projetos/clube-clinica.json +104 -0
  21. package/lib/estudio/dados/projetos/clube-clube-opener.json +125 -0
  22. package/lib/estudio/dados/projetos/clube-clube.json +108 -0
  23. package/lib/estudio/dados/projetos/clube-codigo.json +101 -0
  24. package/lib/estudio/dados/projetos/clube-distilbook.json +104 -0
  25. package/lib/estudio/dados/projetos/clube-drive-upload.json +115 -0
  26. package/lib/estudio/dados/projetos/clube-dub.json +119 -0
  27. package/lib/estudio/dados/projetos/clube-enxovaly.json +105 -0
  28. package/lib/estudio/dados/projetos/clube-fecha7.json +118 -0
  29. package/lib/estudio/dados/projetos/clube-filatov.json +128 -0
  30. package/lib/estudio/dados/projetos/clube-frame.json +108 -0
  31. package/lib/estudio/dados/projetos/clube-glass.json +118 -0
  32. package/lib/estudio/dados/projetos/clube-letra-em-apps.json +84 -0
  33. package/lib/estudio/dados/projetos/clube-letra-globo-3d.json +111 -0
  34. package/lib/estudio/dados/projetos/clube-letra-nos-apps.json +146 -0
  35. package/lib/estudio/dados/projetos/clube-letra-seis-mundos.json +100 -0
  36. package/lib/estudio/dados/projetos/clube-lula-3.json +146 -0
  37. package/lib/estudio/dados/projetos/clube-morph.json +119 -0
  38. package/lib/estudio/dados/projetos/clube-motion-designer.json +115 -0
  39. package/lib/estudio/dados/projetos/clube-motion-pacote.json +112 -0
  40. package/lib/estudio/dados/projetos/clube-movimento.json +103 -0
  41. package/lib/estudio/dados/projetos/clube-not-like-us.json +117 -0
  42. package/lib/estudio/dados/projetos/clube-pinterest-inspiracao.json +86 -0
  43. package/lib/estudio/dados/projetos/clube-pixel-e-tinta.json +108 -0
  44. package/lib/estudio/dados/projetos/clube-ponte.json +118 -0
  45. package/lib/estudio/dados/projetos/clube-processo-edit-tonal.json +126 -0
  46. package/lib/estudio/dados/projetos/clube-processo-motion-100-ia.json +109 -0
  47. package/lib/estudio/dados/projetos/clube-promo.json +114 -0
  48. package/lib/estudio/dados/projetos/clube-proposito.json +115 -0
  49. package/lib/estudio/dados/projetos/clube-rankhog.json +128 -0
  50. package/lib/estudio/dados/projetos/clube-recursao.json +116 -0
  51. package/lib/estudio/dados/projetos/clube-revelar.json +118 -0
  52. package/lib/estudio/dados/projetos/clube-robo.json +125 -0
  53. package/lib/estudio/dados/projetos/clube-shelby-ep2.json +144 -0
  54. package/lib/estudio/dados/projetos/clube-shelby.json +97 -0
  55. package/lib/estudio/dados/projetos/clube-solar-eclipse.json +96 -0
  56. package/lib/estudio/dados/projetos/clube-soldier.json +128 -0
  57. package/lib/estudio/dados/projetos/clube-som-de-cena.json +108 -0
  58. package/lib/estudio/dados/projetos/clube-spotify-candidatura.json +112 -0
  59. package/lib/estudio/dados/projetos/clube-tda-app.json +118 -0
  60. package/lib/estudio/dados/projetos/clube-tda-ia.json +75 -0
  61. package/lib/estudio/dados/projetos/clube-telegram-chat.json +118 -0
  62. package/lib/estudio/dados/projetos/clube-telegram-ilha.json +106 -0
  63. package/lib/estudio/dados/projetos/clube-tennessee-nike.json +132 -0
  64. package/lib/estudio/dados/projetos/clube-tudo-se-move.json +112 -0
  65. package/lib/estudio/dados/projetos/clube-uiad.json +107 -0
  66. package/lib/estudio/dados/projetos/clube-viralgrow.json +108 -0
  67. package/lib/estudio/dados/projetos/clube-whose-side.json +140 -0
  68. package/lib/estudio/dados/projetos/clube-youtube-inscrever.json +108 -0
  69. package/lib/estudio/dados/projetos/elementos-do-site-em-movimento.json +49 -0
  70. package/lib/estudio/dados/projetos/explainer-narrado.json +51 -0
  71. package/lib/estudio/dados/projetos/lancamento-de-produto.json +51 -0
  72. package/lib/estudio/dados/projetos/motion-so-com-sons.json +50 -0
  73. package/lib/estudio/dados/projetos/titulo-em-after-effects.json +51 -0
  74. package/lib/estudio/dados/projetos/tour-de-saas.json +50 -0
  75. package/lib/estudio/dados/projetos-ae/antes-e-agora/ae/cenas.jsx +733 -0
  76. package/lib/estudio/dados/projetos-ae/antes-e-agora/assets/fazer_assets.py +88 -0
  77. package/lib/estudio/dados/projetos-ae/letra-em-apps/ae/cenas.jsx +402 -0
  78. package/lib/estudio/dados/projetos-ae/letra-em-apps/assets/preparar_assets.py +17 -0
  79. package/lib/estudio/dados/projetos-ae/letra-em-apps/assets/svg_para_ae.py +25 -0
  80. package/lib/estudio/dados/projetos-ae/letra-nos-apps/BRIEF-AGENTES.md +31 -0
  81. package/lib/estudio/dados/projetos-ae/letra-nos-apps/README.md +53 -0
  82. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ROTEIRO-CENAS.md +69 -0
  83. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/A-agenda.jsx +148 -0
  84. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/B-docs.jsx +174 -0
  85. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/C-alttab.jsx +113 -0
  86. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/D-spotify.jsx +118 -0
  87. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/E-notas.jsx +146 -0
  88. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/F-jornal1.jsx +144 -0
  89. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/G-google.jsx +153 -0
  90. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/H-mapa.jsx +176 -0
  91. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/I-imagens.jsx +132 -0
  92. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/J-jornal2.jsx +191 -0
  93. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/K-maps.jsx +184 -0
  94. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/camera-movimentos.jsx +48 -0
  95. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/camera.jsx +57 -0
  96. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/card-spotify.jsx +98 -0
  97. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/digitar.jsx +38 -0
  98. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/jornal-comum.jsx +196 -0
  99. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/letra.jsx +23 -0
  100. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/letra_automatica.py +83 -0
  101. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/montagem.jsx +74 -0
  102. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/w-agenda/render_html.py +13 -0
  103. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/w-agenda/util.jsx +162 -0
  104. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/w-midia-comum.jsx +114 -0
  105. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/wg-util.jsx +360 -0
  106. package/lib/estudio/dados/projetos-ae/letra-nos-apps/assets/F-jornal1/render_html.py +13 -0
  107. package/lib/estudio/dados/projetos-ae/letra-nos-apps/assets/render_w-midia.py +26 -0
  108. package/lib/estudio/dados/projetos-ae/letra-nos-apps/assets/w-midia-ferramentas/inclin.py +30 -0
  109. package/lib/estudio/dados/projetos-ae/letra-nos-apps/assets/w-midia-ferramentas/medir_d.py +36 -0
  110. package/lib/estudio/dados/projetos-ae/letra-nos-apps/assets/w-midia-ferramentas/medir_e.py +27 -0
  111. package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/ajustar_movimentos.py +61 -0
  112. package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/ancorar.py +74 -0
  113. package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/assar.py +47 -0
  114. package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/camera3d_jornal.py +172 -0
  115. package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/comparar.py +32 -0
  116. package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/dados_ae_jornal.py +46 -0
  117. package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/folha_contato.py +28 -0
  118. package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/homografia_jornal.py +74 -0
  119. package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/integrar.py +44 -0
  120. package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/medir_quadro_a_quadro.py +32 -0
  121. package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/mesa_2d.py +64 -0
  122. package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/plot_cenas.py +11 -0
  123. package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/retificar_quadros.py +38 -0
  124. package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/w-agenda/assar_ab.py +52 -0
  125. package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/w-agenda/medir_ab.py +59 -0
  126. package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/wg_ajustar_h.py +111 -0
  127. package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/wg_corrige_cam.py +17 -0
  128. package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/wg_ecc_cadeia.py +80 -0
  129. package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/wg_finalizar_cam.py +65 -0
  130. package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/wg_mosaico.py +61 -0
  131. package/lib/estudio/dados/projetos-ae/letra-nos-apps/guia-do-metodo/tutorial-lyric-edit-interfaces/01-gramatica-e-roteiro.md +55 -0
  132. package/lib/estudio/dados/projetos-ae/letra-nos-apps/guia-do-metodo/tutorial-lyric-edit-interfaces/02-camera.md +66 -0
  133. package/lib/estudio/dados/projetos-ae/letra-nos-apps/guia-do-metodo/tutorial-lyric-edit-interfaces/03-montagem-no-ae.md +50 -0
  134. package/lib/estudio/dados/projetos-ae/letra-nos-apps/guia-do-metodo/tutorial-lyric-edit-interfaces/04-render-e-conferencia.md +43 -0
  135. package/lib/estudio/dados/projetos-ae/letra-nos-apps/guia-do-metodo/tutorial-lyric-edit-interfaces/05-abertura-celular-mergulho.md +43 -0
  136. package/lib/estudio/dados/projetos-ae/letra-nos-apps/guia-do-metodo/tutorial-lyric-edit-interfaces.md +42 -0
  137. package/lib/estudio/dados/projetos-ae/letra-nos-apps/transicao/atraso_imagem.py +31 -0
  138. package/lib/estudio/dados/projetos-ae/letra-nos-apps/transicao/fundo.py +56 -0
  139. package/lib/estudio/dados/projetos-ae/letra-nos-apps/transicao/juntar.py +45 -0
  140. package/lib/estudio/dados/projetos-ae/letra-nos-apps/transicao/montar.py +177 -0
  141. package/lib/estudio/dados/projetos-ae/letra-nos-apps/transicao/rastreio.py +74 -0
  142. package/lib/estudio/dados/projetos-ae/letra-nos-apps/transicao/sincronia.py +18 -0
  143. package/lib/estudio/dados/projetos-ae/pinterest-inspiracao/ae/cenas.jsx +530 -0
  144. package/lib/estudio/dados/projetos-ae/pinterest-inspiracao/assets/fazer_assets.py +107 -0
  145. package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/assets/pessoas/figurinha.py +46 -0
  146. package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/comp-a/BRIEF-A.md +83 -0
  147. package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/comp-a/audio.py +179 -0
  148. package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/comp-a/confere.py +20 -0
  149. package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/comp-a/dados/tempo-parte1.js +1 -0
  150. package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/comp-a/dados/tempo-parte2.js +1 -0
  151. package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/comp-a/gerar_emojis.py +11 -0
  152. package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/comp-a/img/filmes/_indice.js +122 -0
  153. package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/comp-a/js/base.js +380 -0
  154. package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/comp-a/js/main.js +86 -0
  155. package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/comp-a/js/p1a.js +169 -0
  156. package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/comp-a/js/p1b.js +455 -0
  157. package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/comp-a/js/p1c.js +389 -0
  158. package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/comp-a/js/p2.js +379 -0
  159. package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/comp-a/video.py +62 -0
  160. package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/take-unico/README.md +65 -0
  161. package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/take-unico/oraculo_take.py +137 -0
  162. package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/take-unico/render_take.py +388 -0
  163. package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/take-unico/take.js +235 -0
  164. package/lib/estudio/editor.js +290 -0
  165. package/lib/estudio/effectcraft.js +152 -0
  166. package/lib/estudio/exportar.js +181 -0
  167. package/lib/estudio/index.js +97 -2
  168. package/lib/estudio/kit-ae/base.jsx +1146 -0
  169. package/lib/estudio/kit-ae/cenas.jsx +395 -0
  170. package/lib/estudio/kit-ae/primitivas.jsx +911 -0
  171. package/lib/estudio/maquina-diretor.js +393 -0
  172. package/lib/estudio/maquina-elementos.js +134 -0
  173. package/lib/estudio/maquina-layout.js +198 -0
  174. package/lib/estudio/maquina-som.js +478 -0
  175. package/lib/estudio/maquina-trilha.js +160 -0
  176. package/lib/estudio/maquina-voz.js +429 -0
  177. package/lib/estudio/maquina.js +516 -0
  178. package/lib/estudio/projetos.js +72 -0
  179. package/lib/headless.js +71 -0
  180. package/lib/hooks.js +72 -0
  181. package/lib/limites.js +100 -0
  182. package/lib/local/cerebro.json +398 -12
  183. package/lib/local/entrada.js +46 -2
  184. package/lib/marca-kit.js +312 -0
  185. package/lib/mcp.js +286 -6
  186. package/lib/perigo.js +31 -3
  187. package/lib/planos.js +49 -19
  188. package/lib/preparar.js +111 -0
  189. package/lib/retomar.js +55 -0
  190. package/lib/sandbox.js +197 -0
  191. package/lib/skills.js +3 -1
  192. package/lib/tools.js +13 -4
  193. package/lib/ui.js +1 -1
  194. package/package.json +4 -2
  195. package/skills/after-effects/SKILL.md +52 -0
  196. package/skills/maquina-de-motion/SKILL.md +84 -0
@@ -0,0 +1,88 @@
1
+ """Assets da recriação 16 ("how it started vs how its going", @illdari), feitos por código (sem geração paga):
2
+
3
+ - papel-de-parede.png: flor azul translúcida estilo "Bloom" (papel de parede de desktop), 2160×2160, desenhada por
4
+ pétalas em degradê + brilho; sem marca.
5
+ - galeria-1..6.png: fotos de nicho da biblioteca (fotos-nichos) recortadas em retrato para a galeria escura atrás dos
6
+ mockups 3D.
7
+ - capa-album.png: capa quadrada para o item da lista no trecho estilo Spotify (foto de nicho da biblioteca).
8
+ - Ícones de app: copiados de 06-telegram-chat/assets/icones (glifos oficiais do Simple Icons).
9
+ - Mãos: copiadas de 01-pinterest/assets.
10
+ Uso: python fazer_assets.py
11
+ """
12
+ import math
13
+ import shutil
14
+ from pathlib import Path
15
+
16
+ import numpy as np
17
+ from PIL import Image, ImageDraw, ImageFilter
18
+
19
+ AQUI = Path(__file__).resolve().parent
20
+ RAIZ = AQUI.parents[1]
21
+ BIB = Path("<BIBLIOTECA>/public/biblioteca")
22
+
23
+
24
+ def bloom(n=2160):
25
+ yy, xx = np.mgrid[0:n, 0:n] / n
26
+ base = np.zeros((n, n, 3))
27
+ top = np.array([0.80, 0.86, 0.95]); bot = np.array([0.20, 0.38, 0.78])
28
+ t = np.clip(yy * 0.9 + xx * 0.2, 0, 1)[..., None]
29
+ base = top * (1 - t) + bot * t
30
+ img = Image.fromarray((base * 255).astype(np.uint8)).convert("RGBA")
31
+ camada = Image.new("RGBA", (n, n), (0, 0, 0, 0))
32
+ d = ImageDraw.Draw(camada)
33
+ cx, cy = n * 0.62, n * 0.95
34
+ for k in range(9): # pétalas: elipses compridas giradas em leque
35
+ ang = math.radians(-170 + k * 20)
36
+ L = n * (0.55 + 0.08 * math.sin(k))
37
+ px, py = cx + math.cos(ang) * L * 0.45, cy + math.sin(ang) * L * 0.45
38
+ petala = Image.new("RGBA", (n, n), (0, 0, 0, 0))
39
+ dp = ImageDraw.Draw(petala)
40
+ w, h = L, L * 0.28
41
+ dp.ellipse([px - w / 2, py - h / 2, px + w / 2, py + h / 2], fill=(30 + k * 6, 90 + k * 10, 230, 150))
42
+ petala = petala.rotate(-math.degrees(ang), center=(px, py), resample=Image.BICUBIC)
43
+ camada = Image.alpha_composite(camada, petala)
44
+ camada = camada.filter(ImageFilter.GaussianBlur(n * 0.006))
45
+ brilho = Image.new("RGBA", (n, n), (0, 0, 0, 0))
46
+ db = ImageDraw.Draw(brilho)
47
+ for k in range(9):
48
+ ang = math.radians(-170 + k * 20)
49
+ L = n * 0.5
50
+ db.line([cx, cy, cx + math.cos(ang) * L, cy + math.sin(ang) * L], fill=(200, 230, 255, 120), width=int(n * 0.004))
51
+ brilho = brilho.filter(ImageFilter.GaussianBlur(n * 0.004))
52
+ img = Image.alpha_composite(img, camada)
53
+ img = Image.alpha_composite(img, brilho)
54
+ img.convert("RGB").save(AQUI / "papel-de-parede.png")
55
+
56
+
57
+ def galeria():
58
+ fotos = ["nicho-viagem", "nicho-concessionaria", "nicho-estetica", "nicho-academia", "nicho-roupa", "nicho-salao"]
59
+ for i, f in enumerate(fotos):
60
+ p = Image.open(BIB / "fotos-nichos" / f"{f}.png").convert("RGB")
61
+ w, h = p.size
62
+ nw = int(h * 0.62)
63
+ p = p.crop(((w - nw) // 2, 0, (w + nw) // 2, h)).resize((620, 1000), Image.LANCZOS)
64
+ p.save(AQUI / f"galeria-{i + 1}.png")
65
+ c = Image.open(BIB / "fotos-nichos" / "nicho-lancamento.png").convert("RGB")
66
+ s = min(c.size); c = c.crop(((c.width - s) // 2, (c.height - s) // 2, (c.width + s) // 2, (c.height + s) // 2)).resize((400, 400))
67
+ c.save(AQUI / "capa-album.png")
68
+
69
+
70
+ def copias():
71
+ (AQUI / "icones").mkdir(exist_ok=True)
72
+ for n in ["chatgpt", "gmail", "youtube", "pinterest", "figma", "instagram", "spotify", "adobe-ae", "adobe-pr", "adobe-ai", "adobe-xd", "adobe-an"]:
73
+ shutil.copy(RAIZ / "06-telegram-chat" / "assets" / "icones" / f"{n}.png", AQUI / "icones" / f"{n}.png")
74
+ for n in ["mao-aponta.png", "mao-agarra.png"]:
75
+ shutil.copy(RAIZ / "01-pinterest" / "assets" / n, AQUI / n)
76
+ shutil.copy(RAIZ / "06-telegram-chat" / "assets" / "avatar-pessoa.png", AQUI / "avatar-pessoa.png") # pessoa gerada (biblioteca), sem foto real
77
+
78
+
79
+ def spotify():
80
+ """Logo do Spotify: glifo oficial (Simple Icons) em verde #1ED760, as barras vazadas."""
81
+ import io, resvg_py
82
+ svg = (RAIZ / "06-telegram-chat" / "assets" / "svg" / "spotify.svg").read_text(encoding="utf-8").replace("<svg ", "<svg fill=\"#1ED760\" ", 1)
83
+ Image.open(io.BytesIO(bytes(resvg_py.svg_to_bytes(svg_string=svg, width=512, height=512)))).convert("RGBA").save(AQUI / "spotify-logo.png")
84
+
85
+
86
+ if __name__ == "__main__":
87
+ bloom(); galeria(); copias(); spotify()
88
+ print("ok")
@@ -0,0 +1,402 @@
1
+ // =====================================================================
2
+ // Recriação da técnica do edit 40 do illdari ("Kanye", 245 mil plays)
3
+ // Cada frase num "app" diferente: capa de álbum nas nuvens -> post curvo
4
+ // (cilindro com câmera) com whip pan e destaque -> busca do Google que
5
+ // explode numa roda de fotos -> pílula de chamada; trocas no tempo forte
6
+ // (~89 BPM) e corte seco no fim.
7
+ // Gerador ES3 para o After Effects (rodar com eval num envio só, comp fechada).
8
+ // Exercício de estilo: texto da tela é exemplo nosso, trocável (bloco T abaixo).
9
+ // =====================================================================
10
+
11
+ // ---------------- TEXTOS TROCÁVEIS ----------------
12
+ var T = {
13
+ // textos 100% nossos (revisão 17/10): nada de letra de música nem verso adaptado
14
+ titulo1: "Bom dia", // nuvens, linha 1 (2 palavras)
15
+ titulo2: "est\u00fadio", // nuvens, linha 2 (1 palavra)
16
+ palavra2: "Caf\u00e9", // céu roxo (1 palavra)
17
+ perfil: "@seu.perfil", // autor do post
18
+ post: "Hoje a timeline sai do papel bem cedo", // 8 palavras
19
+ destaque: "cedo", // última palavra, ganha a caixa roxa
20
+ busca: "como deixar anima\u00e7\u00e3o mais lisa no af", // digitada no Google (corta no meio)
21
+ chamada: "Cliente aprovou a vers\u00e3o", // pílula, 4 palavras
22
+ tChamada: [8.35, 8.65, 8.88, 9.10] // instante de cada palavra
23
+ };
24
+
25
+ // ---------------- CONSTANTES ----------------
26
+ var FPS = 30, W = 1080, H = 1080, DUR = 312 / 30; // 10,4 s como o original
27
+ var PASTA = ((typeof RECR_BASE !== "undefined") ? new Folder(RECR_BASE) : File($.fileName).parent.parent).fsName.replace(/\\/g, "/") + "/"; // pasta do projeto (o script fica em ae/)
28
+ var ASSETS = PASTA + "assets/";
29
+ var F = 1 / FPS;
30
+ var COR = {
31
+ creme: [0xFA/255, 0xE6/255, 0xD7/255], selo: [0x69/255, 0xEF/255, 0xF7/255], destaque: [0x84/255, 0x23/255, 0xA6/255],
32
+ laranja: [0xC4/255, 0x6A/255, 0x1E/255], telefone: [0x68/255, 0xFF/255, 0x0C/255]
33
+ };
34
+ var FONTE_TIT = "Caprasimo-Regular", FONTE_ICO = "SegoeFluentIcons";
35
+ var P = app.project;
36
+
37
+ // ---------------- AJUDANTES ----------------
38
+ function nEases(prop) {
39
+ var t = prop.propertyValueType;
40
+ if (t == PropertyValueType.TwoD_SPATIAL || t == PropertyValueType.ThreeD_SPATIAL) return 1;
41
+ if (t == PropertyValueType.TwoD) return 2;
42
+ if (t == PropertyValueType.ThreeD) return 3;
43
+ return 1;
44
+ }
45
+ // keys: [[t, v, inflIn, inflOut], ...]; infl 0 = linear naquele lado; "h" = segura
46
+ function Q(t) { return Math.round(t * FPS) / FPS; } // encaixa na grade de quadros
47
+ function K(prop, keys) {
48
+ for (var i = 0; i < keys.length; i++) keys[i][0] = Q(keys[i][0]);
49
+ var ehCor = prop.propertyValueType == PropertyValueType.COLOR;
50
+ for (var i = 0; i < keys.length; i++) {
51
+ var v = keys[i][1];
52
+ if (ehCor && v.length == 3) v = [v[0], v[1], v[2], 1];
53
+ try { prop.setValueAtTime(keys[i][0], v); } catch (e) { throw new Error('K em ' + prop.name + ' / ' + prop.parentProperty.name + ' / ' + (prop.propertyGroup(prop.propertyDepth).name) + ': ' + e.toString()); }
54
+ }
55
+ var n = nEases(prop);
56
+ for (var i = 0; i < keys.length; i++) {
57
+ var idx = prop.nearestKeyIndex(keys[i][0]);
58
+ var a = keys[i][2], b = keys[i][3];
59
+ if (a == "h" || b == "h") { prop.setInterpolationTypeAtKey(idx, KeyframeInterpolationType.HOLD, KeyframeInterpolationType.HOLD); continue; }
60
+ if (a === undefined) a = 75; if (b === undefined) b = a;
61
+ var ei = [], eo = [];
62
+ for (var k = 0; k < n; k++) { ei.push(new KeyframeEase(0, Math.max(0.1, a))); eo.push(new KeyframeEase(0, Math.max(0.1, b))); }
63
+ if (a <= 0.1 && b <= 0.1) prop.setInterpolationTypeAtKey(idx, KeyframeInterpolationType.LINEAR, KeyframeInterpolationType.LINEAR);
64
+ else prop.setTemporalEaseAtKey(idx, ei, eo);
65
+ }
66
+ if (prop.isSpatial) for (var i = 1; i <= prop.numKeys; i++) { try { prop.setSpatialAutoBezierAtKey(i, true); } catch (e) {} }
67
+ }
68
+ function tr(L) { return L.property("ADBE Transform Group"); }
69
+ function pos(L) { return tr(L).property("ADBE Position"); }
70
+ function esc(L) { return tr(L).property("ADBE Scale"); }
71
+ function opa(L) { return tr(L).property("ADBE Opacity"); }
72
+ function rot(L) { return tr(L).property("ADBE Rotate Z"); }
73
+ function fx(L, mn) { return L.property("ADBE Effect Parade").addProperty(mn); }
74
+ function item(nome) { for (var i = 1; i <= P.numItems; i++) if (P.item(i).name == nome) return P.item(i); return null; }
75
+ function importa(arq) {
76
+ var it = item(arq); if (it) return it;
77
+ it = P.importFile(new ImportOptions(File(ASSETS + arq))); it.name = arq; return it;
78
+ }
79
+ function comp(nome, dur) {
80
+ var c = item(nome); if (c) c.remove();
81
+ c = P.items.addComp(nome, W, H, 1, dur || DUR, FPS);
82
+ c.motionBlur = true; c.shutterAngle = 360; c.shutterPhase = -180; c.motionBlurSamplesPerFrame = 32; c.motionBlurAdaptiveSampleLimit = 256;
83
+ c.bgColor = [0, 0, 0];
84
+ return c;
85
+ }
86
+ function texto(c, s, fonte, tam, cor, p, just, track) {
87
+ var L = c.layers.addText(s);
88
+ var tp = L.property("ADBE Text Properties").property("ADBE Text Document");
89
+ var d = tp.value; d.resetCharStyle(); d.resetParagraphStyle();
90
+ d.font = fonte; d.fontSize = tam; d.fillColor = cor; d.applyFill = true; d.applyStroke = false; d.tracking = track || 0;
91
+ d.justification = just || ParagraphJustification.LEFT_JUSTIFY;
92
+ tp.setValue(d);
93
+ pos(L).setValue(p);
94
+ return L;
95
+ }
96
+ // forma: retângulo arredondado (centro, tamanho, raio), preenchimento e traço opcionais
97
+ function retangulo(c, nome, centro, tam, raio, cor, traco) {
98
+ var L = c.layers.addShape(); L.name = nome;
99
+ var g = L.property("ADBE Root Vectors Group").addProperty("ADBE Vector Group");
100
+ var v = g.property("ADBE Vectors Group");
101
+ var r = v.addProperty("ADBE Vector Shape - Rect");
102
+ r.property("ADBE Vector Rect Size").setValue(tam); r.property("ADBE Vector Rect Roundness").setValue(raio);
103
+ if (traco) {
104
+ var s = v.addProperty("ADBE Vector Graphic - Stroke");
105
+ s.property("ADBE Vector Stroke Color").setValue(traco[0]); s.property("ADBE Vector Stroke Width").setValue(traco[1]);
106
+ s.property("ADBE Vector Stroke Opacity").setValue(traco[2]);
107
+ }
108
+ if (cor) { var f = v.addProperty("ADBE Vector Graphic - Fill"); f.property("ADBE Vector Fill Color").setValue(cor); }
109
+ pos(L).setValue(centro);
110
+ return L;
111
+ }
112
+ function elipse(c, nome, centro, diam, cor) {
113
+ var L = c.layers.addShape(); L.name = nome;
114
+ var g = L.property("ADBE Root Vectors Group").addProperty("ADBE Vector Group");
115
+ var v = g.property("ADBE Vectors Group");
116
+ v.addProperty("ADBE Vector Shape - Ellipse").property("ADBE Vector Ellipse Size").setValue([diam, diam]);
117
+ v.addProperty("ADBE Vector Graphic - Fill").property("ADBE Vector Fill Color").setValue(cor);
118
+ pos(L).setValue(centro);
119
+ return L;
120
+ }
121
+ function cor(L) { // primeira cor de preenchimento de uma forma
122
+ return L.property("ADBE Root Vectors Group").property(1).property("ADBE Vectors Group").property("ADBE Vector Graphic - Fill").property("ADBE Vector Fill Color");
123
+ }
124
+ // máscara retângulo arredondado
125
+ function mascaraArred(L, x, y, w, h, r) {
126
+ var k = 0.5523 * r, s = new Shape();
127
+ s.vertices = [[x + r, y], [x + w - r, y], [x + w, y + r], [x + w, y + h - r], [x + w - r, y + h], [x + r, y + h], [x, y + h - r], [x, y + r]];
128
+ s.inTangents = [[-k, 0], [0, 0], [0, -k], [0, 0], [k, 0], [0, 0], [0, k], [0, 0]];
129
+ s.outTangents = [[0, 0], [k, 0], [0, 0], [0, k], [0, 0], [-k, 0], [0, 0], [0, -k]];
130
+ s.closed = true;
131
+ var m = L.property("ADBE Mask Parade").addProperty("ADBE Mask Atom");
132
+ m.property("ADBE Mask Shape").setValue(s);
133
+ return m;
134
+ }
135
+ // formas do SVG oficial (svg_para_ae.py) -> camada de forma
136
+ function formaSVG(c, nome, json, idxPath, subIdx, escala, origem, corF) {
137
+ var L = c.layers.addShape(); L.name = nome;
138
+ var g = L.property("ADBE Root Vectors Group").addProperty("ADBE Vector Group");
139
+ var v = g.property("ADBE Vectors Group");
140
+ for (var s = 0; s < subIdx.length; s++) {
141
+ var sp = json[idxPath].paths[subIdx[s]];
142
+ var sh = new Shape(), vv = [], ii = [], oo = [];
143
+ for (var k = 0; k < sp.v.length; k++) {
144
+ vv.push([(sp.v[k][0] - origem[0]) * escala, (sp.v[k][1] - origem[1]) * escala]);
145
+ ii.push([sp.i[k][0] * escala, sp.i[k][1] * escala]); oo.push([sp.o[k][0] * escala, sp.o[k][1] * escala]);
146
+ }
147
+ sh.vertices = vv; sh.inTangents = ii; sh.outTangents = oo; sh.closed = true;
148
+ v.addProperty("ADBE Vector Shape - Group").property("ADBE Vector Shape").setValue(sh);
149
+ }
150
+ v.addProperty("ADBE Vector Graphic - Fill").property("ADBE Vector Fill Color").setValue(corF);
151
+ return L;
152
+ }
153
+ function lerJSON(arq) { var f = File(arq); f.encoding = "UTF-8"; f.open("r"); var s = f.read(); f.close(); return eval("(" + s + ")"); }
154
+ function blur(L, v, solto) { var e = fx(L, "ADBE Gaussian Blur 2"); e.property("ADBE Gaussian Blur 2-0003").setValue(solto ? 0 : 1); if (v !== undefined) e.property("ADBE Gaussian Blur 2-0001").setValue(v); return e.property("ADBE Gaussian Blur 2-0001"); }
155
+ function glow(L, raio, inten, limiar) {
156
+ var e = fx(L, "ADBE Glo2"); e.property("ADBE Glo2-0002").setValue(limiar === undefined ? 40 : limiar);
157
+ e.property("ADBE Glo2-0003").setValue(raio); e.property("ADBE Glo2-0004").setValue(inten); return e;
158
+ }
159
+ // parentesco sem compensação (o .parent = do script converte a rotação do pai em Orientation do filho)
160
+ function pai(L, P) { L.setParentWithJump(P); }
161
+ function janela(L, a, b) { L.inPoint = Q(a); L.outPoint = Q(b); }
162
+ function mb(L) { L.motionBlur = true; }
163
+ // deriva lenta (nunca parado): expressão de wiggle bem lento
164
+ function vivo(prop, freq, amp) { prop.expression = "wiggle(" + freq + "," + amp + ")"; }
165
+
166
+ // =====================================================================
167
+ // 1) CAPA DE ÁLBUM (0–2,2 s): nuvens + título que cai letra a letra,
168
+ // mascote atravessa colado na câmera, céu roxo + palavra + cabeça do mascote
169
+ // =====================================================================
170
+ function letrasCaindo(L, t0, t1) { // animador por caractere: cai de cima, grande e borrado -> 100%
171
+ var an = L.property("ADBE Text Properties").property("ADBE Text Animators");
172
+ var a = an.addProperty("ADBE Text Animator"); a.name = "cai";
173
+ var pr = a.property("ADBE Text Animator Properties");
174
+ pr.addProperty("ADBE Text Position 3D").setValue([0, -90, 0]);
175
+ pr.addProperty("ADBE Text Scale 3D").setValue([180, 180, 100]);
176
+ pr.addProperty("ADBE Text Blur").setValue([22, 22]);
177
+ pr.addProperty("ADBE Text Opacity").setValue(0);
178
+ var s = a.property("ADBE Text Selectors").addProperty("ADBE Text Selector");
179
+ s.property("ADBE Text Range Advanced").property("ADBE Text Selector Smoothness").setValue(100);
180
+ K(s.property("ADBE Text Percent Start"), [[t0, 0, 0, 0], [t1, 100, 0, 30]]);
181
+ }
182
+ function cenaCapa() {
183
+ var c = comp("C_capa");
184
+ var roxo = c.layers.addSolid([0.42, 0.16, 0.54], "roxo", W, H, 1);
185
+ var nv = c.layers.add(importa("nuvens.png")); nv.name = "nuvens";
186
+ esc(nv).setValue([150, 150]); janela(nv, 0, 1.42);
187
+ K(pos(nv), [[0.0, [540, 470], 0, 0], [1.42, [560, 600], 0, 0]]);
188
+ K(opa(nv), [[0.06, 0, 0, 0], [0.20, 100, 0, 0]]);
189
+ var l1 = texto(c, T.titulo1, FONTE_TIT, 165, COR.creme, [105, 440], ParagraphJustification.LEFT_JUSTIFY);
190
+ var l2 = texto(c, T.titulo2, FONTE_TIT, 165, COR.creme, [105, 570], ParagraphJustification.LEFT_JUSTIFY);
191
+ letrasCaindo(l1, 0.18, 0.52); letrasCaindo(l2, 0.45, 0.74);
192
+ var tts = [l1, l2];
193
+ for (var i = 0; i < 2; i++) { janela(tts[i], 0, 1.42); K(pos(tts[i]), [[0.2, [80, 420 + i * 160], 0, 0], [1.42, [76, 460 + i * 160], 0, 0]]); mb(tts[i]); }
194
+ // mascote voa de baixo à direita, cresce e passa colado na câmera (wipe pelo personagem)
195
+ var m = c.layers.add(importa("mascote-recorte.png")); m.name = "mascote voando"; mb(m); janela(m, 0.76, 1.50);
196
+ K(pos(m), [[0.76, [1080, 1130], 0, 0], [1.02, [640, 600], 0, 0], [1.20, [520, 560], 0, 30], [1.42, [200, 700], 30, 0]]);
197
+ K(esc(m), [[0.76, [30, 30], 0, 0], [1.02, [62, 62], 0, 0], [1.20, [95, 95], 0, 30], [1.42, [480, 480], 30, 0]]);
198
+ K(rot(m), [[0.76, 28, 0, 0], [1.20, -6, 0, 0], [1.42, -30, 0, 0]]);
199
+ // segunda cena: céu roxo, nuvem no alto, palavra cai e a cabeça do mascote sobe
200
+ var ceu = c.layers.addSolid([0, 0, 0], "ceu roxo", W, H, 1); ceu.moveBefore(roxo);
201
+ var gr = fx(ceu, "ADBE Ramp"); gr.property("ADBE Ramp-0001").setValue([540, 0]); gr.property("ADBE Ramp-0002").setValue([0.30, 0.07, 0.40]);
202
+ gr.property("ADBE Ramp-0003").setValue([540, 1080]); gr.property("ADBE Ramp-0004").setValue([0.58, 0.16, 0.66]);
203
+ janela(ceu, 1.38, DUR);
204
+ var nv2 = c.layers.add(importa("nuvens.png")); nv2.name = "nuvem alto"; janela(nv2, 1.38, DUR); nv2.moveBefore(ceu);
205
+ esc(nv2).setValue([260, 260]); K(pos(nv2), [[1.38, [900, 760], 0, 0], [3.0, [880, 790], 0, 0]]);
206
+ var hs = fx(nv2, "ADBE Color Balance (HLS)"); hs.property(1).setValue(-25);
207
+ var leve = texto(c, T.palavra2, FONTE_TIT, 140, COR.creme, [540, 470], ParagraphJustification.CENTER_JUSTIFY);
208
+ janela(leve, 1.50, DUR); letrasCaindo(leve, 1.55, 1.82); mb(leve);
209
+ var cab = c.layers.add(importa("mascote-recorte.png")); cab.name = "cabeca mascote"; janela(cab, 1.55, DUR); mb(cab);
210
+ K(pos(cab), [[1.55, [540, 1500], 0, 80], [1.92, [540, 955], 80, 60], [3.0, [540, 945], 60, 0]]);
211
+ esc(cab).setValue([120, 120]);
212
+ vivo(pos(nv2), 0.4, 8);
213
+ return c;
214
+ }
215
+
216
+ // =====================================================================
217
+ // 2) MUNDO PLANO (post + Google + roda de fotos), depois curvado em cilindro
218
+ // Coordenadas do "mundo": cartão do post com 820 u de largura (escala do quadro 2,3 s)
219
+ // =====================================================================
220
+ var MW = 2400, MH = 2200;
221
+ function largura(c, s, fonte, tam) { var L = texto(c, s, fonte, tam, [1, 1, 1], [0, 0]); var w = L.sourceRectAtTime(0, false).width; L.remove(); return w; }
222
+ function cenaMundo(capa) {
223
+ var c = P.items.addComp("C_mundo", MW, MH, 1, DUR, FPS); c.motionBlur = true; c.shutterAngle = 360; c.shutterPhase = -180;
224
+ var preto = c.layers.addSolid([0.024, 0.024, 0.024], "fundo post", MW, MH, 1);
225
+ // cartão = capa do álbum (a própria cena 1 continua tocando dentro dele)
226
+ var card = c.layers.add(capa); card.name = "cartao capa"; esc(card).setValue([75.9, 75.9]); pos(card).setValue([1010, 1590]);
227
+ mascaraArred(card, 0, 0, 1080, 1080, 34);
228
+ var vb = fx(card, "ADBE Venetian Blinds"); vb.property("ADBE Venetian Blinds-0001").setValue(14); vb.property("ADBE Venetian Blinds-0002").setValue(90); vb.property("ADBE Venetian Blinds-0003").setValue(5);
229
+ var borda = retangulo(c, "borda cartao", [1010, 1590], [820, 820], 26, null, [[1, 1, 1], 2, 35]);
230
+ // cabeçalho do post: avatar, @, selo
231
+ var av = c.layers.add(importa("avatar.png")); av.name = "avatar"; esc(av).setValue([6.4, 6.4]); pos(av).setValue([584, 1092]);
232
+ var mk = av.property("ADBE Mask Parade").addProperty("ADBE Mask Atom"); var sh = new Shape(), r = 512, k = 0.5523 * r;
233
+ sh.vertices = [[512, 0], [1024, 512], [512, 1024], [0, 512]]; sh.inTangents = [[-k, 0], [0, -k], [k, 0], [0, k]]; sh.outTangents = [[k, 0], [0, k], [-k, 0], [0, -k]]; sh.closed = true;
234
+ mk.property("ADBE Mask Shape").setValue(sh);
235
+ var nome = texto(c, T.perfil, "SegoeUI-Bold", 30, [1, 1, 1], [628, 1103], ParagraphJustification.LEFT_JUSTIFY);
236
+ var wn = nome.sourceRectAtTime(0, false).width;
237
+ var selo = elipse(c, "selo", [628 + wn + 22, 1092], 30, COR.selo);
238
+ var chk = texto(c, "", FONTE_ICO, 18, [1, 1, 1], [628 + wn + 22, 1101], ParagraphJustification.CENTER_JUSTIFY);
239
+ var cab = [av, nome, selo, chk];
240
+ for (var i = 0; i < cab.length; i++) K(opa(cab[i]), [[2.22, 0, 0, 0], [2.32, 100, 0, 0]]);
241
+ // texto do post: letras saltam da linha de base (~1 caractere por quadro), 2,25–3,85
242
+ var xT = 600, yT = 1146;
243
+ var post = texto(c, T.post, "SegoeUI-Semibold", 31, [1, 1, 1], [xT, yT], ParagraphJustification.LEFT_JUSTIFY);
244
+ var an = post.property("ADBE Text Properties").property("ADBE Text Animators");
245
+ var a1 = an.addProperty("ADBE Text Animator"); a1.name = "pulo da base";
246
+ var pr = a1.property("ADBE Text Animator Properties");
247
+ pr.addProperty("ADBE Text Position 3D").setValue([0, 22, 0]); pr.addProperty("ADBE Text Scale 3D").setValue([60, 60, 100]); pr.addProperty("ADBE Text Opacity").setValue(0);
248
+ var s1 = a1.property("ADBE Text Selectors").addProperty("ADBE Text Selector");
249
+ s1.property("ADBE Text Range Advanced").property("ADBE Text Selector Smoothness").setValue(100);
250
+ K(s1.property("ADBE Text Percent Start"), [[2.25, 0, 0, 0], [3.85, 100, 0, 0]]);
251
+ // destaque roxo atrás da última palavra, cresce da esquerda em 3 qd
252
+ var pref = T.post.substring(0, T.post.length - T.destaque.length);
253
+ var x0 = xT + largura(c, pref, "SegoeUI-Semibold", 31), wd = largura(c, T.destaque, "SegoeUI-Semibold", 31);
254
+ var hl = retangulo(c, "destaque", [x0 - 6, yT - 11], [wd + 12, 46], 0, COR.destaque);
255
+ tr(hl).property("ADBE Anchor Point").setValue([-(wd + 12) / 2, 0]); hl.moveAfter(post);
256
+ K(esc(hl), [[4.05, [0, 100], 0, 0], [4.14, [100, 100], 0, 30]]);
257
+ // Google modo escuro: logo oficial (SVG) com o 2º "o" trocado por um quadrado laranja
258
+ var gj = lerJSON(ASSETS + "google-ae.json"), GE = 1.55, GX = 1300, GY = 520;
259
+ for (var p = 0; p < gj.length; p++) {
260
+ if (p == 1) continue;
261
+ var idx = []; for (var q = 0; q < gj[p].paths.length; q++) idx.push(q);
262
+ var hex = gj[p].fill, cc = [parseInt(hex.substr(1, 2), 16) / 255, parseInt(hex.substr(3, 2), 16) / 255, parseInt(hex.substr(5, 2), 16) / 255];
263
+ var g = formaSVG(c, "google " + p, gj, p, idx, GE, [135, 45], cc); pos(g).setValue([GX, GY]);
264
+ }
265
+ var QX = GX + (141.5 - 135) * GE, QY = GY + (47 - 45) * GE;
266
+ var quad = retangulo(c, "quadrado o", [QX, QY], [40 * GE * 0.85, 40 * GE * 0.85], 3, COR.laranja);
267
+ var fundoG = c.layers.addSolid([0.12, 0.118, 0.13], "fundo google", MW, 1000, 1); pos(fundoG).setValue([MW / 2, GY + 150]); fundoG.moveBefore(preto);
268
+ var bar = retangulo(c, "barra busca", [GX, GY + 130], [640, 58], 29, [0.204, 0.208, 0.227]);
269
+ var lupa = texto(c, "", FONTE_ICO, 24, [0.85, 0.85, 0.85], [GX - 292, GY + 140], ParagraphJustification.CENTER_JUSTIFY);
270
+ var busca = texto(c, T.busca, "SFProText-Medium", 26, [0.95, 0.95, 0.95], [GX - 262, GY + 139], ParagraphJustification.LEFT_JUSTIFY);
271
+ var an2 = busca.property("ADBE Text Properties").property("ADBE Text Animators");
272
+ var a2 = an2.addProperty("ADBE Text Animator"); var p2 = a2.property("ADBE Text Animator Properties");
273
+ p2.addProperty("ADBE Text Position 3D").setValue([0, -20, 0]); p2.addProperty("ADBE Text Opacity").setValue(0);
274
+ var s2 = a2.property("ADBE Text Selectors").addProperty("ADBE Text Selector");
275
+ s2.property("ADBE Text Range Advanced").property("ADBE Text Selector Smoothness").setValue(100);
276
+ K(s2.property("ADBE Text Percent Start"), [[5.0, 0, 0, 0], [6.35, 100, 0, 0]]);
277
+ // roda de fotos: a 1ª foto sai do quadrado, as outras nascem em volta (1 qd de stagger) e o anel gira
278
+ var cx = GX, cy = GY + 130, N = 14, RR = 205, LADO = 220;
279
+ var roda = c.layers.addNull(); roda.name = "roda"; tr(roda).property("ADBE Anchor Point").setValue([50, 50]); pos(roda).setValue([cx, cy]);
280
+ K(rot(roda), [[6.38, -40, 0, 30], [7.45, 60, 30, 0]]);
281
+ for (var i = 0; i < N; i++) {
282
+ var f = c.layers.add(importa("fotos/foto-" + (i < 9 ? "0" : "") + (i + 1) + ".png")); f.name = "foto " + (i + 1); mb(f);
283
+ var ang = i * 360 / N, rad = ang * Math.PI / 180;
284
+ pai(f, roda);
285
+ var px = 50 + RR * Math.cos(rad), py = 50 + RR * Math.sin(rad);
286
+ var t0 = (i == 0) ? 6.12 : 6.32 + i * F;
287
+ janela(f, t0, DUR);
288
+ var sc = LADO / 246 * 100;
289
+ if (i == 0) K(pos(f), [[6.12, [50 + (QX - cx) * 1, 50 + (QY - cy)], 0, 60], [6.40, [px, py], 60, 60]]);
290
+ else K(pos(f), [[t0, [50 + RR * 0.6 * Math.cos(rad), 50 + RR * 0.6 * Math.sin(rad)], 0, 70], [t0 + 7 * F, [px, py], 70, 60]]);
291
+ K(esc(f), [[t0, [0, 0], 0, 70], [t0 + 7 * F, [sc * 1.06, sc * 1.06], 70, 70], [t0 + 10 * F, [sc, sc], 70, 70]]);
292
+ rot(f).setValue(ang + 90 + ((i % 3) - 1) * 9);
293
+ }
294
+ roda.moveToBeginning();
295
+ for (var i = 1; i <= c.numLayers; i++) { var L = c.layer(i); if (L.name.indexOf("foto ") == 0) L.moveToBeginning(); }
296
+ return c;
297
+ }
298
+
299
+ // cilindro de eixo vertical feito de tiras do mundo (perspectiva real com a câmera)
300
+ var RC = 800, UC = 1010, VC = 1100;
301
+ function cenaPost(mundo) {
302
+ var c = comp("C_post");
303
+ // raio do cilindro animado: curvo no post (800) e quase plano no Google (5000), como no original
304
+ var ctrl = c.layers.addNull(); ctrl.name = "CTRL"; ctrl.enabled = false;
305
+ var sl = fx(ctrl, "ADBE Slider Control"); sl.name = "raio";
306
+ K(sl.property(1), [[2.2, RC, 0, 0], [4.80, RC, 0, 60], [5.30, 5000, 60, 0]]);
307
+ var u0 = 300, u1 = 2300, larg = 25;
308
+ for (var u = u0; u < u1; u += larg) {
309
+ var L = c.layers.add(mundo); L.name = "tira " + u; L.threeDLayer = true;
310
+ var m = L.property("ADBE Mask Parade").addProperty("ADBE Mask Atom"); var sh = new Shape();
311
+ sh.vertices = [[u - 1, 0], [u + larg + 1, 0], [u + larg + 1, MH], [u - 1, MH]]; sh.closed = true; m.property("ADBE Mask Shape").setValue(sh);
312
+ var uc = u + larg / 2;
313
+ var R_ = "thisComp.layer('CTRL').effect('raio')(1)";
314
+ tr(L).property("ADBE Anchor Point").expression = "[" + uc + ", " + VC + ", " + R_ + "]";
315
+ pos(L).expression = "[540, 540, " + R_ + "]";
316
+ tr(L).property("ADBE Rotate Y").expression = "-(" + (uc - UC) + " / " + R_ + ") * 180 / Math.PI";
317
+ }
318
+ // câmera: [t, cx, cy, distância, inclinação (câmera abaixo olhando para cima), rolagem]
319
+ var cam = c.layers.addCamera("camera", [540, 540]);
320
+ cam.property("ADBE Camera Options Group").property("ADBE Camera Zoom").setValue(1000);
321
+ var KC = [
322
+ [2.20, 535, 990, 1000, 0, 0, 60], [2.30, 535, 981, 1000, 0, 0, 60], [2.60, 520, 940, 900, 20, 0, 70],
323
+ [2.95, 275, 584, 225, 75, 0, 60], [3.30, 335, 580, 222, 75, 0, 40], [3.55, 520, 574, 215, 70, 0, 50],
324
+ [4.05, 632, 568, 222, 65, 0, 70], [4.75, 640, 568, 202, 65, 0, 30], [5.00, 830, 120, 420, 60, -2, 60],
325
+ [5.20, 830, 79, 500, 40, -3, 70], [6.00, 832, 86, 515, 30, -3, 60], [6.30, 830, 80, 600, 20, -2, 70], [7.45, 830, 78, 650, 20, -1, 0]
326
+ ];
327
+ var kp = [], ka = [], kr = [];
328
+ for (var i = 0; i < KC.length; i++) {
329
+ var z = KC[i]; var poiX = z[1], poiY = z[2]; // já em coordenadas da comp (mundo u -> 540 + u - UC; v -> 540 + v - VC)
330
+ ka.push([z[0], [poiX, poiY, 0], z[6], z[6]]);
331
+ kp.push([z[0], [poiX, poiY + z[4], -z[3]], z[6], z[6]]);
332
+ kr.push([z[0], z[5], z[6], z[6]]);
333
+ }
334
+ K(cam.property("ADBE Transform Group").property("ADBE Anchor Point"), ka);
335
+ K(cam.property("ADBE Transform Group").property("ADBE Position"), kp);
336
+ K(cam.property("ADBE Transform Group").property("ADBE Rotate Z"), kr);
337
+ return c;
338
+ }
339
+
340
+ // =====================================================================
341
+ // 3) CHAMADA (pílula estilo ilha dinâmica): palavra por palavra, câmera aproxima
342
+ // =====================================================================
343
+ function cenaChamada() {
344
+ var c = comp("C_chamada");
345
+ c.layers.addSolid([0.82, 0.82, 0.82], "fundo cinza", W, H, 1);
346
+ var pil = retangulo(c, "pilula", [540, 560], [100, 100], 70, [0.933, 0.933, 0.933]);
347
+ var tx = texto(c, T.chamada, "SFProText-Semibold", 42, [0.07, 0.07, 0.07], [0, 575], ParagraphJustification.LEFT_JUSTIFY);
348
+ var tempos = T.tChamada, ws = T.chamada.split(" ");
349
+ var ex = "var ts=[" + tempos.join(",") + "], w=['" + ws.join("','") + "']; var n=0; for (var i=0;i<ts.length;i++) if (time>=ts[i]) n=i+1; n==0 ? ' ' : w.slice(0,n).join(' ');";
350
+ tx.property("ADBE Text Properties").property("ADBE Text Document").expression = ex;
351
+ janela(tx, 8.2, DUR);
352
+ // a palavra nova cai de cima (3 qd)
353
+ var an = tx.property("ADBE Text Properties").property("ADBE Text Animators");
354
+ var a = an.addProperty("ADBE Text Animator"); var pr = a.property("ADBE Text Animator Properties");
355
+ pr.addProperty("ADBE Text Position 3D").setValue([0, -18, 0]); pr.addProperty("ADBE Text Opacity").setValue(0);
356
+ var sel = a.property("ADBE Text Selectors").addProperty("ADBE Text Expressible Selector");
357
+ sel.property("ADBE Text Range Type2").setValue(3); // palavras
358
+ sel.property("ADBE Text Expressible Amount").expression = "var ts=[" + tempos.join(",") + "]; var n=0; for (var i=0;i<ts.length;i++) if (time>=ts[i]) n=i+1; var v=0; if (textIndex==n){ v=Math.max(0,1-(time-ts[n-1])/0.1)*100; } [v,v,v];";
359
+ // largura da pílula acompanha o texto; o ícone fica na ponta esquerda
360
+ var sz = pil.property("ADBE Root Vectors Group").property(1).property("ADBE Vectors Group").property("ADBE Vector Shape - Rect").property("ADBE Vector Rect Size");
361
+ K(sz, [[7.80, [0, 0], 0, 70], [7.95, [100, 100], 70, 70], [8.00, [100, 100], 70, 0], [8.10, [470, 136], 0, 70]]);
362
+ sz.expression = "if (time < 8.12) value; else { var r=thisComp.layer('" + tx.name + "').sourceRectAtTime(time,false); var alvo=Math.max(470, r.width+200); [alvo, 136]; }";
363
+ tr(tx).property("ADBE Position").expression = "var w=thisComp.layer('pilula').content(1).content(1).size[0]; [540 - w/2 + 140, 575];";
364
+ var circ = elipse(c, "circulo icone", [540, 600], 96, [1, 1, 1]);
365
+ var ic = texto(c, "", FONTE_ICO, 48, COR.telefone, [540, 618], ParagraphJustification.CENTER_JUSTIFY);
366
+ pai(ic, circ); pos(ic).setValue([0, 18]);
367
+ pos(circ).expression = "var w=thisComp.layer('pilula').content(1).content(1).size[0]; var p=[540,560]; if (time < 7.95) p = [540, 560 + 40*Math.max(0, (7.9-time)/0.45)]; else p=[540 - w/2 + 68, 560]; p;";
368
+ K(esc(circ), [[7.45, [60, 60], 0, 60], [7.80, [80, 80], 60, 60], [7.95, [100, 100], 60, 60]]);
369
+ K(opa(circ), [[7.45, 100, 0, 0]]);
370
+ var gC = cor(circ); K(gC, [[7.70, [0.82, 0.82, 0.82], 60, 60], [7.90, [1, 1, 1], 60, 60]]);
371
+ mb(pil); blur(pil, 0, true);
372
+ K(pil.property("ADBE Effect Parade").property(1).property(1), [[8.0, 0, 0, 0], [8.05, 8, 0, 0], [8.12, 0, 0, 0]]);
373
+ return c;
374
+ }
375
+
376
+ // =====================================================================
377
+ // 4) COMP FINAL
378
+ // =====================================================================
379
+ function montaFinal() {
380
+ var cC = cenaCapa();
381
+ var old = item("C_mundo"); if (old) old.remove();
382
+ var cM = cenaMundo(cC), cP = cenaPost(cM), cH = cenaChamada();
383
+ var c = comp("RECRIACAO");
384
+ c.layers.addSolid([0, 0, 0], "preto", W, H, 1);
385
+ var lC = c.layers.add(cC); lC.name = "capa"; janela(lC, 0, 2.22); mb(lC);
386
+ K(esc(lC), [[2.00, [100, 100], 0, 80], [2.20, [77, 77], 80, 60]]);
387
+ var oc = fx(lC, "ADBE Optics Compensation"); K(oc.property(1), [[2.00, 0, 0, 0], [2.20, 30, 0, 0]]);
388
+ var lP = c.layers.add(cP); lP.name = "post google roda"; janela(lP, 2.20, 7.45);
389
+ // íris branca cresce do furo da roda
390
+ var ir = elipse(c, "iris branca", [540, 560], 100, [0.96, 0.96, 0.96]); blur(ir, 110, true); janela(ir, 7.18, 7.46);
391
+ var irS = ir.property("ADBE Root Vectors Group").property(1).property("ADBE Vectors Group").property("ADBE Vector Shape - Ellipse").property("ADBE Vector Ellipse Size");
392
+ K(irS, [[7.18, [0, 0], 0, 0], [7.40, [720, 720], 40, 0], [7.45, [2400, 2400], 0, 0]]);
393
+ var lH = c.layers.add(cH); lH.name = "chamada"; janela(lH, 7.45, 10.20); mb(lH);
394
+ K(esc(lH), [[8.20, [100, 100], 40, 40], [10.10, [133, 133], 40, 40]]);
395
+ var db = fx(lH, "ADBE Motion Blur"); db.property("ADBE Motion Blur-0001").setValue(90);
396
+ K(db.property("ADBE Motion Blur-0002"), [[10.12, 0, 0, 0], [10.19, 220, 0, 0]]);
397
+ c.workAreaStart = 0; c.workAreaDuration = DUR;
398
+ return c;
399
+ }
400
+
401
+ var compFinal = montaFinal();
402
+ "ok " + compFinal.numLayers;
@@ -0,0 +1,17 @@
1
+ """Prepara os assets do edit 40: 16 fotos da grade, mascote sem o fundo verde."""
2
+ import numpy as np, os
3
+ from PIL import Image, ImageFilter
4
+ A = os.path.dirname(os.path.abspath(__file__))
5
+ g = Image.open(os.path.join(A, "grade-fotos.png")).convert("RGB"); W, H = g.size
6
+ os.makedirs(os.path.join(A, "fotos"), exist_ok=True)
7
+ for k in range(16):
8
+ x, y = (k % 4) * W // 4, (k // 4) * H // 4
9
+ g.crop((x + 3, y + 3, x + W // 4 - 3, y + H // 4 - 3)).save(os.path.join(A, "fotos", "foto-%02d.png" % (k + 1)))
10
+ m = np.asarray(Image.open(os.path.join(A, "mascote.png")).convert("RGB")).astype(float)
11
+ r, gg, b = m[..., 0], m[..., 1], m[..., 2]
12
+ verde = gg - np.maximum(r, b) # quanto "verde de chroma" o pixel tem
13
+ alfa = np.clip(1 - (verde - 40) / 50, 0, 1)
14
+ m[..., 1] = np.minimum(gg, np.maximum(r, b) + 10) # tira o vazamento verde
15
+ out = np.dstack([m, alfa * 255]).astype(np.uint8)
16
+ im = Image.fromarray(out, "RGBA"); im = im.crop(im.getbbox()); im.save(os.path.join(A, "mascote-recorte.png"))
17
+ print("ok", im.size)
@@ -0,0 +1,25 @@
1
+ """Converte os paths de um SVG oficial em formas do AE (vértices + tangentes), para animar a marca por partes.
2
+ Uso: python svg_para_ae.py entrada.svg saida.json"""
3
+ import json, sys, re
4
+ from svgpathtools import parse_path, Line, CubicBezier, QuadraticBezier
5
+ src = open(sys.argv[1], encoding="utf-8").read()
6
+ out = []
7
+ for m in re.finditer(r'<path([^>]*)/?>', src):
8
+ attrs = m.group(1)
9
+ d = re.search(r'\sd="([^"]+)"', attrs).group(1)
10
+ fm = re.search(r'fill="([^"]+)"', attrs)
11
+ p = parse_path(d)
12
+ subs = []
13
+ for sp in p.continuous_subpaths():
14
+ segs = list(sp); n = len(segs)
15
+ v, it, ot = [0]*n, [0]*n, [0]*n
16
+ for k, s in enumerate(segs):
17
+ if isinstance(s, QuadraticBezier):
18
+ s = CubicBezier(s.start, s.start + 2/3*(s.control-s.start), s.end + 2/3*(s.control-s.end), s.end)
19
+ c1, c2 = (s.start, s.end) if isinstance(s, Line) else (s.control1, s.control2)
20
+ v[k] = s.start; ot[k] = c1 - s.start; it[(k + 1) % n] = c2 - s.end
21
+ subs.append({"v": [[z.real, z.imag] for z in v], "i": [[z.real, z.imag] for z in it],
22
+ "o": [[z.real, z.imag] for z in ot], "closed": True})
23
+ out.append({"fill": fm.group(1) if fm else "#000000", "paths": subs})
24
+ json.dump(out, open(sys.argv[2], "w"), indent=0)
25
+ print(len(out), "paths", [len(o["paths"]) for o in out])
@@ -0,0 +1,31 @@
1
+ # Pedido comum aos agentes de cena (recriação On My Way, 17/10)
2
+
3
+ > Data: 2026-10-17 · Tipo: guia · Status: vigente
4
+
5
+ **Resumo:** regras que todo agente de cena segue: uma cópia própria do After Effects, um `.aep` próprio, cenas no contrato do `ROTEIRO-CENAS.md`, conferência visual com `ae_see` contra o original em vários instantes (inclusive no meio dos movimentos), nenhum render de vídeo.
6
+
7
+ ## Onde está tudo
8
+
9
+ - Projeto: `<SUA_CENTRAL>/07-videos/projetos/2026-10-17-subzmotion-recriacao/` (raiz `P`). Leia `P/ROTEIRO-CENAS.md` inteiro antes de começar.
10
+ - Original: `<SUA_CENTRAL>/05-pesquisa/referencias/2026-10-17-subzmotion/midia/05-DeMZpQ5gv-t.mp4`. Folhas de contato: `.../dados-brutos/folhas/05-*` e `.../folhas/transicoes/05-*`. Decupagem: `.../evidencias/05-on-my-way.md`.
11
+ - Quadro do original em resolução cheia (o filtro de cor é obrigatório): `ffmpeg -v error -y -ss T -i ORIGINAL -frames:v 1 -vf "setparams=colorspace=bt709:color_primaries=bt709:color_trc=bt709,scale=flags=accurate_rnd" P/ref/<id>-tT.png`.
12
+ - Câmera: `P/camera/cena-<id>.json` (chaves por quadro) e `P/camera/encaixes.json`. Ajudantes: `P/ae/camera.jsx`, `P/ae/digitar.jsx` (leia o cabeçalho de cada um).
13
+
14
+ ## After Effects (obrigatório)
15
+
16
+ 1. Ferramentas `mcp__after-effects__*` (carregue por ToolSearch: `select:mcp__after-effects__ae_do,mcp__after-effects__ae_see,mcp__after-effects__ae_render_frame,mcp__after-effects__ae_review_motion,mcp__after-effects__ae_expressions_check,mcp__after-effects__ae_docs,mcp__after-effects__ae_lessons,mcp__after-effects__ae_save_project,mcp__after-effects__ae_checkpoint,mcp__after-effects__ae_get_skill,mcp__after-effects__ae_context`).
17
+ 2. Abra a SUA cópia: `ae_do instance.start {name: "<seu nome>"}` e passe `instance: "<seu nome>"` em TODA chamada. Há outras cópias vivas; nunca mexa nelas.
18
+ 3. Crie o seu projeto (`app.newProject()` + `app.project.save(File(...))`) em `P/ae/cenas-<seu nome>.aep`. Monte cada cena por um gerador comentado em `P/ae/<id>.jsx` rodado num `eval.run` só, com a comp fechada (ES3: var, function, sem arrow/let/template). Carregue os ajudantes com `$.evalFile(P + "ae/camera.jsx")` e `$.evalFile(P + "ae/digitar.jsx")`. Efeitos por matchName; descubra parâmetros listando antes de setar. Antes de começar, `ae_lessons search` com o tema.
19
+ 4. Interfaces: faça em HTML/CSS e tire PNG com Playwright em 3× (`device_scale_factor=3`), sem os textos que serão digitados (esses são camadas de texto no AE). Guarde HTML e PNG em `P/assets/<id>/`. Fonte nova: baixe (Google Fonts) e instale em `C:\Program Files\Common Files\Adobe\Fonts` (só aparece após reiniciar a sua cópia). Sugestões: Google Sans Flex (Google), Figtree (Spotify), Roboto (Instagram/Maps), Segoe UI (Windows), serifadas do Google Fonts para os jornais.
20
+ 5. Imagens: `python -P "<SUA_CENTRAL>/08-integracoes/codex-imagem.py" "prompt em inglês" P/assets/<id>/nome.png` (~60–90 s cada; no máximo 2 ao mesmo tempo por agente). Ícones oficiais de apps citados podem vir do simpleicons/Wikimedia.
21
+ 6. Olhe sempre: `ae_see` da comp da cena com `reference: {video: ORIGINAL}` nos mesmos tempos (REF | NOSSO | DIF). Confira pelo menos: o `ref`, o começo e o fim da cena, o instante de cada troca e 3 quadros no meio de movimentos. Onde o enquadramento não bater, corrija os keyframes dos controles do CAM (a medição é ponto de partida, tem trechos contaminados) ou a posição dos elementos no mundo. `ae_review_motion` no fim; `ae_expressions_check` depois de expressões.
22
+ Pegadinhas já vistas (17/10): `eval.run` só devolve valor com `return`; caminho com "ç" em `File`/`$.evalFile` vai codificado: `File.decode("<HOME>/Documents/Cria%C3%A7%C3%A3o%20de%20conteudo/...")`; `app.newProject()` com projeto sujo abre diálogo de salvar (feche antes com `app.project.close(CloseOptions.DO_NOT_SAVE_CHANGES)`); se der DIALOG_OPEN, `instance.dismiss_dialog {id}`. Adicionar efeito/forma invalida referências antigas: crie tudo e busque de novo pelo nome.
23
+ Personagens comuns (mesmas pessoas em várias cenas) ficam em `P/assets/comum/` (avatar-lu, avatar-produtor, avatar-mari, avatar-seu, wallpaper-produtor; sendo gerados agora, chegam em ~5 min). Não gere outra versão deles.
24
+ Medidas que já sabemos do autor: borrão de movimento ≈ ¼ do deslocamento do quadro (obturador ~90°; confira contra o original e anote se precisar de outro valor); digitação começa até 0,35 s antes da palavra cantada.
25
+ 7. **Não use `ae_render_video`** (RAM: um render pesado por vez; quem renderiza é o coordenador). Quadros soltos podem.
26
+ 8. No fim: `ae_save_project`, `instance.stop {name, save: true}` e uma ficha curta em `P/revisao/<seu nome>.md` (o que fez, o que ficou diferente do original, onde mexer).
27
+
28
+ ## Escrita e direitos
29
+
30
+ - Português do Brasil simples. **Não escreva letra de música em lugar nenhum** (nem verso, nem tradução, nem paráfrase); use só os textos do roteiro. Pessoas reais e marcas de jogo viram personagens e imagens originais.
31
+ - Credencial nunca em texto. Não faça commit.
@@ -0,0 +1,53 @@
1
+ # Recriação do edit "On My Way" do @subzmotion (letra dentro de interfaces + câmera em cadeia)
2
+
3
+ > Data: 2026-10-17 · Tipo: projeto · Status: aprovado ("a recriação ficou perfeita", 17/10) · base do estilo · Fonte: https://www.instagram.com/p/DeMZpQ5gv-t/
4
+
5
+ **Resumo:** O autor pediu (17/10) a engenharia reversa do perfil @subzmotion e a recriação de pelo menos um vídeo, com a câmera entre as cenas e as animações idênticas, para depois fazer edits assim em qualquer música, 100% com IA. Escolhido o On My Way (1,87 mi plays, o mais visto, e com vídeo da timeline do AE). Pesquisa em [05-pesquisa/referencias/2026-10-17-subzmotion](../../../05-pesquisa/referencias/2026-10-17-subzmotion/README.md).
6
+
7
+ ## Critério de pronto
8
+
9
+ - Estudo completo em `05-pesquisa` (3 edits + 3 timelines + portfólio), com a receita para fazer em qualquer música.
10
+ - Recriação do On My Way inteira (33,9 s, 11 cenas-mundo) no After Effects, com a câmera medida do original, encaixes cena-dentro-de-cena e digitação no tempo da voz.
11
+ - Câmera comparada em números com o original (curvas de zoom/translação), renderizada e entregue aqui na conversa.
12
+ - Regras de recriação: **interface em inglês e a mesma letra do original** (pedido do autor, 17/10: "não é pra traduzir… tem que ser a mesma letra"); a letra entra pelos slots de `ae/letra.json`, que o autor preenche (o Claude não escreve letra de música); pessoas viram personagens originais, sem narração, não posta. Aprovada, vai para o Clube do Motion.
13
+
14
+ ## Como está organizado
15
+
16
+ - `ROTEIRO-CENAS.md`: as 11 cenas, tempos, campos de letra e encaixes.
17
+ - `camera/`: medição e câmera por cena. `integrar.py` (fluxo óptico → câmera absoluta), `ancorar.py` (corrige deriva casando com o quadro de referência por SIFT), `assar.py` (limpa e gera `cena-<id>.json` + `encaixes.json`).
18
+ - `ae/camera.jsx` (null CAM com Zoom log2 / Mira X / Mira Y / Giro), `ae/camera-movimentos.jsx` (o rig do autor: lista de movimentos → um null por movimento), `ae/digitar.jsx` (digitação com cursor), `ae/letra.jsx` + `ae/letra.json` + `ae/letra_automatica.py` (letra colada → 31 campos com tempo por palavra), `ae/cenas-w-*.aep` (uma por agente), `ae/montagem.jsx` + `ae/card-spotify.jsx` → `ae/montagem.aep` (final).
19
+ - `assets/<id>/`: interfaces (HTML → PNG 3×) e imagens geradas de cada cena. `audio/`: música do original (só referência interna, fora de qualquer pacote). `render/`, `revisao/`.
20
+
21
+ ## Checklist
22
+
23
+ - [x] Download, comentários (Apify) e portfólio (REA) do perfil
24
+ - [x] Decupagem do On My Way + timeline (evidência 05), Rockabye (03), Love Story (01)
25
+ - [x] Câmera medida, ancorada e assada por cena; encaixes calculados
26
+ - [x] Roteiro com campos e tempos de palavra; letra por slots em `ae/letra.json` (17/10)
27
+ - [x] O autor colou a letra inteira em `ae/LETRA-COMPLETA.txt`; `ae/letra_automatica.py` distribuiu nos 31 campos (todos com o nº de palavras do original)
28
+ - [x] Card do Spotify no fim creditando a música (sugestão do autor, 17/10): `ae/card-spotify.jsx`, cauda até 36,5 s com eco da última frase (`audio/eco_cauda.py` → `audio/mix-com-card.wav`)
29
+ - [x] Cenas no AE (4 agentes): A+B (`cenas-w-agenda.aep`) · C+D+E (`cenas-w-midia.aep`) · F+J (`cenas-w-jornal.aep`, plano 3D por homografia) · G+H+I+K (`cenas-w-google.aep`, Corner Pin na perspectiva); fichas em `revisao/w-*.md`
30
+ - [x] Montagem (`ae/montagem.jsx` → `ae/montagem.aep`): 8 encaixes trocados, obturador 90°, comp "MAIN recriacao On My Way" (36,5 s com o card)
31
+ - [x] Render v1 (`render/on-my-way-recriacao-v1.mp4`, 36,5 s, 1080², 24 qps, −14,2 LUFS; cópia leve `-leve.mp4`) e conferência quadro a quadro (`revisao/rec-v1-2qps-*.jpg`). Lado a lado: `render/comparativo-original-x-recriacao-v1.mp4`
32
+ - [x] Curvas da câmera original × recriação (`revisao/camera-original-x-recriacao-v1*.png/.md`): correlação 0,94–1,00 na maioria dos canais; os picos que baixam G, H e K são falha do rastreador em quadros borrados (conferido quadro a quadro em `revisao/par-*.jpg`)
33
+ - [x] Doc principal da pesquisa + receita para qualquer música; rig `ae/camera-movimentos.jsx` (um null por movimento) conferido no AE
34
+ - [x] Entrega + anotação + memória
35
+ - [x] **Aprovado** pelo autor (17/10): "a recriação ficou perfeita"; pediu documentar cada passo e a abertura do celular
36
+ - [x] v2 com a abertura "celular filmando a tela → mergulho" (`transicao/`, mesmo efeito da Shelby ep. 2): `render/on-my-way-recriacao-v2-celular.mp4` (1080×1920, 24 qps, 37,1 s, −14,2 LUFS) e `07-videos/editados/subzmotion-on-my-way-recriacao-v2-celular.mp4`
37
+ - [x] Tutorial completo do estilo: `07-videos/linguagem-edicao/tutorial-lyric-edit-interfaces.md` (5 partes); base na tabela do `07-videos/_LEIA.md` e na skill `conteudo-video`
38
+ - [ ] Melhorias possíveis (se ele pedir): queima do jornal antigo mais vermelha e com chama (27–28 s); fotos do jornal 1 como vídeo; mapa do jogo menos verde; grafias da letra colada (3 no Spotify, vírgula no chip)
39
+ - [ ] Aprovado → Clube do Motion (projeto + referência + skills; fora do zip: música comercial, originais do @subzmotion, quadros do original em `ref/`)
40
+
41
+ ## Como usar esta pasta como modelo (próximo edit no estilo)
42
+
43
+ Passo a passo completo: [tutorial-lyric-edit-interfaces](../../linguagem-edicao/tutorial-lyric-edit-interfaces.md). Em resumo:
44
+
45
+ 1. Copie a pasta inteira para `07-videos/projetos/AAAA-MM-DD-<musica>/` e apague `render/`, `revisao/`, `ref/`, `transicao/quadros/` e as `cenas-w-*.aep`.
46
+ 2. Música em `audio/musica.wav`; `python -P audio/tempos_da_voz.py audio/musica.wav` (tempos das palavras, sem letra).
47
+ 3. Roteiro novo (`ROTEIRO-CENAS.md`) e campos novos em `ae/letra.json` (+ `JANELAS` em `ae/letra_automatica.py`). O autor cola a letra em `ae/LETRA-COMPLETA.txt`; `python -P ae/letra_automatica.py`.
48
+ 4. Câmera: lista de movimentos → `ae/camera-movimentos.jsx` (ou, copiando uma referência, `camera/integrar.py` → `ancorar.py` → `assar.py`).
49
+ 5. Cenas: um gerador por cena (os de `ae/A-agenda.jsx` … `ae/K-maps.jsx` e as interfaces de `assets/*/` são a biblioteca de interfaces). Agentes em paralelo com `BRIEF-AGENTES.md`.
50
+ 6. `ae/montagem.jsx` + `ae/card-spotify.jsx` (os `.jsx` acham a pasta do projeto sozinhos), render leve, conferência.
51
+ 7. Abertura do celular: `transicao/config.json` + os 6 scripts.
52
+
53
+ <!-- Cópia do README.md para o pacote do Clube do Motion (letra-nos-apps), sem os links dos posts do autor. Gerada em 17/10/2026; se o README mudar, gere de novo. -->