@facelessad/cli 1.0.1 → 1.0.2

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 (3) hide show
  1. package/README.md +4 -2
  2. package/index.js +44 -12
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -18,8 +18,10 @@ 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 gives up after 30
22
- minutes (`--timeout <seconds>` changes that). A video whose status is
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
package/index.js CHANGED
@@ -397,8 +397,10 @@ Create flags:
397
397
  --no-brand-kit --no-winners --brand-kit <id> (see: facelessad brands)
398
398
  --file body.json (base body; flags override) --dry-run (print, don't send)
399
399
 
400
- --wait gives up after 30 minutes; --timeout <seconds> changes that. A video
401
- in "draft" is not building and is never waited on.
400
+ --wait stops watching after 6 hours; --timeout <seconds> changes that and
401
+ --timeout 0 waits with no limit. Stopping the wait never cancels the build.
402
+ A video in "draft" is not building and is never waited on. For overnight
403
+ runs a webhook beats leaving a terminal open — see ${API}/developers.
402
404
 
403
405
  Accepted spellings: --brand = --brand-name, --color = --brand-color,
404
406
  --voice-id = --voice, --voice-gender = --gender, --no-voice-over = --no-voice,
@@ -412,10 +414,26 @@ see ${API}/developers.
412
414
  },
413
415
  };
414
416
 
417
+ /**
418
+ * 1.0.2 — kaksi siistimistä ihmisluettavaan tulosteeseen.
419
+ *
420
+ * 1. VAIHE PIILOON KUN VALMIS. Rivi luki "done (render_queued)", mikä näyttää
421
+ * ristiriitaiselta. Se ei ole vika: final-render menee omaan jonoonsa eikä
422
+ * sen valmistuminen kirjoita terminaalivaihetta Redisiin (§471 rakentaa
423
+ * jonokaton laskennan tämän varaan), joten `phase` jää viimeiseen
424
+ * kirjoitettuun arvoon. `status` on silti oikein. Kun ajo on päättynyt,
425
+ * vaihe ei kerro mitään hyödyllistä, joten sitä ei näytetä.
426
+ * 2. PRESIGNED-URL EI IHMISELLE. Se on ~500 merkkiä ja rivittyy terminaalissa
427
+ * lukukelvottomaksi, eikä sitä voi lyhentää — allekirjoitus on osa URLia ja
428
+ * katkaistu linkki ei toimi. Ihmiselle näytetään komento joka hakee saman
429
+ * tiedoston; koneelle `--json` kantaa `url`-kentän ennallaan.
430
+ */
415
431
  function statusLine(d) {
432
+ const done = d.status === 'done' || d.status === 'failed' || d.status === 'draft';
416
433
  let s = 'Video ' + d.id + ': ' + bold(d.status);
417
- if (d.phase) s += ' ' + dim('(' + d.phase + ')');
418
- if (d.url) s += '\nURL (valid ~1 h): ' + d.url;
434
+ if (d.phase && !done) s += ' ' + dim('(' + d.phase + ')');
435
+ if (d.url) s += '\n' + dim('Download: ') + 'facelessad download ' + d.id + ' --out ad.mp4'
436
+ + '\n' + dim('(the direct link is in --json output; it expires in ~1 h)');
419
437
  if (d.error) s += '\n' + d.error;
420
438
  return s;
421
439
  }
@@ -436,7 +454,20 @@ function statusLine(d) {
436
454
  * Oletuskatto on 30 min; --timeout <sekuntia> muuttaa sen. Aikakatto poistuu
437
455
  * koodilla 1, koska pipeline ei saa jatkaa videolla jota ei ole.
438
456
  */
439
- const WAIT_TIMEOUT_DEFAULT = 1800;
457
+ /**
458
+ * 1.0.2 — aikakatto uusiksi. 1.0.1:n 30 min oli liian tiukka: API-ajot menevät
459
+ * bulk-kaistalle joka etenee vasta kun käyttöliittymän jonot ovat tyhjät, ja
460
+ * kuvamallit ovat ajoittain hyvin hitaita. Yön yli jonottaminen on
461
+ * nimenomainen tuotelupaus, eikä odotuskomennon oletus saa olla sitä vastaan.
462
+ *
463
+ * Oletus on nyt 6 h ja `--timeout 0` odottaa rajattomasti. Rajaton on
464
+ * SALLITTU mutta ei oletus: 1.0.0:n ikuinen silmukka oli vika koska se ei
465
+ * ollut kenenkään valinta — nyt se on.
466
+ *
467
+ * Aikakatto EI peruuta buildia. Se lopettaa vain odottamisen, ja viesti sanoo
468
+ * sen. Yön yli -ajoihin oikea työkalu on silti webhook, ei auki jätetty pääte.
469
+ */
470
+ const WAIT_TIMEOUT_DEFAULT = 21600;
440
471
  const POLL_SECONDS = 5;
441
472
 
442
473
  async function waitFor(id) {
@@ -444,15 +475,15 @@ async function waitFor(id) {
444
475
  // 1.0.1: --timeout luetaan sellaisenaan. Ensimmäinen versio tästä pakotti
445
476
  // lattian 30 s:aan, jolloin `--timeout 12` odotti 30 s eikä kertonut siitä
446
477
  // — 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.
478
+ // Nyt kelpaamaton arvo on VIRHE ja kelvollinen tehdään sellaisenaan.
449
479
  let limit = WAIT_TIMEOUT_DEFAULT;
450
480
  if (flags.timeout !== undefined && flags.timeout !== true) {
451
481
  limit = Number(flags.timeout);
452
- if (!Number.isFinite(limit) || limit < POLL_SECONDS) {
453
- die('--timeout must be a number of seconds, at least ' + POLL_SECONDS, 'invalid_timeout');
482
+ if (!Number.isFinite(limit) || (limit !== 0 && limit < POLL_SECONDS)) {
483
+ die('--timeout must be 0 (wait forever) or at least ' + POLL_SECONDS + ' seconds', 'invalid_timeout');
454
484
  }
455
485
  }
486
+ const noLimit = limit === 0;
456
487
  let last = '';
457
488
  for (;;) {
458
489
  const d = await call('GET', '/api/v1/videos/' + encodeURIComponent(id));
@@ -475,9 +506,10 @@ async function waitFor(id) {
475
506
  else { process.stderr.write('Error: ' + msg + '\n'); }
476
507
  process.exit(1);
477
508
  }
478
- if (elapsed + POLL_SECONDS > limit) {
479
- const msg = 'Gave up waiting for video ' + id + ' after ' + elapsed + 's (last status: ' + d.status
480
- + (d.phase ? ' — ' + d.phase : '') + '). The build may still finish; check with: facelessad status ' + id;
509
+ if (!noLimit && elapsed + POLL_SECONDS > limit) {
510
+ const msg = 'Stopped waiting for video ' + id + ' after ' + elapsed + 's (last status: ' + d.status
511
+ + (d.phase ? ' — ' + d.phase : '') + '). The build was NOT cancelled and is still running '
512
+ + 'check it with: facelessad status ' + id + ' (or wait longer with --timeout 0)';
481
513
  if (asJson) { process.stdout.write(JSON.stringify({ ...d, ok: false, code: 'wait_timeout' }) + '\n'); }
482
514
  else { process.stderr.write('Error: ' + msg + '\n' + dim('wait_timeout') + '\n'); }
483
515
  process.exit(1);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@facelessad/cli",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "description": "Create faceless video ads from your terminal or build scripts — the FacelessAd command line.",
5
5
  "license": "MIT",
6
6
  "type": "module",