wizz-method 2.17.1 → 2.18.0
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.
- package/THIRD-PARTY-NOTICES.md +12 -0
- package/package.json +1 -1
- package/skills-registry.yaml +24 -6
- package/src/core-skills/_shared/handoff-protocol.md +3 -1
- package/src/modules/wizz/_shared/despacho-barato.md +1 -1
- package/src/modules/wizz/_shared/model-ladder.md +10 -0
- package/src/modules/wizz/_shared/video-pipeline.md +10 -0
- package/src/modules/wizz/agents/wizz-designer/customize.toml +2 -2
- package/src/modules/wizz/agents/wizz-maestro/SKILL.md +1 -1
- package/src/modules/wizz/agents/wizz-social/SKILL.md +2 -0
- package/src/modules/wizz/agents/wizz-social/customize.toml +10 -0
- package/src/modules/wizz/subagents/codex/wizz-exec-standard.toml +1 -1
- package/src/modules/wizz/subagents/codex/wizz-exec-strong.toml +1 -1
- package/src/modules/wizz/subagents/gemini/wizz-exec-standard.md +1 -1
- package/src/modules/wizz/subagents/gemini/wizz-exec-strong.md +1 -1
- package/src/modules/wizz/subagents/opencode/wizz-exec-standard.md +1 -1
- package/src/modules/wizz/subagents/opencode/wizz-exec-strong.md +1 -1
- package/src/modules/wizz/subagents/spec.yaml +6 -6
- package/src/modules/wizz/subagents/wizz-exec-standard.md +1 -1
- package/src/modules/wizz/subagents/wizz-exec-strong.md +1 -1
- package/src/skills-lib/wizz-hook-extractor/LICENSE +21 -0
- package/src/skills-lib/wizz-hook-extractor/SKILL.md +56 -0
- package/src/skills-lib/wizz-hook-extractor/references/examples.md +59 -0
- package/src/skills-lib/wizz-hook-extractor/references/formulas.md +136 -0
- package/src/skills-lib/wizz-hook-extractor/scripts/read-post.sh +83 -0
- package/src/skills-lib/wizz-ig-profile-optimizer/LICENSE +21 -0
- package/src/skills-lib/wizz-ig-profile-optimizer/SKILL.md +74 -0
- package/src/skills-lib/wizz-ig-profile-optimizer/scripts/capture-profile.sh +41 -0
- package/src/skills-lib/wizz-ig-profile-optimizer/scripts/limits.mjs +82 -0
- package/src/skills-lib/wizz-premium-landing-ui-researcher/SKILL.md +5 -0
- package/src/skills-lib/wizz-premium-landing-ui-researcher/references/component-sources.md +1 -1
- package/src/skills-lib/wizz-premium-landing-ui-researcher/references/source-first-protocol.md +1 -1
- package/src/skills-lib/wizz-premium-landing-ui-researcher/references/web-access-and-evidence.md +40 -0
- package/src/skills-lib/wizz-router/references/routing-table-flat.md +3 -1
- package/src/skills-lib/wizz-ui-component-curator/SKILL.md +5 -3
- package/src/skills-lib/wizz-video-effects/SKILL.md +1 -0
- package/src/skills-lib/wizz-video-styles/SKILL.md +9 -3
- package/src/skills-lib/wizz-video-styles/data/recipes.json +33 -1
- package/src/skills-lib/wizz-video-styles/data/references.json +175 -0
- package/src/skills-lib/wizz-video-styles/data/styles.json +55 -0
- package/src/skills-lib/wizz-video-styles/references/generated-prompts.md +60 -0
- package/src/skills-lib/wizz-video-styles/references/measured-patterns.md +2 -0
- package/src/skills-lib/wizz-video-styles/references/style-picker.md +5 -3
- package/src/skills-lib/wizz-video-styles/references/styles/tipografia-cinetica.md +68 -0
- package/src/skills-lib/wizz-video-styles/scripts/analyze.sh +2 -1
- package/src/skills-lib/wizz-video-styles/scripts/beat_grid.py +68 -0
- package/src/skills-lib/wizz-video-styles/scripts/references.mjs +264 -0
|
@@ -20,6 +20,8 @@ It can also run at step 1, so the script is written for the style (room for b-ro
|
|
|
20
20
|
|
|
21
21
|
## Per piece
|
|
22
22
|
|
|
23
|
+
Scripts live in this skill's `scripts/`; run them from the project root (the history file is written in the current directory, or in `--project <dir>`).
|
|
24
|
+
|
|
23
25
|
1. **Inventory first, always.** Ask or read from the plan, one line each:
|
|
24
26
|
- Is a real person on camera? Which shots exist (face to camera, podcast, stage, street, hands only)?
|
|
25
27
|
- Voice: the person's own recording, a podcast/interview track, TTS, or none?
|
|
@@ -27,16 +29,17 @@ It can also run at step 1, so the script is written for the style (room for b-ro
|
|
|
27
29
|
- B-roll or stock, a physical product, food, a place?
|
|
28
30
|
- Niche, goal of the piece (follow, comment, save, click), duration.
|
|
29
31
|
Never pick a style the material cannot feed. "Talking head" needs a person on camera; "motion editorial" needs nothing but a voice; "macro / ASMR" needs a physical product filmed close.
|
|
30
|
-
2. **Pick the style** with [references/style-picker.md](references/style-picker.md): material decides which styles are possible, niche and goal rank them. Mixed is allowed and common: person on camera for the hook and the CTA, another style in the middle.
|
|
32
|
+
2. **Pick the style** with [references/style-picker.md](references/style-picker.md): material decides which styles are possible, niche and goal rank them. Mixed is allowed and common: person on camera for the hook and the CTA, another style in the middle. Before deciding, run `node scripts/references.mjs history` in the project: if the last two pieces used the same style, do not pick it a third time unless the user wants a series; choose another style the material allows, or keep it only for the hook and CTA.
|
|
31
33
|
3. **Open the style card** in `references/styles/` and follow it: rhythm, captions, palette, typography, motion, music, SFX, hook, CTA, and its "do not" list.
|
|
32
|
-
4. **
|
|
34
|
+
4. **Get fresh references and watch two** before editing: `node scripts/references.mjs pick --style <key>` returns measured references plus, for styles generated in code, current viral videos from the awesome-opus5-5-videos list, skipping everything this project already used. Reading a card is not watching a video. For how those generated videos were briefed, see [references/generated-prompts.md](references/generated-prompts.md).
|
|
33
35
|
5. **Audio:** music and SFX choices from [references/audio.md](references/audio.md); the redistributable kit is in `assets/audio/` (credits in `assets/audio/CREDITS.md`).
|
|
34
36
|
6. **Hand over** to `wizz-video-effects` with the card's SFX ceiling and music level, then to Remotion.
|
|
35
37
|
7. **Look at the final file**: extract frames and compare them side by side with a contact sheet of one reference.
|
|
38
|
+
8. **Record the piece**: `node scripts/references.mjs record --style <key> --piece <slug> --refs <ids watched>`. It writes `_wizz-output/video-styles/historico.json`, which is what keeps the next piece from repeating the same references and look.
|
|
36
39
|
|
|
37
40
|
## What the measurements say across every style
|
|
38
41
|
|
|
39
|
-
- **SFX are rare.** Across
|
|
42
|
+
- **SFX are rare.** Across 104 measured shorts, sound effects landed on a cut in a small minority of cuts (per-style counts in each card). Putting a sound on every cut is the most common amateur tell.
|
|
40
43
|
- **Music bed: style-dependent.** Fast montage, animated explainers and travel POV carry a continuous bed; talking heads and podcast clips mostly have none or a faint one.
|
|
41
44
|
- **Captions** in reach-heavy styles are 1 to 3 words at a time, bold, lower-centre, with one keyword in an accent colour; editorial and explainer styles keep them small or drop them.
|
|
42
45
|
- **Rhythm is a style property, not a virtue.** The highest-reach videos in the set range from single takes with no detected cut to a cut every second.
|
|
@@ -53,7 +56,10 @@ Details and the per-style table: [references/measured-patterns.md](references/me
|
|
|
53
56
|
| [references/audio.md](references/audio.md) | music and SFX per style, the kit, libraries and licences |
|
|
54
57
|
| [references/research-method.md](references/research-method.md) | how the catalogue was measured, its limits, how to add a niche or a style |
|
|
55
58
|
| `data/references.json` | every measured short: link, channel, views, niche, style, metrics |
|
|
59
|
+
| [references/generated-prompts.md](references/generated-prompts.md) | prompts of viral code-generated videos, grouped by style, and how they run here |
|
|
56
60
|
| `scripts/analyze.sh` | measures one short (cuts, music bed, SFX heuristics, loudness, contact sheet) |
|
|
61
|
+
| `scripts/references.mjs` | fresh references per style without repeats, and the project's history of styles used |
|
|
62
|
+
| `scripts/beat_grid.py` | BPM, beats and downbeats of a song, so cuts snap to the music (lyric video, kinetic typography) |
|
|
57
63
|
| `assets/remotion/editorial/` | Remotion template of the motion editorial style |
|
|
58
64
|
| `assets/audio/` | licensed music beds and SFX that may ship with a project |
|
|
59
65
|
|
|
@@ -7,7 +7,8 @@
|
|
|
7
7
|
"e": "health / fitness / nutrition",
|
|
8
8
|
"f": "food / recipes / lifestyle / travel",
|
|
9
9
|
"g": "beauty / fashion / local services (clinic, salon, architecture, real estate)",
|
|
10
|
-
"h": "education / science / comedy"
|
|
10
|
+
"h": "education / science / comedy",
|
|
11
|
+
"i": "music / artists / lyric videos"
|
|
11
12
|
},
|
|
12
13
|
"styles": {
|
|
13
14
|
"motion-editorial": {
|
|
@@ -336,6 +337,37 @@
|
|
|
336
337
|
"dont": [
|
|
337
338
|
"Setup longer than 3 s before the first joke."
|
|
338
339
|
]
|
|
340
|
+
},
|
|
341
|
+
"tipografia-cinetica": {
|
|
342
|
+
"title": "Kinetic typography and lyric video (mexicat P(doom), PixelValet, Ninh Truong)",
|
|
343
|
+
"needs": "An audio track with words: a song with its lyrics, or a voice-over with its script. Nobody on camera, no footage. Everything is generated (HyperFrames, three.js, canvas). Word timing from ctc-align; a beat grid when there is music.",
|
|
344
|
+
"fits": "Music releases and lyric videos, manifestos, a strong opinion read aloud, brand promos built on a slogan, poetry. Shares and rewatches; weak for teaching step by step.",
|
|
345
|
+
"look": [
|
|
346
|
+
"The words ARE the image: one keyword huge in the frame, the rest of the line small. No caption strip at the bottom.",
|
|
347
|
+
"Plain background (black, or a flat colour that changes per section) or one generated world the camera travels through (a map, a grid in perspective, a 3D object the line wraps around).",
|
|
348
|
+
"Each word is animated by what it means: PAIN breaks, CHEERFUL bounces, LIGHT lights up, a word curves around the object it names.",
|
|
349
|
+
"Code-rendered pieces add film treatment: bloom, halation, grain, a thin HUD in mono."
|
|
350
|
+
],
|
|
351
|
+
"palette": "Dark base (#0A0A0A or a deep teal #0F3B3A) plus ONE glowing accent (orange #FF6A1A in P(doom), coral in Maps) and off-white text. Flat-colour variant: one saturated background per word or section, text in white or ink.",
|
|
352
|
+
"type": "Two or three families with high contrast: a heavy grotesk or condensed caps for the keyword (Archivo Black, Bebas Neue), a mono for small lines and HUD (IBM Plex Mono), optionally a serif italic for a soft word (Cormorant Garamond). Mix weights inside the same line.",
|
|
353
|
+
"motion": [
|
|
354
|
+
"Words enter on the syllable they are sung or spoken (forced alignment, never guessed); the keyword lands on the beat.",
|
|
355
|
+
"Scene changes snap to the downbeat. P(doom) changes every ~0.3 s at peaks; the calm variant is a single continuous camera move for a whole verse.",
|
|
356
|
+
"The camera moves through the type (push, orbit, travel across a map) instead of the text sliding over a still frame.",
|
|
357
|
+
"Hold the last word of a line a beat longer before it leaves."
|
|
358
|
+
],
|
|
359
|
+
"captions": "None separate: the type is the caption. Keep the full line readable somewhere (small, mono, corner) when the keyword alone would lose the sense.",
|
|
360
|
+
"hook": "The first sung or spoken word, huge, in the accent colour, landing on the first downbeat (0 to 1 s). No logo intro.",
|
|
361
|
+
"cta": "The title or slogan locked up big and still for 1 to 2 s, the accent lit; one small line (artist, handle, link) under it.",
|
|
362
|
+
"music": "The track itself is the whole soundtrack. Lyric videos: song at full level, no extra bed. Voice-over promos: a beat-driven bed under the voice.",
|
|
363
|
+
"sfx": "Song-driven pieces: almost none (5 of 56 cuts in P(doom), 0 of 2 in Maps). Promo variant: hits on every word change (9/13 and 16/20 cuts), which the heuristic partly confuses with the kick. Never add whooshes over a song.",
|
|
364
|
+
"dont": [
|
|
365
|
+
"Karaoke subtitles at the bottom over a static background: that is a lyric 'overlay', not this style.",
|
|
366
|
+
"Guessing word timing by ear; align it (ctc-align) and snap scenes to the beat grid.",
|
|
367
|
+
"More than one accent colour, or a different font for every word.",
|
|
368
|
+
"Shipping 16:9: every measured reference is horizontal; recompose for 9:16 with the keyword in the upper-middle and nothing in the bottom 20%."
|
|
369
|
+
],
|
|
370
|
+
"template": null
|
|
339
371
|
}
|
|
340
372
|
}
|
|
341
373
|
}
|
|
@@ -3393,5 +3393,180 @@
|
|
|
3393
3393
|
"on_cuts": "0/2"
|
|
3394
3394
|
},
|
|
3395
3395
|
"lufs": "-14.4"
|
|
3396
|
+
},
|
|
3397
|
+
{
|
|
3398
|
+
"id": "5EoO5413dBY",
|
|
3399
|
+
"url": "https://www.youtube.com/watch?v=5EoO5413dBY",
|
|
3400
|
+
"channel": "mexicat",
|
|
3401
|
+
"views": "120706",
|
|
3402
|
+
"nicho": "i",
|
|
3403
|
+
"idioma": "en",
|
|
3404
|
+
"style": "tipografia-cinetica",
|
|
3405
|
+
"estilo_visual": "Clipe musical renderizado em código (three.js): fundo preto, uma palavra ou verso gigante por batida sobre cenas 3D (buraco negro, criatura, torre, grade em perspectiva), HUD de interface e formulários de papel",
|
|
3406
|
+
"motion_transicoes": "Troca de cena no downbeat, texto entra palavra a palavra no tempo da voz, câmera orbita objetos 3D, bloom e grão de filme, texto curvado em volta de objetos",
|
|
3407
|
+
"legenda": "A letra É o visual: palavra-chave gigante (sans bold ou serifa) + verso pequeno em mono no canto; nunca legenda embaixo",
|
|
3408
|
+
"hook_0_3s": "Linhas de código de terminal e uma palavra laranja enorme ('AGI') já no primeiro verso",
|
|
3409
|
+
"paleta": "preto, laranja incandescente, branco/creme; um respiro em papel bege",
|
|
3410
|
+
"tipografia": "sans grotesca pesada (Archivo), mono (IBM Plex Mono), serifa itálica (Cormorant) e fonte de traço único",
|
|
3411
|
+
"ritmo": {
|
|
3412
|
+
"cuts_per_10s": 3.57,
|
|
3413
|
+
"shot_median_s": 0.27
|
|
3414
|
+
},
|
|
3415
|
+
"musica": {
|
|
3416
|
+
"guess": "continua",
|
|
3417
|
+
"ratio": 0.326,
|
|
3418
|
+
"impressão": "heurística de áudio; nome da faixa não confirmado"
|
|
3419
|
+
},
|
|
3420
|
+
"sfx": {
|
|
3421
|
+
"counts": {
|
|
3422
|
+
"pop_click": 0,
|
|
3423
|
+
"whoosh": 29,
|
|
3424
|
+
"impact_low": 0
|
|
3425
|
+
},
|
|
3426
|
+
"per_10s": 1.85,
|
|
3427
|
+
"on_cuts": "5/56"
|
|
3428
|
+
},
|
|
3429
|
+
"lufs": "-15.5",
|
|
3430
|
+
"formato": "16:9 (horizontal); adaptar para 9:16"
|
|
3431
|
+
},
|
|
3432
|
+
{
|
|
3433
|
+
"id": "FOMWS5pChuA",
|
|
3434
|
+
"url": "https://www.youtube.com/watch?v=FOMWS5pChuA",
|
|
3435
|
+
"channel": "Ninh Truong",
|
|
3436
|
+
"views": "243944",
|
|
3437
|
+
"nicho": "i",
|
|
3438
|
+
"idioma": "en",
|
|
3439
|
+
"style": "tipografia-cinetica",
|
|
3440
|
+
"estilo_visual": "Lyric video em tipografia cinética: palavras da letra montadas como objetos num mundo 2D (mapa de ruas visto de cima, nuvens, rádio), fundo verde-petróleo",
|
|
3441
|
+
"motion_transicoes": "Câmera viaja e gira pelo mapa seguindo o verso, palavras dobram em 3D e se encaixam nas ruas, poucas trocas de cena (plano contínuo)",
|
|
3442
|
+
"legenda": "A letra é o visual: verso inteiro, palavras de pesos e tamanhos misturados, palavra-chave em destaque de cor",
|
|
3443
|
+
"hook_0_3s": "Letra 'I' gigante no escuro, depois o verso se monta em blocos",
|
|
3444
|
+
"paleta": "verde-petróleo escuro, verde-menta, coral/salmão de acento, cinza nas cenas de céu",
|
|
3445
|
+
"tipografia": "sans condensada em caixa alta misturada com sans fina e script; pesos alternados no mesmo verso",
|
|
3446
|
+
"ritmo": {
|
|
3447
|
+
"cuts_per_10s": 0.63,
|
|
3448
|
+
"shot_median_s": 9.92
|
|
3449
|
+
},
|
|
3450
|
+
"musica": {
|
|
3451
|
+
"guess": "continua",
|
|
3452
|
+
"ratio": 0.218,
|
|
3453
|
+
"impressão": "heurística de áudio; nome da faixa não confirmado"
|
|
3454
|
+
},
|
|
3455
|
+
"sfx": {
|
|
3456
|
+
"counts": {
|
|
3457
|
+
"pop_click": 0,
|
|
3458
|
+
"whoosh": 2,
|
|
3459
|
+
"impact_low": 0
|
|
3460
|
+
},
|
|
3461
|
+
"per_10s": 0.63,
|
|
3462
|
+
"on_cuts": "0/2"
|
|
3463
|
+
},
|
|
3464
|
+
"lufs": "-15.9",
|
|
3465
|
+
"formato": "16:9 (horizontal); adaptar para 9:16"
|
|
3466
|
+
},
|
|
3467
|
+
{
|
|
3468
|
+
"id": "Kt8vGm8_ekQ",
|
|
3469
|
+
"url": "https://www.youtube.com/watch?v=Kt8vGm8_ekQ",
|
|
3470
|
+
"channel": "PixelValet",
|
|
3471
|
+
"views": "928526",
|
|
3472
|
+
"nicho": "h",
|
|
3473
|
+
"idioma": "en",
|
|
3474
|
+
"style": "tipografia-cinetica",
|
|
3475
|
+
"estilo_visual": "Uma palavra por cena (emoções) no centro de um fundo de cor chapada; cada palavra é animada pelo significado dela (PAIN quebra, CHEERFUL pula em arco, LIGHT acende)",
|
|
3476
|
+
"motion_transicoes": "Uma bolinha guia passa de cena em cena; troca de fundo a cada palavra; a letra se deforma conforme o sentido",
|
|
3477
|
+
"legenda": "Sem legenda; a palavra é o único elemento",
|
|
3478
|
+
"hook_0_3s": "Fundo roxo, bolinha e a primeira palavra se desenhando",
|
|
3479
|
+
"paleta": "cores chapadas que mudam por emoção (roxo, amarelo, azul, verde, vermelho, cinza escuro)",
|
|
3480
|
+
"tipografia": "sans geométrica, uma família só, variando peso e espaçamento",
|
|
3481
|
+
"ritmo": {
|
|
3482
|
+
"cuts_per_10s": 0.68,
|
|
3483
|
+
"shot_median_s": 7.29
|
|
3484
|
+
},
|
|
3485
|
+
"musica": {
|
|
3486
|
+
"guess": "sem",
|
|
3487
|
+
"ratio": 0.021,
|
|
3488
|
+
"impressão": "heurística de áudio; nome da faixa não confirmado"
|
|
3489
|
+
},
|
|
3490
|
+
"sfx": {
|
|
3491
|
+
"counts": {
|
|
3492
|
+
"pop_click": 0,
|
|
3493
|
+
"whoosh": 78,
|
|
3494
|
+
"impact_low": 16
|
|
3495
|
+
},
|
|
3496
|
+
"per_10s": 21.18,
|
|
3497
|
+
"on_cuts": "1/3"
|
|
3498
|
+
},
|
|
3499
|
+
"lufs": "-22.5",
|
|
3500
|
+
"formato": "16:9 (horizontal); adaptar para 9:16"
|
|
3501
|
+
},
|
|
3502
|
+
{
|
|
3503
|
+
"id": "9ngNySFSWWU",
|
|
3504
|
+
"url": "https://www.youtube.com/watch?v=9ngNySFSWWU",
|
|
3505
|
+
"channel": "PiXeL ARTWORKS",
|
|
3506
|
+
"views": "1215529",
|
|
3507
|
+
"nicho": "d",
|
|
3508
|
+
"idioma": "en",
|
|
3509
|
+
"style": "tipografia-cinetica",
|
|
3510
|
+
"estilo_visual": "Texto pequeno branco no centro de fundo preto, alternando com fundo branco de papel; nada além do texto",
|
|
3511
|
+
"motion_transicoes": "Corte seco a cada frase no ritmo da narração/batida; palavras trocam de lugar e de caixa; sem objetos",
|
|
3512
|
+
"legenda": "O texto é a narração escrita, uma frase curta por vez",
|
|
3513
|
+
"hook_0_3s": "Logo do estúdio, depois 'fine good? ok' no preto",
|
|
3514
|
+
"paleta": "preto, branco, um vermelho só na abertura",
|
|
3515
|
+
"tipografia": "sans neutra pequena com tracking largo",
|
|
3516
|
+
"ritmo": {
|
|
3517
|
+
"cuts_per_10s": 4.62,
|
|
3518
|
+
"shot_median_s": 1.54
|
|
3519
|
+
},
|
|
3520
|
+
"musica": {
|
|
3521
|
+
"guess": "sem",
|
|
3522
|
+
"ratio": 0.0,
|
|
3523
|
+
"impressão": "heurística de áudio; nome da faixa não confirmado"
|
|
3524
|
+
},
|
|
3525
|
+
"sfx": {
|
|
3526
|
+
"counts": {
|
|
3527
|
+
"pop_click": 0,
|
|
3528
|
+
"whoosh": 44,
|
|
3529
|
+
"impact_low": 78
|
|
3530
|
+
},
|
|
3531
|
+
"per_10s": 28.2,
|
|
3532
|
+
"on_cuts": "16/20"
|
|
3533
|
+
},
|
|
3534
|
+
"lufs": "-8.9",
|
|
3535
|
+
"formato": "16:9 (horizontal); adaptar para 9:16"
|
|
3536
|
+
},
|
|
3537
|
+
{
|
|
3538
|
+
"id": "A0CKFb24MJ8",
|
|
3539
|
+
"url": "https://www.youtube.com/watch?v=A0CKFb24MJ8",
|
|
3540
|
+
"channel": "Cinthya Studios",
|
|
3541
|
+
"views": "353630",
|
|
3542
|
+
"nicho": "d",
|
|
3543
|
+
"idioma": "en",
|
|
3544
|
+
"style": "tipografia-cinetica",
|
|
3545
|
+
"estilo_visual": "Promo em tipografia cinética: palavras em caixa alta condensada que preenchem a tela, algumas com vídeo de cidade dentro das letras",
|
|
3546
|
+
"motion_transicoes": "Corte na batida, palavras escalam e deslizam, texto vira máscara de vídeo, ícone animado no fim",
|
|
3547
|
+
"legenda": "O texto é a mensagem de venda; uma ou duas palavras por vez",
|
|
3548
|
+
"hook_0_3s": "'CONVERT' em condensada preta no branco, inverte pra fundo preto",
|
|
3549
|
+
"paleta": "preto, branco, azul e vermelho só no logo final",
|
|
3550
|
+
"tipografia": "sans condensada em caixa alta (estilo Bebas), pesos variando",
|
|
3551
|
+
"ritmo": {
|
|
3552
|
+
"cuts_per_10s": 5.54,
|
|
3553
|
+
"shot_median_s": 1.32
|
|
3554
|
+
},
|
|
3555
|
+
"musica": {
|
|
3556
|
+
"guess": "continua",
|
|
3557
|
+
"ratio": 0.176,
|
|
3558
|
+
"impressão": "heurística de áudio; nome da faixa não confirmado"
|
|
3559
|
+
},
|
|
3560
|
+
"sfx": {
|
|
3561
|
+
"counts": {
|
|
3562
|
+
"pop_click": 0,
|
|
3563
|
+
"whoosh": 0,
|
|
3564
|
+
"impact_low": 46
|
|
3565
|
+
},
|
|
3566
|
+
"per_10s": 19.6,
|
|
3567
|
+
"on_cuts": "9/13"
|
|
3568
|
+
},
|
|
3569
|
+
"lufs": "-10.7",
|
|
3570
|
+
"formato": "16:9 (horizontal); adaptar para 9:16"
|
|
3396
3571
|
}
|
|
3397
3572
|
]
|
|
@@ -722,5 +722,60 @@
|
|
|
722
722
|
"url": "https://www.youtube.com/shorts/zKJSyVJB_yE"
|
|
723
723
|
}
|
|
724
724
|
]
|
|
725
|
+
},
|
|
726
|
+
"tipografia-cinetica": {
|
|
727
|
+
"n": 5,
|
|
728
|
+
"nichos": [
|
|
729
|
+
"d",
|
|
730
|
+
"h",
|
|
731
|
+
"i"
|
|
732
|
+
],
|
|
733
|
+
"langs": [
|
|
734
|
+
"en"
|
|
735
|
+
],
|
|
736
|
+
"views_max": 1215529,
|
|
737
|
+
"views_median": 353630,
|
|
738
|
+
"cuts_per_10s": [
|
|
739
|
+
0.63,
|
|
740
|
+
3.57,
|
|
741
|
+
5.54
|
|
742
|
+
],
|
|
743
|
+
"shot_median_s": 1.54,
|
|
744
|
+
"music": {
|
|
745
|
+
"continua": 3,
|
|
746
|
+
"fraca": 0,
|
|
747
|
+
"sem": 2
|
|
748
|
+
},
|
|
749
|
+
"sfx_on_cuts": [
|
|
750
|
+
31,
|
|
751
|
+
94
|
|
752
|
+
],
|
|
753
|
+
"top": [
|
|
754
|
+
{
|
|
755
|
+
"channel": "PiXeL ARTWORKS",
|
|
756
|
+
"views": "1215529",
|
|
757
|
+
"url": "https://www.youtube.com/watch?v=9ngNySFSWWU"
|
|
758
|
+
},
|
|
759
|
+
{
|
|
760
|
+
"channel": "PixelValet",
|
|
761
|
+
"views": "928526",
|
|
762
|
+
"url": "https://www.youtube.com/watch?v=Kt8vGm8_ekQ"
|
|
763
|
+
},
|
|
764
|
+
{
|
|
765
|
+
"channel": "Cinthya Studios",
|
|
766
|
+
"views": "353630",
|
|
767
|
+
"url": "https://www.youtube.com/watch?v=A0CKFb24MJ8"
|
|
768
|
+
},
|
|
769
|
+
{
|
|
770
|
+
"channel": "Ninh Truong",
|
|
771
|
+
"views": "243944",
|
|
772
|
+
"url": "https://www.youtube.com/watch?v=FOMWS5pChuA"
|
|
773
|
+
},
|
|
774
|
+
{
|
|
775
|
+
"channel": "mexicat",
|
|
776
|
+
"views": "120706",
|
|
777
|
+
"url": "https://www.youtube.com/watch?v=5EoO5413dBY"
|
|
778
|
+
}
|
|
779
|
+
]
|
|
725
780
|
}
|
|
726
781
|
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Generated-video prompts, by catalogue style
|
|
2
|
+
|
|
3
|
+
Fifteen prompts picked from [awesome-opus5-5-videos](https://github.com/yihui-dev/awesome-opus5-5-videos) (513 entries in its `data/videos.json`, MIT, checked on 2026-10-10): videos that a model wrote as code (canvas, SVG, three.js) and that went viral. Each line says what the prompt asks for, in our words; the full prompt and the video are at the links. The prompts belong to their creators: link to them, quote at most a phrase, never paste the whole prompt into a project or a skill.
|
|
4
|
+
|
|
5
|
+
This page is a fixed sample. For current videos, and to skip the ones a project already used, run `node scripts/references.mjs pick --style <key>`.
|
|
6
|
+
|
|
7
|
+
Use this page **after** the style is chosen ([style-picker.md](style-picker.md)), to see how others briefed a model for that look. These are not measured references: none of them enter `data/references.json` unless one is measured with `scripts/analyze.sh`.
|
|
8
|
+
|
|
9
|
+
## How a generated piece runs in the Method
|
|
10
|
+
|
|
11
|
+
- **HyperFrames draws, Remotion assembles** (`_shared/video-pipeline.md`, wizz module). A prompt that asks for "one HTML file with the music" becomes: scenes drawn silent in HyperFrames, assembled in Remotion with the audio.
|
|
12
|
+
- **Timing is measured, never guessed.** Words from `ctc-align`; cuts on the downbeats from [`scripts/beat_grid.py`](../scripts/beat_grid.py). Several prompts below ask for a "120 BPM grid" by hand; the script gives the real grid of the actual song.
|
|
13
|
+
- **Recompose for 9:16** when the original was square or horizontal: keyword in the upper-middle, nothing important in the bottom 20 %.
|
|
14
|
+
|
|
15
|
+
## Kinetic typography and lyric video ([card](styles/tipografia-cinetica.md))
|
|
16
|
+
|
|
17
|
+
| Prompt | What it asks | Links |
|
|
18
|
+
|---|---|---|
|
|
19
|
+
| anabology · Claude Pop remake | Reimagine a whole pop song's video end to end with the same audio, every lyric represented on screen, free to go abstract. | [post](https://x.com/anabology/status/2103534482930491441) · [video](https://skillry.dev/ai-videos/opus-5-5/anabology-491441) |
|
|
20
|
+
| doubleunplussed · music video "like P(doom)" | Music video in the P(doom) style for another song, with references and lore instead of literal lyrics ("tokens" becomes a stochastic parrot). | [post](https://x.com/doubleunplussed/status/2103697580421181894) · [video](https://skillry.dev/ai-videos/opus-5-5/doubleunplussed-181894) |
|
|
21
|
+
| ParanoidAmerica · evil pizza parlour | A full music video synced to a `lyrics.txt`, one continuous camera journey through a place, collage plus ballpoint-pen look. | [post](https://x.com/ParanoidAmerica/status/2103570879619686717) · [video](https://skillry.dev/ai-videos/opus-5-5/paranoidamerica-686717) |
|
|
22
|
+
| twoclipping · wordmark launch film | A one-word brand built in one continuous take to a 120 BPM song with a drop and a breakdown; every scene grows out of the previous one, nothing fades or cuts. | [post](https://x.com/twoclipping/status/2103835273813496100) · [video](https://skillry.dev/ai-videos/opus-5-5/twoclipping-496100) |
|
|
23
|
+
|
|
24
|
+
## Motion editorial and product UI ([card](styles/motion-editorial.md))
|
|
25
|
+
|
|
26
|
+
| Prompt | What it asks | Links |
|
|
27
|
+
|---|---|---|
|
|
28
|
+
| twoclipping · one shape, many UI states | One element morphs through 8 to 12 UI states (button, loader, player, chart), a cursor drives each change, springs, cut on a 120 BPM grid. | [post](https://x.com/twoclipping/status/2103273003555402193) · [video](https://skillry.dev/ai-videos/opus-5-5/twoclipping-402193) |
|
|
29
|
+
| verbove · product story in one canvas | The same morphing-shape idea as strict rules: one `draw(t)` function, real UI and real data, something on every beat, exports in 1:1, 16:9 and 9:16. | [post](https://x.com/verbove/status/2103483957266268381) · [video](https://skillry.dev/ai-videos/opus-5-5/verbove-268381) |
|
|
30
|
+
| AnnaCher · promo from the product's own UI | Same structure built from the real product UI, one accent colour, last frame equals the first so it loops; a long banned list (glows, particles, bouncy easing). | [post](https://x.com/AnnaCher___/status/2103571096549433425) · [video](https://skillry.dev/ai-videos/opus-5-5/annacher-433425) |
|
|
31
|
+
| jhylee95 · launch video | Research the product, hook in the first second, distinct beats for proof, every cut and hit retimed until it lands on a beat or drop. | [post](https://x.com/jhylee95/status/2103604431627452427) · [video](https://skillry.dev/ai-videos/opus-5-5/jhylee95-452427) |
|
|
32
|
+
| twoclipping · minimal launch with real footage | 10 bars at 120 BPM, one idea per shot, masked type reveals and match cuts over the owner's real vertical clips; no placeholder cards. | [post](https://x.com/twoclipping/status/2102554209166000267) · [video](https://skillry.dev/ai-videos/opus-5-5/twoclipping-000267) |
|
|
33
|
+
| Mounnna · logo reveal | Logo drawn as a wireframe, low-poly shards fly in and assemble it, then the icon background and the typed brand name. A 3 to 5 s sting for the CTA. | [post](https://x.com/Mounnna/status/2103802871934497266) · [video](https://skillry.dev/ai-videos/opus-5-5/mounnna-497266) |
|
|
34
|
+
|
|
35
|
+
## Animated explainer ([card](styles/explicador-animado.md))
|
|
36
|
+
|
|
37
|
+
| Prompt | What it asks | Links |
|
|
38
|
+
|---|---|---|
|
|
39
|
+
| alex_prompter · 30 s business explainer | Five scenes: the customer's problem, what you do, how it works in 3 steps, one proof point, your name. The simplest brief that works. | [post](https://x.com/alex_prompter/status/2103499977632997524) · [video](https://skillry.dev/ai-videos/opus-5-5/alex-prompter-997524) |
|
|
40
|
+
| emollick · recursion | Explain one idea where each explanation switches to a radically different visual style; self-referential and fast. | [post](https://x.com/emollick/status/2103688362960019567) · [video](https://skillry.dev/ai-videos/opus-5-5/emollick-019567) |
|
|
41
|
+
| koldo2k · infinite zoom loop | A looping camera flight from landscape to landscape through vintage objects, collage and halftone look, film grain. | [post](https://x.com/koldo2k/status/2103129343253778767) · [video](https://skillry.dev/ai-videos/opus-5-5/koldo2k-778767) |
|
|
42
|
+
|
|
43
|
+
## Whiteboard ([card](styles/quadro-branco.md))
|
|
44
|
+
|
|
45
|
+
| Prompt | What it asks | Links |
|
|
46
|
+
|---|---|---|
|
|
47
|
+
| tak3sh8 · hand-drawn physics | Hand-drawn whiteboard animation of a hard concept, then a second prompt turning it into an MP4 with narration. | [post](https://x.com/tak3sh8/status/2103667481139441895) · [video](https://skillry.dev/ai-videos/opus-5-5/tak3sh8-441895) |
|
|
48
|
+
|
|
49
|
+
## Fast meme ([card](styles/meme-rapido.md))
|
|
50
|
+
|
|
51
|
+
| Prompt | What it asks | Links |
|
|
52
|
+
|---|---|---|
|
|
53
|
+
| kloss_xyz · 9:16 brain rot | A chaotic vertical video generated in Python and rendered with ffmpeg, with motion and sound design, told from the model's point of view. | [post](https://x.com/kloss_xyz/status/2103664956482941143) · [video](https://skillry.dev/ai-videos/opus-5-5/kloss-xyz-941143) |
|
|
54
|
+
|
|
55
|
+
## What the good ones share
|
|
56
|
+
|
|
57
|
+
- **Inputs first.** The strongest prompts open with an `<inputs>` block: ask for the real product, the real UI states, the real data, the brand colour and the song before drawing anything.
|
|
58
|
+
- **A banned list.** Glows, particle bursts, RGB split, camera shake, bouncy easing: naming what not to do does more than adjectives.
|
|
59
|
+
- **One rule for continuity.** "One shape, never cut" or "every scene is made out of the previous one" gives the piece its identity.
|
|
60
|
+
- **The beat as the clock.** Every change lands on a beat or a drop. Here that clock comes from `beat_grid.py`, not from a hand-typed BPM.
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
Set measured on 2026-10-04: **99 public shorts**, 46 in English and 53 in Brazilian Portuguese, 8 niche groups (9 to 16 per group), views from 23,718 to 107,324,401 (median 608,820). Per-style numbers live in [`data/styles.json`](../data/styles.json) and at the top of each card; this page keeps only what holds across styles. Method and limits: [research-method.md](research-method.md).
|
|
4
4
|
|
|
5
|
+
Added on 2026-10-10: 5 kinetic typography and lyric videos (style `tipografia-cinetica`, all 16:9, a 9th niche group: music). They are in the per-style numbers and their card, but not in the cross-style aggregates below, which still describe the 99 vertical shorts.
|
|
6
|
+
|
|
5
7
|
## Sound effects are rare
|
|
6
8
|
|
|
7
9
|
| | Cuts with a sound effect on them |
|
|
@@ -7,6 +7,7 @@ The material decides which styles are **possible**. Niche and goal only rank the
|
|
|
7
7
|
| What the project has | Possible styles |
|
|
8
8
|
|---|---|
|
|
9
9
|
| Voice only (own recording or TTS), no one on camera | [motion-editorial](styles/motion-editorial.md), [explicador-animado](styles/explicador-animado.md), [meme-rapido](styles/meme-rapido.md) |
|
|
10
|
+
| A song with its lyrics, or a voice-over or slogan with no footage at all | [tipografia-cinetica](styles/tipografia-cinetica.md) (the words are the image; scenes generated in code) |
|
|
10
11
|
| Voice + app/site screens | [motion-editorial](styles/motion-editorial.md) (screenshots in a phone card), [camera-no-setup](styles/camera-no-setup.md) (film the screen), [meme-rapido](styles/meme-rapido.md) |
|
|
11
12
|
| A person on camera talking, nothing else | [talking-head-legenda](styles/talking-head-legenda.md) |
|
|
12
13
|
| A person on camera + headlines, articles, charts, b-roll | [talking-head-cards](styles/talking-head-cards.md) (face bubble over full-screen b-roll is a variant) |
|
|
@@ -31,18 +32,19 @@ From [`data/references.json`](../data/references.json): the styles measured in e
|
|
|
31
32
|
| dev / AI / build in public (EN) | meme-rapido (8.8M), camera-no-setup (8.1M), talking-head-legenda (5.4M), quadro-branco (4.4M), motion-editorial (0.83M), talking-head-cards (0.82M), podcast-clip (0.48M) |
|
|
32
33
|
| dev / AI / tech business (BR) | quadro-branco (1.1M), talking-head-cards (0.41M), talking-head-legenda (0.26M), meme-rapido (0.23M), camera-no-setup (0.11M) |
|
|
33
34
|
| personal finance / investing | podcast-clip (1.2M), talking-head-cards (1.2M), talking-head-legenda (0.77M) |
|
|
34
|
-
| product / SaaS / marketing / business / law | desafio-numero (20.9M), talking-head-legenda (9.8M), camera-no-setup (0.53M), podcast-clip (0.33M), quadro-branco (0.15M), talking-head-cards (0.09M) |
|
|
35
|
+
| product / SaaS / marketing / business / law | desafio-numero (20.9M), talking-head-legenda (9.8M), tipografia-cinetica (1.2M), camera-no-setup (0.53M), podcast-clip (0.33M), quadro-branco (0.15M), talking-head-cards (0.09M) |
|
|
35
36
|
| health / fitness / nutrition | explicador-animado (33.4M), podcast-clip (9.8M), quadro-branco (0.61M), macro-asmr (0.54M), talking-head-cards (0.43M), desafio-numero (0.22M) |
|
|
36
37
|
| food / recipes / lifestyle / travel | desafio-numero (107M), pov-vlog (8.7M), macro-asmr (7.7M), tour (1.05M) |
|
|
37
38
|
| beauty / fashion / local services (clinic, salon, architecture, real estate) | macro-asmr (10.6M), talking-head-legenda (3.8M), talking-head-cards (2.0M), pov-vlog (1.4M), tour (0.37M) |
|
|
38
|
-
| education / science / comedy | experimento (49M), explicador-animado (4.7M), esquete (2.9M), talking-head-cards (2.2M), talking-head-legenda (1.7M) |
|
|
39
|
+
| education / science / comedy | experimento (49M), explicador-animado (4.7M), esquete (2.9M), talking-head-cards (2.2M), talking-head-legenda (1.7M), tipografia-cinetica (0.93M) |
|
|
40
|
+
| music / artists / lyric videos | tipografia-cinetica (0.24M) |
|
|
39
41
|
|
|
40
42
|
BR local services (clinic, salon, law, architecture), BR travel and BR sales/marketing were filled with a second pass (300k to 10.6M views). Still thin: BR software/SaaS proper (only sales/marketing found), real-estate agents in BR, and every niche has 9 to 16 references, not hundreds. When a niche is missing, pick by material and by the closest niche, and say the reference is borrowed.
|
|
41
43
|
|
|
42
44
|
## 3. Goal → tie-breaker
|
|
43
45
|
|
|
44
46
|
- **Saves and comments (teach, explain a system):** motion-editorial, explicador-animado, quadro-branco, talking-head-cards.
|
|
45
|
-
- **Reach and shares (curiosity, humour):** desafio-numero, experimento, meme-rapido, esquete, pov-vlog.
|
|
47
|
+
- **Reach and shares (curiosity, humour):** desafio-numero, experimento, meme-rapido, esquete, pov-vlog, tipografia-cinetica (music and manifestos).
|
|
46
48
|
- **Trust and authority (a person the audience should know):** talking-head-legenda, podcast-clip, camera-no-setup.
|
|
47
49
|
- **Desire for a product:** macro-asmr, desafio-numero, camera-no-setup.
|
|
48
50
|
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# Kinetic typography and lyric video (mexicat P(doom), PixelValet, Ninh Truong)
|
|
2
|
+
|
|
3
|
+
Measured on 5 shorts (en) in: product / SaaS / marketing / business / law, education / science / comedy, music / artists / lyric videos.
|
|
4
|
+
|
|
5
|
+
## Measured
|
|
6
|
+
|
|
7
|
+
| Metric | Value |
|
|
8
|
+
|---|---|
|
|
9
|
+
| Reach (views) | max 1,215,529 · median 353,630 |
|
|
10
|
+
| Cuts per 10 s | min 0.63 · median 3.57 · max 5.54 |
|
|
11
|
+
| Median scene length | 1.54 s |
|
|
12
|
+
| Music bed (continuous / faint / none) | 3 / 0 / 2 |
|
|
13
|
+
| Cuts with a sound effect on them | 31/94 (33%) |
|
|
14
|
+
|
|
15
|
+
Cuts come from scene detection (it undercounts overlays and cards); music and SFX from an audio heuristic that classifies type, never names the sound ([../research-method.md](../research-method.md)).
|
|
16
|
+
|
|
17
|
+
## Material it needs
|
|
18
|
+
|
|
19
|
+
An audio track with words: a song with its lyrics, or a voice-over with its script. Nobody on camera, no footage. Everything is generated (HyperFrames, three.js, canvas). Word timing from ctc-align; a beat grid when there is music.
|
|
20
|
+
|
|
21
|
+
## When it fits
|
|
22
|
+
|
|
23
|
+
Music releases and lyric videos, manifestos, a strong opinion read aloud, brand promos built on a slogan, poetry. Shares and rewatches; weak for teaching step by step.
|
|
24
|
+
|
|
25
|
+
## Recipe
|
|
26
|
+
|
|
27
|
+
**Look**
|
|
28
|
+
|
|
29
|
+
- The words ARE the image: one keyword huge in the frame, the rest of the line small. No caption strip at the bottom.
|
|
30
|
+
- Plain background (black, or a flat colour that changes per section) or one generated world the camera travels through (a map, a grid in perspective, a 3D object the line wraps around).
|
|
31
|
+
- Each word is animated by what it means: PAIN breaks, CHEERFUL bounces, LIGHT lights up, a word curves around the object it names.
|
|
32
|
+
- Code-rendered pieces add film treatment: bloom, halation, grain, a thin HUD in mono.
|
|
33
|
+
|
|
34
|
+
**Palette:** Dark base (#0A0A0A or a deep teal #0F3B3A) plus ONE glowing accent (orange #FF6A1A in P(doom), coral in Maps) and off-white text. Flat-colour variant: one saturated background per word or section, text in white or ink.
|
|
35
|
+
|
|
36
|
+
**Typography:** Two or three families with high contrast: a heavy grotesk or condensed caps for the keyword (Archivo Black, Bebas Neue), a mono for small lines and HUD (IBM Plex Mono), optionally a serif italic for a soft word (Cormorant Garamond). Mix weights inside the same line.
|
|
37
|
+
|
|
38
|
+
**Motion**
|
|
39
|
+
|
|
40
|
+
- Words enter on the syllable they are sung or spoken (forced alignment, never guessed); the keyword lands on the beat.
|
|
41
|
+
- Scene changes snap to the downbeat. P(doom) changes every ~0.3 s at peaks; the calm variant is a single continuous camera move for a whole verse.
|
|
42
|
+
- The camera moves through the type (push, orbit, travel across a map) instead of the text sliding over a still frame.
|
|
43
|
+
- Hold the last word of a line a beat longer before it leaves.
|
|
44
|
+
|
|
45
|
+
**Captions:** None separate: the type is the caption. Keep the full line readable somewhere (small, mono, corner) when the keyword alone would lose the sense.
|
|
46
|
+
|
|
47
|
+
**Hook (0 to 3 s):** The first sung or spoken word, huge, in the accent colour, landing on the first downbeat (0 to 1 s). No logo intro.
|
|
48
|
+
|
|
49
|
+
**CTA:** The title or slogan locked up big and still for 1 to 2 s, the accent lit; one small line (artist, handle, link) under it.
|
|
50
|
+
|
|
51
|
+
**Music:** The track itself is the whole soundtrack. Lyric videos: song at full level, no extra bed. Voice-over promos: a beat-driven bed under the voice.
|
|
52
|
+
|
|
53
|
+
**SFX:** Song-driven pieces: almost none (5 of 56 cuts in P(doom), 0 of 2 in Maps). Promo variant: hits on every word change (9/13 and 16/20 cuts), which the heuristic partly confuses with the kick. Never add whooshes over a song.
|
|
54
|
+
|
|
55
|
+
## Do not
|
|
56
|
+
|
|
57
|
+
- Karaoke subtitles at the bottom over a static background: that is a lyric 'overlay', not this style.
|
|
58
|
+
- Guessing word timing by ear; align it (ctc-align) and snap scenes to the beat grid.
|
|
59
|
+
- More than one accent colour, or a different font for every word.
|
|
60
|
+
- Shipping 16:9: every measured reference is horizontal; recompose for 9:16 with the keyword in the upper-middle and nothing in the bottom 20%.
|
|
61
|
+
|
|
62
|
+
## Watch before editing
|
|
63
|
+
|
|
64
|
+
- [PiXeL ARTWORKS](https://www.youtube.com/watch?v=9ngNySFSWWU) · 1,215,529 views
|
|
65
|
+
- [PixelValet](https://www.youtube.com/watch?v=Kt8vGm8_ekQ) · 928,526 views
|
|
66
|
+
- [Cinthya Studios](https://www.youtube.com/watch?v=A0CKFb24MJ8) · 353,630 views
|
|
67
|
+
- [Ninh Truong](https://www.youtube.com/watch?v=FOMWS5pChuA) · 243,944 views
|
|
68
|
+
- [mexicat](https://www.youtube.com/watch?v=5EoO5413dBY) · 120,706 views
|
|
@@ -9,7 +9,8 @@ id="$1"; out="$2"
|
|
|
9
9
|
[[ "$id" =~ ^[A-Za-z0-9_-]{11}$ ]] || { echo "id inválido: $id" >&2; exit 2; }
|
|
10
10
|
mkdir -p "$out"; cd "$out"
|
|
11
11
|
f="./$id" # prefixo ./ : ID que começa com hífen não vira opção do ffmpeg
|
|
12
|
-
|
|
12
|
+
# .part: a first download that fails (HTTP 403) leaves it behind before the android fallback runs.
|
|
13
|
+
trap 'rm -f "$f.wav" "$f.mp4" "$f".*.part' EXIT
|
|
13
14
|
# Python with librosa + numpy: set VIDEO_STYLES_PY to a venv python, or python3 must have them.
|
|
14
15
|
PY="${VIDEO_STYLES_PY:-python3}"
|
|
15
16
|
"$PY" -c "import librosa, numpy" 2>/dev/null || { echo "precisa de librosa e numpy: pip install librosa numpy (ou VIDEO_STYLES_PY=/caminho/do/python)" >&2; exit 2; }
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"""Beat grid of a song: tempo, beats and downbeats, so scene cuts can snap to the music.
|
|
2
|
+
|
|
3
|
+
Usage: python3 beat_grid.py <audio_file> [--meter 4] [--fps 30] > grid.json
|
|
4
|
+
|
|
5
|
+
Prints JSON: {"bpm", "meter", "duration_s", "beats": [s...], "downbeats": [s...],
|
|
6
|
+
"downbeat_frames": [frame...]}. Needs librosa + numpy (same Python as analyze.sh;
|
|
7
|
+
set VIDEO_STYLES_PY and call it with that interpreter).
|
|
8
|
+
|
|
9
|
+
Downbeats are estimated, not detected: of the `meter` possible phases, the one
|
|
10
|
+
whose beats carry the most low-frequency onset energy is taken as beat 1. Check
|
|
11
|
+
two or three of them by ear before cutting a whole film on the grid.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
import argparse
|
|
15
|
+
import json
|
|
16
|
+
import sys
|
|
17
|
+
|
|
18
|
+
import librosa
|
|
19
|
+
import numpy as np
|
|
20
|
+
|
|
21
|
+
HOP = 512
|
|
22
|
+
LOW_HZ = 150
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def downbeat_phase(y_perc, sr, beat_frames, meter):
|
|
26
|
+
spec = np.abs(librosa.stft(y_perc, hop_length=HOP))
|
|
27
|
+
freqs = librosa.fft_frequencies(sr=sr)
|
|
28
|
+
low_energy = spec[freqs < LOW_HZ].sum(axis=0)
|
|
29
|
+
at_beats = low_energy[np.minimum(beat_frames, len(low_energy) - 1)]
|
|
30
|
+
scores = [at_beats[phase::meter].mean() if len(at_beats[phase::meter]) else 0.0 for phase in range(meter)]
|
|
31
|
+
return int(np.argmax(scores))
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def beat_grid(path, meter, fps):
|
|
35
|
+
y, sr = librosa.load(path, sr=22050, mono=True)
|
|
36
|
+
_, y_perc = librosa.effects.hpss(y)
|
|
37
|
+
_, beat_frames = librosa.beat.beat_track(y=y_perc, sr=sr, hop_length=HOP)
|
|
38
|
+
if len(beat_frames) < meter:
|
|
39
|
+
sys.exit(f"too few beats found in {path}: {len(beat_frames)} (is there a steady rhythm?)")
|
|
40
|
+
beats = librosa.frames_to_time(beat_frames, sr=sr, hop_length=HOP)
|
|
41
|
+
phase = downbeat_phase(y_perc, sr, beat_frames, meter)
|
|
42
|
+
downbeats = beats[phase::meter]
|
|
43
|
+
return {
|
|
44
|
+
# librosa's tempo and single beat intervals are quantised to the hop; the slope of a
|
|
45
|
+
# line fitted through every beat time averages that out.
|
|
46
|
+
"bpm": round(60.0 / float(np.polyfit(np.arange(len(beats)), beats, 1)[0]), 3),
|
|
47
|
+
"meter": meter,
|
|
48
|
+
"duration_s": round(len(y) / sr, 3),
|
|
49
|
+
"beats": [round(float(t), 3) for t in beats],
|
|
50
|
+
"downbeats": [round(float(t), 3) for t in downbeats],
|
|
51
|
+
"downbeat_frames": [int(round(t * fps)) for t in downbeats],
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def main():
|
|
56
|
+
parser = argparse.ArgumentParser(description=__doc__.split("\n")[0])
|
|
57
|
+
parser.add_argument("audio")
|
|
58
|
+
parser.add_argument("--meter", type=int, default=4)
|
|
59
|
+
parser.add_argument("--fps", type=int, default=30)
|
|
60
|
+
args = parser.parse_args()
|
|
61
|
+
if args.meter < 2 or args.fps < 1:
|
|
62
|
+
sys.exit("--meter must be >= 2 and --fps >= 1")
|
|
63
|
+
json.dump(beat_grid(args.audio, args.meter, args.fps), sys.stdout, indent=1)
|
|
64
|
+
print()
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
if __name__ == "__main__":
|
|
68
|
+
main()
|