@facelessad/cli 1.0.1 → 1.1.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/README.md +45 -2
- package/index.js +58 -12
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -18,13 +18,56 @@ The key is stored in `~/.facelessad/config.json` with mode 0600. The
|
|
|
18
18
|
environment variable `FACELESSAD_API_KEY` always wins — that is the way CI
|
|
19
19
|
should authenticate. `FACELESSAD_API_URL` overrides the API base.
|
|
20
20
|
|
|
21
|
-
`--wait` polls and prints phase + elapsed time, and
|
|
22
|
-
|
|
21
|
+
`--wait` polls and prints phase + elapsed time, and stops watching after 6
|
|
22
|
+
hours (`--timeout <seconds>` changes that; `--timeout 0` waits with no
|
|
23
|
+
limit). **Stopping the wait never cancels the build** — API videos run on a
|
|
24
|
+
bulk lane that can take hours when the queue is busy, and they keep going. A video whose status is
|
|
23
25
|
`draft` is not building and will never finish on its own, so `--wait`
|
|
24
26
|
stops on it immediately instead of polling forever. For long-running
|
|
25
27
|
builds a webhook is still the better tool — a stuck shell is a stuck CI
|
|
26
28
|
job. See https://facelessad.com/developers → Webhooks.
|
|
27
29
|
|
|
30
|
+
## Custom style (1.1.0)
|
|
31
|
+
|
|
32
|
+
Most tools accept a look of your own instead of one of the built-in styles.
|
|
33
|
+
`facelessad tools` marks which ones (`supports.customStyle`):
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
facelessad create --tool motion-graphics --url https://your-product.com \
|
|
37
|
+
--style custom \
|
|
38
|
+
--custom-style "Retro terminal — phosphor green on near-black, scan lines, chunky mono type" --wait
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
What to describe depends on what the tool builds. Animated Ad, Character,
|
|
42
|
+
Music Video, Inspiration, Motivational and Slideshow render images, so
|
|
43
|
+
describe the illustration or cinematic style. Motion Graphics, SaaS UI Ad,
|
|
44
|
+
Text Animation and Video Banner generate their graphics as code, so describe
|
|
45
|
+
typography, palette and motion. Crude Drawings has no custom style.
|
|
46
|
+
|
|
47
|
+
Product Showcase builds both — cinematic clips animated from your real
|
|
48
|
+
product photo, and text graphics drawn in code on top — so it takes two
|
|
49
|
+
descriptions:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
facelessad create --tool product-showcase --product-image https://.../bottle.jpg \
|
|
53
|
+
--text "A steel bottle that keeps drinks cold for 24 hours" \
|
|
54
|
+
--style custom \
|
|
55
|
+
--custom-style "Frozen tundra — cracked ice, pale blue rim light, slow orbiting camera" \
|
|
56
|
+
--custom-graphics-style "Deep navy typography, thin white hairlines — the video reads light" --wait
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Leave `--custom-graphics-style` out and it is written for you from the first
|
|
60
|
+
description.
|
|
61
|
+
|
|
62
|
+
Your text is expanded into a full style specification before the video is
|
|
63
|
+
built. `facelessad status <id> --json` returns the expanded text as
|
|
64
|
+
`customStyle`; feed it back with `--no-refine-style` to give a whole
|
|
65
|
+
campaign one identical look.
|
|
66
|
+
|
|
67
|
+
`--style-hint` is now an alias: with no `--style`, it is treated exactly like
|
|
68
|
+
`--custom-style`. Before 1.1.0 it only reached the script writer and could
|
|
69
|
+
not change how the video looked.
|
|
70
|
+
|
|
28
71
|
## Flag spellings (1.0.1)
|
|
29
72
|
|
|
30
73
|
Both spellings are accepted, because the older ones appeared on the
|
package/index.js
CHANGED
|
@@ -111,6 +111,10 @@ if (flags['no-music'] === true) { flags.music = 'false'; delete flags['no-music'
|
|
|
111
111
|
*/
|
|
112
112
|
const KNOWN_FLAGS = new Set([
|
|
113
113
|
'tool', 'url', 'text', 'duration', 'aspect', 'language', 'style', 'style-hint',
|
|
114
|
+
// 1.1.0 (§673): custom style. --style custom yksin ei riita — palvelin
|
|
115
|
+
// vastaa custom_style_required, ja se on tarkoitus: tyylin sisalto ON
|
|
116
|
+
// kayttajan teksti.
|
|
117
|
+
'custom-style', 'custom-graphics-style', 'no-refine-style',
|
|
114
118
|
'structure', 'hook', 'video-mode', 'brand-color', 'brand-name', 'cta', 'name',
|
|
115
119
|
'voice', 'gender', 'no-voice', 'music', 'sfx', 'captions', 'product-image',
|
|
116
120
|
'no-brand-kit', 'no-winners', 'brand-kit',
|
|
@@ -220,6 +224,13 @@ function buildBody() {
|
|
|
220
224
|
set('language', flags.language !== undefined ? String(flags.language) : undefined);
|
|
221
225
|
set('style', flags.style !== undefined ? String(flags.style) : undefined);
|
|
222
226
|
set('style_hint', flags['style-hint'] !== undefined ? String(flags['style-hint']) : undefined);
|
|
227
|
+
// 1.1.0 (§673): oma tyyli. --custom-style on ainoa lippu joka oikeasti
|
|
228
|
+
// vaihtaa videon ilmeen; --style-hint on nykyaan sen alias silloin kun
|
|
229
|
+
// --style on antamatta. --custom-graphics-style koskee vain
|
|
230
|
+
// product-showcasea (video + sen paalle koodilla piirretyt grafiikat).
|
|
231
|
+
set('custom_style', flags['custom-style'] !== undefined ? String(flags['custom-style']) : undefined);
|
|
232
|
+
set('custom_graphics_style', flags['custom-graphics-style'] !== undefined ? String(flags['custom-graphics-style']) : undefined);
|
|
233
|
+
if (flags['no-refine-style'] === true) body.custom_style_refine = false;
|
|
223
234
|
set('ad_structure', flags.structure !== undefined ? String(flags.structure) : undefined);
|
|
224
235
|
set('hook_formula', flags.hook !== undefined ? String(flags.hook) : undefined);
|
|
225
236
|
set('video_mode', flags['video-mode'] !== undefined ? String(flags['video-mode']) : undefined);
|
|
@@ -392,13 +403,18 @@ Commands:
|
|
|
392
403
|
|
|
393
404
|
Create flags:
|
|
394
405
|
--tool --url --text --duration --aspect --language --style --style-hint
|
|
406
|
+
--custom-style "<look you want>" (with --style custom; see facelessad tools)
|
|
407
|
+
--custom-graphics-style "<...>" (product-showcase only)
|
|
408
|
+
--no-refine-style (use your text verbatim, don't expand it)
|
|
395
409
|
--structure --hook --video-mode --brand-color --brand-name --cta --name
|
|
396
410
|
--voice --gender --no-voice --music --sfx --captions --product-image
|
|
397
411
|
--no-brand-kit --no-winners --brand-kit <id> (see: facelessad brands)
|
|
398
412
|
--file body.json (base body; flags override) --dry-run (print, don't send)
|
|
399
413
|
|
|
400
|
-
--wait
|
|
401
|
-
|
|
414
|
+
--wait stops watching after 6 hours; --timeout <seconds> changes that and
|
|
415
|
+
--timeout 0 waits with no limit. Stopping the wait never cancels the build.
|
|
416
|
+
A video in "draft" is not building and is never waited on. For overnight
|
|
417
|
+
runs a webhook beats leaving a terminal open — see ${API}/developers.
|
|
402
418
|
|
|
403
419
|
Accepted spellings: --brand = --brand-name, --color = --brand-color,
|
|
404
420
|
--voice-id = --voice, --voice-gender = --gender, --no-voice-over = --no-voice,
|
|
@@ -412,10 +428,26 @@ see ${API}/developers.
|
|
|
412
428
|
},
|
|
413
429
|
};
|
|
414
430
|
|
|
431
|
+
/**
|
|
432
|
+
* 1.0.2 — kaksi siistimistä ihmisluettavaan tulosteeseen.
|
|
433
|
+
*
|
|
434
|
+
* 1. VAIHE PIILOON KUN VALMIS. Rivi luki "done (render_queued)", mikä näyttää
|
|
435
|
+
* ristiriitaiselta. Se ei ole vika: final-render menee omaan jonoonsa eikä
|
|
436
|
+
* sen valmistuminen kirjoita terminaalivaihetta Redisiin (§471 rakentaa
|
|
437
|
+
* jonokaton laskennan tämän varaan), joten `phase` jää viimeiseen
|
|
438
|
+
* kirjoitettuun arvoon. `status` on silti oikein. Kun ajo on päättynyt,
|
|
439
|
+
* vaihe ei kerro mitään hyödyllistä, joten sitä ei näytetä.
|
|
440
|
+
* 2. PRESIGNED-URL EI IHMISELLE. Se on ~500 merkkiä ja rivittyy terminaalissa
|
|
441
|
+
* lukukelvottomaksi, eikä sitä voi lyhentää — allekirjoitus on osa URLia ja
|
|
442
|
+
* katkaistu linkki ei toimi. Ihmiselle näytetään komento joka hakee saman
|
|
443
|
+
* tiedoston; koneelle `--json` kantaa `url`-kentän ennallaan.
|
|
444
|
+
*/
|
|
415
445
|
function statusLine(d) {
|
|
446
|
+
const done = d.status === 'done' || d.status === 'failed' || d.status === 'draft';
|
|
416
447
|
let s = 'Video ' + d.id + ': ' + bold(d.status);
|
|
417
|
-
if (d.phase) s += ' ' + dim('(' + d.phase + ')');
|
|
418
|
-
if (d.url) s += '\
|
|
448
|
+
if (d.phase && !done) s += ' ' + dim('(' + d.phase + ')');
|
|
449
|
+
if (d.url) s += '\n' + dim('Download: ') + 'facelessad download ' + d.id + ' --out ad.mp4'
|
|
450
|
+
+ '\n' + dim('(the direct link is in --json output; it expires in ~1 h)');
|
|
419
451
|
if (d.error) s += '\n' + d.error;
|
|
420
452
|
return s;
|
|
421
453
|
}
|
|
@@ -436,7 +468,20 @@ function statusLine(d) {
|
|
|
436
468
|
* Oletuskatto on 30 min; --timeout <sekuntia> muuttaa sen. Aikakatto poistuu
|
|
437
469
|
* koodilla 1, koska pipeline ei saa jatkaa videolla jota ei ole.
|
|
438
470
|
*/
|
|
439
|
-
|
|
471
|
+
/**
|
|
472
|
+
* 1.0.2 — aikakatto uusiksi. 1.0.1:n 30 min oli liian tiukka: API-ajot menevät
|
|
473
|
+
* bulk-kaistalle joka etenee vasta kun käyttöliittymän jonot ovat tyhjät, ja
|
|
474
|
+
* kuvamallit ovat ajoittain hyvin hitaita. Yön yli jonottaminen on
|
|
475
|
+
* nimenomainen tuotelupaus, eikä odotuskomennon oletus saa olla sitä vastaan.
|
|
476
|
+
*
|
|
477
|
+
* Oletus on nyt 6 h ja `--timeout 0` odottaa rajattomasti. Rajaton on
|
|
478
|
+
* SALLITTU mutta ei oletus: 1.0.0:n ikuinen silmukka oli vika koska se ei
|
|
479
|
+
* ollut kenenkään valinta — nyt se on.
|
|
480
|
+
*
|
|
481
|
+
* Aikakatto EI peruuta buildia. Se lopettaa vain odottamisen, ja viesti sanoo
|
|
482
|
+
* sen. Yön yli -ajoihin oikea työkalu on silti webhook, ei auki jätetty pääte.
|
|
483
|
+
*/
|
|
484
|
+
const WAIT_TIMEOUT_DEFAULT = 21600;
|
|
440
485
|
const POLL_SECONDS = 5;
|
|
441
486
|
|
|
442
487
|
async function waitFor(id) {
|
|
@@ -444,15 +489,15 @@ async function waitFor(id) {
|
|
|
444
489
|
// 1.0.1: --timeout luetaan sellaisenaan. Ensimmäinen versio tästä pakotti
|
|
445
490
|
// lattian 30 s:aan, jolloin `--timeout 12` odotti 30 s eikä kertonut siitä
|
|
446
491
|
// — sama hiljainen ylikirjoitus jota tämä paketti on täynnä korjaamassa.
|
|
447
|
-
// Nyt kelpaamaton arvo on VIRHE ja kelvollinen tehdään sellaisenaan
|
|
448
|
-
// alaraja on yksi pollausväli, koska sitä lyhyempi ei ehdi kysyä kertaakaan.
|
|
492
|
+
// Nyt kelpaamaton arvo on VIRHE ja kelvollinen tehdään sellaisenaan.
|
|
449
493
|
let limit = WAIT_TIMEOUT_DEFAULT;
|
|
450
494
|
if (flags.timeout !== undefined && flags.timeout !== true) {
|
|
451
495
|
limit = Number(flags.timeout);
|
|
452
|
-
if (!Number.isFinite(limit) || limit < POLL_SECONDS) {
|
|
453
|
-
die('--timeout must be
|
|
496
|
+
if (!Number.isFinite(limit) || (limit !== 0 && limit < POLL_SECONDS)) {
|
|
497
|
+
die('--timeout must be 0 (wait forever) or at least ' + POLL_SECONDS + ' seconds', 'invalid_timeout');
|
|
454
498
|
}
|
|
455
499
|
}
|
|
500
|
+
const noLimit = limit === 0;
|
|
456
501
|
let last = '';
|
|
457
502
|
for (;;) {
|
|
458
503
|
const d = await call('GET', '/api/v1/videos/' + encodeURIComponent(id));
|
|
@@ -475,9 +520,10 @@ async function waitFor(id) {
|
|
|
475
520
|
else { process.stderr.write('Error: ' + msg + '\n'); }
|
|
476
521
|
process.exit(1);
|
|
477
522
|
}
|
|
478
|
-
if (elapsed + POLL_SECONDS > limit) {
|
|
479
|
-
const msg = '
|
|
480
|
-
+ (d.phase ? ' — ' + d.phase : '') + '). The build
|
|
523
|
+
if (!noLimit && elapsed + POLL_SECONDS > limit) {
|
|
524
|
+
const msg = 'Stopped waiting for video ' + id + ' after ' + elapsed + 's (last status: ' + d.status
|
|
525
|
+
+ (d.phase ? ' — ' + d.phase : '') + '). The build was NOT cancelled and is still running — '
|
|
526
|
+
+ 'check it with: facelessad status ' + id + ' (or wait longer with --timeout 0)';
|
|
481
527
|
if (asJson) { process.stdout.write(JSON.stringify({ ...d, ok: false, code: 'wait_timeout' }) + '\n'); }
|
|
482
528
|
else { process.stderr.write('Error: ' + msg + '\n' + dim('wait_timeout') + '\n'); }
|
|
483
529
|
process.exit(1);
|