@officexapp/vidfarm-devcli 0.21.59 โ†’ 0.21.61

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.
@@ -54,7 +54,7 @@ Two ingredients, no more:
54
54
 
55
55
  | Ingredient | What it is | Where it comes from |
56
56
  |---|---|---|
57
- | ๐ŸŽฌ **The meme** | A borrowed clip everyone already half-recognises, doing one absurd physical thing | `vidfarm.cc/explore` โ†’ public raws, `sourceType: MemeScreens` (851 of them, once you page past what the CLI shows you) |
57
+ | ๐ŸŽฌ **The meme** | A borrowed clip everyone already half-recognises, doing one absurd physical thing | `vidfarm.cc/explore` โ†’ public raws, `sourceType: MemeScreens` (851 of them โ€” reached with `vector_q`, **not** with the CLI, which shows you 200 and does not say so) |
58
58
  | โœ๏ธ **The caption** | One sentence naming a specific lived experience, in the second person or the first | You. This is the only original work in the video, and it is 90% of the outcome |
59
59
 
60
60
  **The offer is named on the video.** First choice: inside the caption, as a character in the setup
@@ -446,43 +446,201 @@ it is the cheapest route out of a caption that got safe.
446
446
 
447
447
  ## Casting the meme
448
448
 
449
- ### Browse the shelf. Do not search it.
449
+ ### Two channels: the slug is the IDENTITY, the vector is the BODY
450
+
451
+ The shelf answers two completely different questions and it answers them through two different
452
+ fields. Casting badly is almost always the result of asking one of them the other's question.
453
+
454
+ | You want to know | Ask | Why |
455
+ |---|---|---|
456
+ | **which meme is this** | the `slugId` | The AI descriptions are blind to the meme's identity โ€” see the warning below |
457
+ | **what does the body DO** | `vector_q`, semantic search | The embedding is built from the description **plus the action / emotion / motion / subject tags**, which is exactly the verb axis this format casts on |
450
458
 
451
459
  > โš ๏ธ **The descriptions on this shelf were written by a model that has never heard of the meme.**
452
- > This is not a small problem โ€” it is the defining fact of sourcing this format.
453
- >
454
460
  > `my-brain-every-10-seconds` is described as *"A man in a suit gestures and looks at the camera
455
461
  > with a neutral expression."* `jonah-jameson-laugh-you-serious` is described as *"A solid green
456
- > screen background."* A semantic query for `hungry food eating` returns **zero results** on a shelf
457
- > that contains a dozen clips of people eating.
462
+ > screen background."*
458
463
  >
459
- > **The `slugId` is the index. The description is noise.** Pull the whole shelf as JSON, read the
460
- > slugs, and only look at descriptions to break a tie.
464
+ > **The `slugId` is the index of identity. The description is noise about identity.** But that same
465
+ > description says *what the body is doing*, and **that is the only thing this format casts on.** A
466
+ > model that does not know it is looking at J. Jonah Jameson can still tell you the man is laughing
467
+ > in your face โ€” so the descriptions are worthless for the question you were not going to ask and
468
+ > useful for the one you were.
469
+
470
+ #### ๐Ÿšจ `--query` is LEXICAL. Semantic search is a different parameter, and the CLI has no flag for it
471
+
472
+ This is the single most expensive mistake in sourcing this format, and an earlier draft of this file
473
+ made it and drew the wrong conclusion from it.
474
+
475
+ `vidfarm public-raws --query "โ€ฆ"` sends `?q=`, and `q` is a **substring filter that runs first**. It
476
+ throws away every clip whose text does not literally contain your words, and only *then* does the
477
+ server rank whatever survived. On a shelf whose descriptions never use your vocabulary, the filter
478
+ empties the set before the ranking gets to see it โ€” so a perfectly good semantic query returns
479
+ nothing, and it looks like semantic search is broken. It is not. It never ran.
461
480
 
462
481
  ```bash
463
- vidfarm public-raws --categories # the shelves + live counts
464
- vidfarm public-raws --category greenscreen --limit 200 # 193 of these 200 are MemeScreens
465
- # ...and this is only the FIRST 200. See below
466
- vidfarm public-raws --category text-graphics --limit 200
467
- vidfarm public-raws --category lifestyle --limit 200 # also almost entirely MemeScreens
482
+ # โŒ lexical filter. Measured: 0 results, on a shelf full of blank stares
483
+ vidfarm api GET "/api/v1/public-raws?category=greenscreen&limit=8&q=blank%20stare%20confused"
484
+
485
+ # โœ… semantic. Same shelf, same words, ranked by MEANING
486
+ vidfarm api GET "/api/v1/public-raws?category=greenscreen&limit=8&vector_q=blank%20stare%20confused%20no%20comprehension"
487
+ # cat-staring ยท mrbean-wtf-greenscreen ยท james-franco-shocked ยท spongebob-panting โ€ฆ
468
488
  ```
469
489
 
490
+ > **Pass `vector_q` and do NOT pass `q`.** The server defaults `vector_q` to `q` when you omit it,
491
+ > which means the CLI's `--query` gets you the lexical filter *and* the ranking โ€” and the filter has
492
+ > already deleted the answer. Sending only `vector_q` skips the filter and ranks the whole shelf.
493
+ >
494
+ > **`vidfarm public-raws` cannot do this.** It maps `--query` to `q` and has no vector flag. Every
495
+ > semantic cast in this file goes through `vidfarm api GET`.
496
+
497
+ **It needs a login and an embedding key, and it fails SILENTLY without one.** The ranking requires a
498
+ logged-in customer with a saved gemini or openai provider key. With no key โ€” or logged out โ€” the
499
+ server skips the ranking, returns **HTTP 200 with a full page of results**, and says nothing. You
500
+ get the shelf in recency order wearing the costume of a search result.
501
+
502
+ ```bash
503
+ vidfarm whoami # must show a login
504
+ vidfarm api GET /api/v1/user/me/provider-keys | grep -iE "gemini|openai"
505
+ ```
506
+
507
+ **Confirm the ranking is live before you trust a cast:** query `arms up jumping celebration confetti`
508
+ and query `shoulders drop giving up defeated sigh`. If the top result is the same clip for both, the
509
+ embedder is not running and you are reading a recency list.
510
+
511
+ #### Semantic search reaches the whole shelf. The CLI reaches a quarter of it
512
+
470
513
  > ๐Ÿšจ **`vidfarm public-raws` shows you 200 clips and stops, and it does not tell you.** The server
471
514
  > caps a shelf at 200 per call and returns a `next_cursor` โ€” but the CLI has **no `--cursor` flag**,
472
- > so raising `--limit` to 1200 still returns exactly 200. Paging the same shelves through
473
- > `vidfarm api GET` instead turns **200 clips into 959** (851 of them MemeScreens). Cast from the
474
- > first 200 and you are picking from under a quarter of the library, with nothing anywhere saying so.
475
- >
476
- > **Build the full index once (Appendix C) and cast from the TSV, not from the CLI.**
515
+ > so raising `--limit` to 1200 still returns exactly 200.
516
+
517
+ `vector_q` on a **curated shelf** (`greenscreen`, `reaction`, `lifestyle`, `text-graphics`, โ€ฆ) is
518
+ not a rerank of that 200. The server scans **1500 records** for a curated category and ranks all of
519
+ them, so one call sees essentially the entire MemeScreens library and hands you its top N.
520
+
521
+ Measured on the `greenscreen` shelf, ten verb queries against one plain `--limit 200` page:
522
+
523
+ | | |
524
+ |---|---|
525
+ | Distinct clips the ten queries returned | **143** |
526
+ | Of those, **not present** in the CLI's first 200 | **93 โ€” 65%** |
527
+ | Of those, hallucinated / not really on the shelf | **0.** All 143 verified against the fully-paged 693 |
528
+
529
+ **Two thirds of what semantic search finds is invisible to the CLI**, and the two thirds it hides
530
+ are not the boring ones โ€” they are the animal and anonymous clips (`cat-staring`, `cat-sleeping`,
531
+ `hangover-dog`) that are ๐ŸŸข **safe tier**, which is the tier client work is supposed to default to.
532
+ Casting from the first 200 quietly pushes you toward the celebrity clips you are not allowed to use.
533
+
534
+ ```bash
535
+ vidfarm public-raws --categories # the shelves + live counts. Start here
536
+ vidfarm public-raws --category greenscreen --limit 200 # the first 200 only โ€” for a browse, not a cast
537
+ ```
477
538
 
478
539
  **Every shelf the CLI exposes is dominated by MemeScreens** โ€” `b-roll`, `lifestyle` and
479
540
  `text-graphics` all come back as greenscreen memes. Good news for this format, bad news for
480
541
  backgrounds (see below).
481
542
 
482
- > **Delivery work: open your delivery log in the same breath as the shelf.** Anything you already
543
+ ### The diversity sweep โ€” one query per verb, then dedupe
544
+
545
+ > **A single vector query is a relevance tool. It is the OPPOSITE of a diversity tool.** It returns
546
+ > the twenty clips nearest one point, and near-neighbours are near-duplicates: four `cat-sitting`
547
+ > variants of the same cat. Cast ten videos off one query and you will ship ten videos that look
548
+ > like one video.
549
+
550
+ Diversity comes from **querying ten different points and taking the union.** The ten points are
551
+ already written down โ€” they are the ten verb tokens in the table below, which is why the list is
552
+ closed. One query per verb, top 20 each, deduped, is a 143-clip casting pool spread across the whole
553
+ shelf by meaning instead of by upload date.
554
+
555
+ ```python
556
+ #!/usr/bin/env python3
557
+ """meme-sweep.py โ€” one vector query per verb token -> a deduped casting pool.
558
+
559
+ Needs a vidfarm login + a saved gemini/openai key, or the ranking silently
560
+ does not happen (see above). Run it once per session; it is ~10 API calls.
561
+ """
562
+ import json, subprocess, urllib.parse
563
+
564
+ VERBS = {
565
+ "deflate": "shoulders drop eyes go flat accepting a bad outcome without a fight",
566
+ "blank": "total non-comprehension head tilt dead eyes nothing processing",
567
+ "loop": "the same thought going round again mechanically forever repetitive",
568
+ "smug": "arms folded self-satisfied already solved it insufferable",
569
+ "overreact": "full body screaming sobbing flailing at something trivial",
570
+ "correct": "turning to camera to tell you patiently that you are wrong",
571
+ "unbothered": "completely relaxed doing nothing while chaos happens calm",
572
+ "celebrate": "arms up jumping celebration confetti scale joy",
573
+ "stare": "holding eye contact with the camera saying nothing silent",
574
+ "wait": "still waiting visibly longer than is reasonable impatient",
575
+ }
576
+ SHELVES = ("greenscreen", "reaction", "lifestyle", "text-graphics")
577
+
578
+ def get(path):
579
+ out = subprocess.run(["vidfarm", "api", "GET", path], capture_output=True, text=True).stdout
580
+ brace = out.find("{") # first line is a coloured "GET โ€ฆ โ†’ 200" status line
581
+ return json.JSONDecoder().raw_decode(out[brace:])[0] if brace >= 0 else {}
582
+
583
+ pool, tops = {}, {}
584
+ for shelf in SHELVES:
585
+ for verb, q in VERBS.items():
586
+ d = get(f"/api/v1/public-raws?category={shelf}&limit=20"
587
+ f"&vector_q={urllib.parse.quote(q)}") # vector_q ONLY. Never q
588
+ raws = d.get("raws", [])
589
+ if raws:
590
+ tops.setdefault(shelf, []).append(raws[0]["rawId"])
591
+ for rank, raw in enumerate(raws):
592
+ slug = raw.get("slugId") or raw["rawId"]
593
+ pool.setdefault(slug, {"verb": verb, "rank": rank, "id": raw["rawId"],
594
+ "desc": (raw.get("description") or "")[:90]})
595
+
596
+ # The ranking fails SILENTLY (no login / no embedding key) and still returns a
597
+ # full page. The only reliable tell is differential: ten different queries that
598
+ # rank must not all return the same clip first. Do NOT test this by looking for
599
+ # a slugId โ€” several shelves legitimately return bare UUIDs.
600
+ for shelf, first in tops.items():
601
+ if len(set(first)) < len(first) / 2:
602
+ raise SystemExit(f"{shelf}: {len(set(first))} distinct top hits from {len(first)} queries โ€” "
603
+ "the embedder is not running. Check `vidfarm whoami` and your gemini/openai key.")
604
+
605
+ with open("casting-pool.tsv", "w") as f:
606
+ f.write("slug\tverb\trank\trawId\tdescription\n")
607
+ for slug, v in sorted(pool.items(), key=lambda kv: (kv[1]["verb"], kv[1]["rank"])):
608
+ f.write(f"{slug}\t{v['verb']}\t{v['rank']}\t{v['id']}\t{v['desc']}\n")
609
+ print(len(pool), "distinct clips -> casting-pool.tsv")
610
+ ```
611
+
612
+ **Then cast off the TSV, and cast off a verb you have not shipped this week:**
613
+
614
+ ```bash
615
+ awk -F'\t' '$2=="unbothered"' casting-pool.tsv | cut -f1,5 # the whole neighbourhood, one verb
616
+ cut -f2 casting-pool.tsv | sort | uniq -c # how deep each verb actually goes
617
+ ```
618
+
619
+ **Three rules for keeping the pool diverse, all of them cheap:**
620
+
621
+ 1. **Drop the near-duplicate families.** `cat-sitting-5e58โ€ฆ` and `cat-sitting-8eddโ€ฆ` are one casting
622
+ choice, not two. Collapse on the slug stem (the slug minus its trailing hex) before you count
623
+ your options, or you will believe you have 143 and be choosing among about 110.
624
+ 2. **Take the top ~20 per verb, never the top 60 of one.** The tail of one query is further from
625
+ that verb than the head of the next query is โ€” depth on one point buys worse clips, breadth
626
+ across points buys better ones.
627
+ 3. **Subtract the delivery log before you look, not after.** `join` the pool against
628
+ `DELIVERED.tsv` so a retired clip is never on the sheet you are choosing from. A clip you have
629
+ already sent is not an option you rejected, it is an option you should not have seen.
630
+
631
+ ```bash
632
+ cut -f5 CLIPPER/DELIVERED.tsv | sort -u > /tmp/used.txt
633
+ grep -vFf /tmp/used.txt casting-pool.tsv > casting-pool.fresh.tsv # cast from THIS
634
+ ```
635
+
636
+ > **The sweep is how "there was nothing else" stops being sayable.** Rule 11 asks you not to reuse a
637
+ > clip you have already delivered; the honest objection has always been that finding a fresh one
638
+ > meant paging a 693-clip shelf by hand. It is now ten API calls and a TSV.
639
+
640
+ > **Delivery work: open your delivery log in the same breath as the sweep.** Anything you already
483
641
  > sent this client is out (Rule 11), and anything that has been passed on twice is out for everybody.
484
642
  > **A clip you have already used is not a shortcut, it is the shelf you did not read** โ€” and the
485
- > shelf is 851 clips deep, so "there was nothing else" is almost never the real reason.
643
+ > sweep hands you 143 candidates, so "there was nothing else" is almost never the real reason.
486
644
  >
487
645
  > ```bash
488
646
  > cut -f1,5,8 CLIPPER/DELIVERED.tsv # client ยท clip ยท verdict โ€” read it BEFORE the contact sheet
@@ -510,14 +668,26 @@ string equality instead of a judgement, which is the only form of it a gate can
510
668
  | `overreact` | a full-body scream, sob or flail at something trivial | `neon-screaming`, `jontron-nooooooo`, `girl-fake-crying` |
511
669
  | `correct` | turning to camera to tell you, patiently, that you are wrong | `jonathan-frakes-telling-you-youre-wrong-for-47-seconds`, `women-mocking` |
512
670
  | `unbothered` | the room is on fire and the body has not moved | `my-favorite-thing-in-the-world-to-do-is-nothing`, `boss-baby-chilling` |
513
- | `celebrate` | arms up, jumping, confetti-scale joy | *the natural win-pole verb โ€” cast it off the shelf and confirm on the sheet* |
514
- | `stare` | holding eye contact with the camera and saying nothing | *the `<Entity> watching me โ€ฆ` verb (Rule 1, tier 1)* |
515
- | `wait` | still present, still waiting, visibly longer than is reasonable | *for captions whose collapse is elapsed time* |
671
+ | `celebrate` | arms up, jumping, confetti-scale joy | ๐Ÿ”Ž `celebration-dance`, `girl-dancing`, `jojo-siwa-dancing` โ€” the natural win-pole verb |
672
+ | `stare` | holding eye contact with the camera and saying nothing | ๐Ÿ”Ž `cat-staring` (๐ŸŸข), `mortal-kombat-1-shang-tsung` (๐Ÿ”ด) โ€” the `<Entity> watching me โ€ฆ` verb (Rule 1, tier 1) |
673
+ | `wait` | still present, still waiting, visibly longer than is reasonable | ๐Ÿ”Ž `wait-hold-on-wait` โ€” for captions whose collapse is elapsed time |
516
674
 
517
- The last three have no verified slug in this file โ€” nobody has cast them here yet. Find one on the
518
- shelf and confirm it on the contact sheet; **do not add an eleventh token** to avoid the work of
519
- choosing among ten. If a clip genuinely does something none of the ten covers, that is a real
520
- finding: add the token here, with its body line, before you use it.
675
+ **The queries that produce this table are the `VERBS` dict in `meme-sweep.py` above** โ€” the table is
676
+ its cached output, not a separate list to maintain. Re-run the sweep and you regenerate it.
677
+
678
+ > ๐Ÿ”Ž **marks a slug found by the vector sweep and NOT yet confirmed on a contact sheet.** The first
679
+ > seven rows were cast by hand and shipped; the last three were empty until the sweep filled them,
680
+ > and the sweep ranks descriptions, not footage. **Build the sheet before you use one** โ€” that rule
681
+ > does not relax because a machine agreed with you.
682
+
683
+ **Do not add an eleventh token** to avoid the work of choosing among ten. If a clip genuinely does
684
+ something none of the ten covers, that is a real finding: add the token here, **with its body line
685
+ and its vector query**, before you use it.
686
+
687
+ > **The sweep also grades the token list.** `deflate` and `celebrate` came back with 20 and 19 clips
688
+ > the other nine queries had not already found; `stare` and `wait` came back with 9 each. A verb
689
+ > whose neighbourhood is that thin is a verb the shelf cannot really cast โ€” treat a low count as a
690
+ > warning that you are about to force a clip, not as a challenge.
521
691
 
522
692
  **Then confirm by eye.** Build a contact sheet before you commit โ€” a slug promises an action the
523
693
  clip does not always deliver, and half these clips are 16:9 with the subject small at the bottom:
@@ -646,21 +816,68 @@ class before you write a single ffmpeg command.**
646
816
  | Where the caption goes | On the background, above the subject | On a plate above the clip, outside the picture |
647
817
  | Share of the shelf | The large majority | A minority โ€” check for burned-in text on the contact sheet |
648
818
 
649
- **Check the plate colour before you key anything.** These clips are re-encodes and the plate is not
650
- always what it looks like:
819
+ ### Decide the class MECHANICALLY, then read the plate
651
820
 
652
- ```bash
653
- python3 -c "
654
- import subprocess,numpy as np
655
- p=subprocess.run(['ffmpeg','-v','error','-i','meme.mp4','-vf','fps=1,crop=200:200:20:20',
656
- '-pix_fmt','rgb24','-f','rawvideo','-'],capture_output=True)
657
- a=np.frombuffer(p.stdout,np.uint8).reshape(-1,200,200,3)
658
- m=a.reshape(-1,3).mean(0).astype(int); print('plate rgb',m,'hex %02X%02X%02X'%tuple(m))"
821
+ > **The class is not a judgement call and it must not be one.** "Is there a plate" has to be
822
+ > answered before "what colour is the plate", or you key a Class B clip against a muddy average of
823
+ > somebody's living room and punch holes in it. **Nothing downstream catches that** โ€” the key
824
+ > "works", a subject is found, the render is quietly ruined, and `no_residual_green` passes because
825
+ > there was never any green.
826
+
827
+ Run this on every clip, before Appendix A. It answers both questions in the right order:
828
+
829
+ ```python
830
+ #!/usr/bin/env python3
831
+ """plate-check.py <meme.mp4> โ€” Class A or Class B, and the plate if there is one."""
832
+ import subprocess, sys, numpy as np
833
+
834
+ src, W, H = sys.argv[1], 320, 320
835
+ p = subprocess.run(["ffmpeg", "-v", "error", "-i", src, "-vf", f"fps=2,scale={W}:{H}",
836
+ "-pix_fmt", "rgb24", "-f", "rawvideo", "-"], capture_output=True)
837
+ a = np.frombuffer(p.stdout, np.uint8)
838
+ a = a[: len(a) // (W * H * 3) * (W * H * 3)].reshape(-1, H, W, 3).astype(np.int16)
839
+
840
+ # All four corners, not just the top-left: on a keyed clip every corner is plate,
841
+ # on a baked clip they are four different bits of a room.
842
+ c = np.concatenate([a[:, :24, :24], a[:, :24, -24:],
843
+ a[:, -24:, :24], a[:, -24:, -24:]], axis=2).reshape(-1, 3)
844
+ plate = c.mean(0).astype(int)
845
+ flat = c.std(0).mean() # one colour, or a scene
846
+ cover = (np.abs(a - plate).max(3) < 40).mean() # how much of the frame IS plate
847
+ sat = int(plate.max() - plate.min()) # letterbox bars read flat too
848
+
849
+ print(f"plate rgb {plate.tolist()} hex {'%02X%02X%02X' % tuple(plate)} "
850
+ f"corner stdev {flat:.1f} coverage {cover*100:.0f}% saturation {sat}")
851
+
852
+ if flat > 12 or cover < 0.25 or sat < 60:
853
+ sys.exit("CLASS B โ€” no saturated flat plate. Do NOT key. Layout A, crop the burned-in text")
854
+
855
+ digital = plate[1] > 240 and plate[0] < 40 and plate[2] < 40
856
+ print("CLASS A โ€” " + ("digital plate: chromakey 0.18:0.03, NO despill" if digital
857
+ else "filmed plate: chromakey 0.30:0.08, light despill"))
659
858
  ```
660
859
 
661
- A pure `00FF00` reading means a digital plate: key tight (`0.18:0.03`) and **skip despill.** A
662
- reading like `1F D3 1F` means a filmed plate: key looser and despill lightly. Getting this backwards
663
- is the first defect in the list below.
860
+ **The three thresholds and what each one is for:** `flat > 12` is a scene wearing a plate's
861
+ costume, `cover < 0.25` is a plate too small to be the background, `sat < 60` is black or white
862
+ letterbox bars, which are flat and are not a plate. All three are first-pass numbers measured on one
863
+ shelf โ€” **calibrate them against a clip you already agree about before you let them fail anything.**
864
+
865
+ **Then read the colour, because these clips are re-encodes and the plate is not always what it looks
866
+ like.** A pure `00FF00` reading means a digital plate: key tight (`0.18:0.03`) and **skip despill.**
867
+ A reading like `1F D3 1F` means a filmed plate: key looser and despill lightly. Getting this
868
+ backwards is the first defect in the list below.
869
+
870
+ > **The shelf is overwhelmingly green, and the gate only knows green.** `no_residual_green` (gate 2)
871
+ > tests a green hue, so it catches both defects that matter on this shelf โ€” a fringe from a tight
872
+ > key, and a key that never ran at all. `no_unkeyed_plate` sits beside it and is hue-agnostic, for
873
+ > the blue or magenta plate the green test is blind to. **If `plate-check.py` prints a plate that is
874
+ > not green, say so in the build log** โ€” the named check is no longer the one protecting you.
875
+
876
+ > **Why this harness does not use `vidfarm remove-greenscreen` for the video.** The skill's keying
877
+ > surface (`remove-greenscreen --local` / `--smart`, `cutout`, `sticker-pack`) is the right answer
878
+ > almost everywhere else, and it is a **transparent-output** surface. This format never needs
879
+ > transparency in its output โ€” it composites onto a background and ships an opaque MP4 โ€” and the
880
+ > alpha intermediate is where the free path breaks. See the warning under "Cost-saving mode".
664
881
 
665
882
  ### Class B โ€” dealing with the burned-in text
666
883
 
@@ -1183,8 +1400,13 @@ except you.
1183
1400
  **The shelf is public, which makes this sharper than it looks.** You cast from `vidfarm public-raws`
1184
1401
  and so does every other clipper on the marketplace. A slug that is easy to find is easy for everyone
1185
1402
  to find, so the buyer may have seen it this week from somebody else โ€” and 851 MemeScreens means the
1186
- excuse "there was nothing else" is almost never true. Cast past the first 200 (Appendix C) before
1187
- you reach for a clip you have already used.
1403
+ excuse "there was nothing else" is almost never true. **Run the diversity sweep before you reach for
1404
+ a clip you have already used**; it costs ten API calls and it deletes the excuse.
1405
+
1406
+ **And note what "easy to find" now means.** Every clipper on this marketplace reads the same CLI, and
1407
+ the CLI shows everybody the same first 200. If you cast from that page you are competing for 200
1408
+ clips while 65% of the shelf sits unread. The sweep is not only how you avoid repeating *yourself* โ€”
1409
+ it is how you avoid arriving at the buyer with the same clip as everyone who did not run it.
1188
1410
 
1189
1411
  **The number, if you want one:** the same `clip_slug` in more than about **3 of your last 20
1190
1412
  deliveries** is a signature you did not choose. Either it has the approvals to justify it, or you
@@ -1261,8 +1483,16 @@ interchangeable.
1261
1483
  is one ffmpeg command and the caption layer does not change. Do that before you write a second
1262
1484
  caption โ€” you will learn more from five castings of one joke than from five jokes.
1263
1485
 
1264
- Keep the index (Appendix C) and a `captions.tsv` of rung-3 lines. The pairing is the creative act;
1265
- everything downstream is a loop.
1486
+ > โš ๏ธ **Take the five clips from five DIFFERENT verb neighbourhoods, not from the top five of one
1487
+ > query.** This is where a matrix quietly stops being an experiment. The top five of one vector
1488
+ > query are five near-neighbours โ€” often the same performer in four crops โ€” so the five videos
1489
+ > differ by almost nothing and the five data points are one data point with error bars. Pull one
1490
+ > clip per verb from `casting-pool.fresh.tsv` and the five castings actually span the shelf.
1491
+
1492
+ Keep three files: the identity index (Appendix C), a fresh `casting-pool.fresh.tsv` from the sweep,
1493
+ and a `captions.tsv` of rung-3 lines. **The sweep is the only one of the three that goes stale** โ€”
1494
+ re-run it per session, because it is also the file that has your delivery log subtracted from it.
1495
+ The pairing is the creative act; everything downstream is a loop.
1266
1496
 
1267
1497
  ### โš ๏ธ The matrix is YOUR test set. It is not one client's inbox
1268
1498
 
@@ -1347,7 +1577,8 @@ What gate 2 enforces, and the number it uses:
1347
1577
  | Check | Threshold | The defect it exists for |
1348
1578
  |---|---|---|
1349
1579
  | `first_frame_has_subject` / `last_frame_has_subject` | lower-band stdev > 12 | Blank thumbnail / empty tail from un-trimmed plate |
1350
- | `no_residual_green` | < 0.5% green pixels | A key that was too tight |
1580
+ | `no_residual_green` | < 0.5% green pixels | A key that was too tight โ€” a fringe on the shelf's usual `00FF00` plate |
1581
+ | `no_unkeyed_plate` | largest flat saturated colour < 25% of frame, **or** `--flat-bg-ok` | The key never ran, or it ran on a blue/magenta plate the green test is blind to. Pass `--flat-bg-ok` only on ladder tier 6, where a flat brand plate really is the background |
1351
1582
  | `caption_horizontally_centred` | block centre 50% ยฑ 3% | The flush-left plate with a lake of dead space |
1352
1583
  | `caption_side_margins` | > 4% and < 96% | A plate touching an edge |
1353
1584
  | `caption_vertical_placement` | inside 8โ€“85% | Under the platform UI, or on the subject's face |
@@ -1415,7 +1646,11 @@ Run this **before** you build. An unchecked box is a rewrite, not a fix in the e
1415
1646
  - [ ] **Reusing this clip on a DIFFERENT client?** It has an approval on it, or it is unproven and
1416
1647
  this is its one outing. Passed on twice with no approval = retired
1417
1648
  - [ ] This `clip_slug` is not in more than ~3 of your last 20 deliveries
1418
- - [ ] You read past the first 200 of the shelf (Appendix C) before reaching for a clip you own
1649
+ - [ ] **The diversity sweep ran this session** and you cast off `casting-pool.fresh.tsv`, not off
1650
+ the CLI's first 200 and not off memory
1651
+ - [ ] The sweep's ranking was **live** โ€” `vidfarm whoami` shows a login, a gemini/openai key is
1652
+ saved, and two different verb queries returned two different top clips. No key = no ranking,
1653
+ silently, with a full page of results to hide it
1419
1654
  - [ ] The style โ€” plate, font, band, grade, handle โ€” is **identical** to the last delivery. That part
1420
1655
  is meant to repeat
1421
1656
 
@@ -1445,6 +1680,8 @@ Run this **before** you build. An unchecked box is a rewrite, not a fix in the e
1445
1680
 
1446
1681
  **The clip**
1447
1682
  - [ ] Rights tier declared. ๐ŸŸข for client work, or a ๐ŸŸข alternate is built alongside
1683
+ - [ ] **`plate-check.py` run, and the class it printed is the class you built** โ€” Class A keys and
1684
+ needs a background, Class B is never keyed and needs the burned-in text cropped
1448
1685
  - [ ] Plate colour sampled, not assumed
1449
1686
  - [ ] Contact sheet built and looked at
1450
1687
  - [ ] The action lands 0.6โ€“1.0s in, and frame 0 has the subject on it (`-vframes 1`, looked at)
@@ -1462,6 +1699,8 @@ Run this **before** you build. An unchecked box is a rewrite, not a fix in the e
1462
1699
  - [ ] 1080ร—1920, 30fps
1463
1700
  - [ ] The meme is NOT cropped to 9:16
1464
1701
  - [ ] Layout A or B chosen deliberately; in B the subject is bottom-anchored and full width
1702
+ - [ ] **Class A: the plate is gone and a background is behind the subject.** No green edge on the
1703
+ hair or shoulders, no green in the gaps between limbs, nothing green in the last frame
1465
1704
  - [ ] Caption **block centred**, words left-aligned, lines hand-broken to near-equal length
1466
1705
  - [ ] Caption fully legible at frame 0, static, plated, clear of the subject's head
1467
1706
  - [ ] Poster layer present, so frame 0 is never bare background
@@ -1573,9 +1812,19 @@ def frames(path, w, h, vf):
1573
1812
  n = len(p.stdout) // (w * h * 3)
1574
1813
  return np.frombuffer(p.stdout, np.uint8)[:n * w * h * 3].reshape(n, h, w, 3).astype(np.int16)
1575
1814
 
1576
- # --- 1. read the plate colour off the top-left corner, which is always plate ---
1815
+ # --- 1. confirm there IS a plate, THEN read its colour off the corner ---
1816
+ # The order is load-bearing. Handed a Class B clip (its own world, no plate),
1817
+ # every line below still runs: it keys against a muddy average of somebody's
1818
+ # living room, step 2 finds a "subject", and the render ships with holes punched
1819
+ # in it. Nothing catches that downstream โ€” no_residual_green passes, because
1820
+ # there was never any green. So refuse here, not at render.
1577
1821
  corner = frames(meme, 200, 200, "fps=1,crop=200:200:20:20")
1822
+ flat = corner.reshape(-1, 3).std(0).mean()
1578
1823
  plate = corner.reshape(-1, 3).mean(0).astype(int)
1824
+ if flat > 12 or int(plate.max() - plate.min()) < 60:
1825
+ sys.exit(f"corner stdev {flat:.1f}, plate {plate.tolist()} โ€” not a flat saturated "
1826
+ "chroma plate. This is CLASS B: do not key it. Use Layout A and crop "
1827
+ "the burned-in text. (Run plate-check.py for the full read.)")
1579
1828
  digital = bool(plate[1] > 240 and plate[0] < 40 and plate[2] < 40)
1580
1829
  key_hex = "0x%02X%02X%02X" % tuple(plate)
1581
1830
  tol, soft, despill = (0.18, 0.03, False) if digital else (0.30, 0.08, True)
@@ -1651,11 +1900,21 @@ def probe_key(tol_try, soft_try, despill_try, t):
1651
1900
  lum = flat.mean(1)
1652
1901
  lit = flat[lum > max(lum.max() * 0.75, 1)] # the subject's bright side
1653
1902
  green_left = ((flat[:, 1] - flat[:, 0] > 40) & (flat[:, 1] - flat[:, 2] > 40)).mean()
1654
- return lit.mean(), green_left
1903
+ # A radius that erased the whole subject leaves NO bright pixels, and
1904
+ # `lit.mean()` on an empty slice is nan, not 0. nan then loses every
1905
+ # comparison below in silence: `nan > 0.88` is False, so the radius is
1906
+ # rejected for the right reason by accident โ€” but if the UN-keyed probe
1907
+ # comes back nan too, every `keep` is nan, `best` stays None, and the run
1908
+ # falls through to the sampled default with only a printed line. Return a
1909
+ # real 0.0 so a dead frame scores like a dead frame.
1910
+ return (float(lit.mean()) if lit.size else 0.0), green_left
1655
1911
 
1656
1912
  t_probe = START + DUR / 2
1657
1913
  # chromakey's similarity floor is 1e-05, not 0 โ€” a literal 0 is a hard filter error
1658
1914
  src_lit, _ = probe_key(1e-5, 0.0, False, t_probe) # ~nothing keyed = the truth
1915
+ if not src_lit:
1916
+ sys.exit(f"the un-keyed probe frame at {t_probe:.2f}s has no bright pixels โ€” "
1917
+ "the clip is dark or the presence window is wrong. Check it by eye")
1659
1918
  best = None
1660
1919
  for tol_try in (0.34, 0.28, 0.22, 0.18, 0.14, 0.10, 0.07):
1661
1920
  r = probe_key(tol_try, soft, despill, t_probe)
@@ -1804,16 +2063,24 @@ ffmpeg -i media/scene.mp4 -vframes 1 -update 1 media/poster.png # frame 0 of t
1804
2063
 
1805
2064
  ## Appendix C โ€” build the shelf index once
1806
2065
 
1807
- The shelf pages at 200, the CLI cannot follow the cursor, and the slugs are the index โ€” so pull the
1808
- whole thing once through the REST route and grep the file instead of re-querying.
2066
+ > **This is the IDENTITY index, and it is the second of the two tools.** `meme-sweep.py` (in
2067
+ > *The diversity sweep*, above) is how you **cast** โ€” it ranks the whole shelf by what the body does
2068
+ > and hands you a fresh pool in ten calls. This appendix builds the full local mirror you **grep**:
2069
+ > for a slug you half-remember, for the rights tier of a name, for the ledger joins in Rule 11, and
2070
+ > for working with no login or no embedding key. Sweep to choose a clip. Index to look one up.
2071
+
2072
+ The shelf pages at 200, the CLI cannot follow the cursor, and the slugs are the index of identity โ€”
2073
+ so pull the whole thing once through the REST route and grep the file instead of re-querying.
1809
2074
 
1810
2075
  ```python
1811
2076
  #!/usr/bin/env python3
1812
2077
  """meme-index.py โ€” pull every MemeScreens raw into one greppable TSV.
1813
2078
 
1814
- The `--query` filter on this endpoint is thin and inconsistent (a search for
1815
- "hungry food eating" returns nothing on a shelf full of people eating), and the
1816
- AI descriptions are blind to the meme's identity. So: pull it all, index by slug.
2079
+ Note this pages with `q` unset entirely: the `q` filter is a LEXICAL substring
2080
+ match that runs BEFORE ranking, so any query here would silently shrink the
2081
+ mirror. Semantic search is a different parameter (`vector_q`) and lives in
2082
+ meme-sweep.py. The AI descriptions are blind to the meme's identity, so this
2083
+ file is indexed by slug.
1817
2084
 
1818
2085
  Paging note: `vidfarm public-raws` has NO --cursor flag and the server caps a
1819
2086
  shelf at 200 per call, so the CLI alone can only ever show you the first 200 of
@@ -2015,13 +2282,16 @@ and that its answers do not contradict each other. That is a smaller claim than
2015
2282
  "the match is verified", and it is the one this script is entitled to make.
2016
2283
 
2017
2284
  usage: meme-qa.py <render.mp4> <composition.html> [--offer dishcover.io]
2018
- [--pole pain|win] [--silent-ok] [--json]
2285
+ [--pole pain|win] [--silent-ok] [--flat-bg-ok] [--json]
2019
2286
  """
2020
2287
  import json, re, subprocess, sys
2021
2288
  import numpy as np
2022
2289
 
2023
2290
  render, comp = sys.argv[1], sys.argv[2]
2024
2291
  SILENT_OK = "--silent-ok" in sys.argv
2292
+ # Ladder tier 6 only: a flat brand plate really is the background. Everywhere
2293
+ # else a slab of one saturated colour is a plate that never got keyed.
2294
+ FLAT_BG_OK = "--flat-bg-ok" in sys.argv
2025
2295
  OFFER = (sys.argv[sys.argv.index("--offer") + 1].strip().lower()
2026
2296
  if "--offer" in sys.argv else None)
2027
2297
  # The pole changes what the payoff is allowed to do (Rule 2). Default to PAIN:
@@ -2120,7 +2390,21 @@ for label, fr in (("first", first), ("last", last)):
2120
2390
  check(f"{label}_frame_has_subject", band.std() > 12,
2121
2391
  f"lower-band stdev {band.std():.1f} (>12)")
2122
2392
 
2123
- # ------------------------------------------------------ 3. no residual green key
2393
+ # -------------------------------------------- 3. the plate is GONE, in any hue
2394
+ # Two checks, because these are two different defects. `no_residual_green` is a
2395
+ # FRINGE test on the shelf's usual 00FF00 plate โ€” a key that was slightly too
2396
+ # tight. `no_unkeyed_plate` is hue-agnostic and catches the bigger, dumber one:
2397
+ # the key never ran, or it ran on a blue/magenta plate the green test cannot see.
2398
+ #
2399
+ # Measured on four fixtures: an unkeyed 00FF00 plate fails both; a photographic
2400
+ # background passes no_unkeyed_plate at 15%; a flat brand plate fails it at 79%
2401
+ # and needs --flat-bg-ok; an unkeyed MAGENTA plate reads 0.00% green and is
2402
+ # caught ONLY by no_unkeyed_plate. That last row is the whole reason it exists.
2403
+ #
2404
+ # Watch the green check if the joke's world is a lawn, a forest or a pool table:
2405
+ # 0.5% is tight, and a genuinely green BACKGROUND can trip it. That is a false
2406
+ # positive, not a bad key โ€” confirm by eye, then pick a different world rather
2407
+ # than loosening the number.
2124
2408
  mid = probe(render, "scale=270:480", 270, 480, pre=("-ss", str(dur / 2)))
2125
2409
  if mid is not None:
2126
2410
  a = mid[0]
@@ -2128,6 +2412,23 @@ if mid is not None:
2128
2412
  green = ((g - r > 45) & (g - b > 45) & (g > 80)).mean()
2129
2413
  check("no_residual_green", green < 0.005, f"{green*100:.2f}% green pixels (<0.5%)")
2130
2414
 
2415
+ # A quarter of the frame in ONE saturated colour is a plate, not a
2416
+ # background: a background is a photograph, and photographs vary. The
2417
+ # caption plate is white or black, so it is not saturated and not counted.
2418
+ # Ladder tier 6 (a flat brand plate as the background) is a legitimate
2419
+ # exception and the only one โ€” pass --flat-bg-ok, the same way --silent-ok
2420
+ # works, so the exception is a decision somebody made rather than a default.
2421
+ sat_px = (a.max(2) - a.min(2)) > 60
2422
+ if sat_px.any():
2423
+ q = a[sat_px] // 32 # 8^3 colour buckets
2424
+ keys = q[:, 0] * 64 + q[:, 1] * 8 + q[:, 2]
2425
+ biggest = float(np.bincount(keys).max()) / a[..., 0].size
2426
+ else:
2427
+ biggest = 0.0
2428
+ check("no_unkeyed_plate", FLAT_BG_OK or biggest < 0.25,
2429
+ f"largest flat saturated colour = {biggest*100:.0f}% of frame (<25%)"
2430
+ + (" [--flat-bg-ok]" if FLAT_BG_OK else ""))
2431
+
2131
2432
  # ------------------------------------- 4. caption block is horizontally CENTRED
2132
2433
  # Find the caption plate in frame 0 by its own darkness against the background,
2133
2434
  # then compare its horizontal centre to the canvas centre.