๐Ÿ— KeyzHub
19Keys ยท community archive
6148 bytes raw
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
#!/usr/bin/env python3
"""Freeze-frame quote cards (HLC Instagram style).

Each card is 1080x1350 (IG 4:5): two stacked freeze-frames, a bold white/black-outline
quote burned at the bottom of each, a small "HIGH LVL CONVERSATIONS" watermark, and a red
brand $ mark at the seam. Single-frame cards (one quote) also supported.

Define cards below (timestamps in seconds into the source video) and run:
  <venv>/python quote_cards.py <source_video> <out_dir>
"""
import subprocess
import sys
import textwrap
from pathlib import Path
from PIL import Image, ImageDraw, ImageFont

W = 1080
H = 1350
FFMPEG = "/opt/homebrew/opt/ffmpeg-full/bin/ffmpeg"

FONT_CANDIDATES = [
    "/System/Library/Fonts/Supplemental/Arial Narrow Bold.ttf",
    "/System/Library/Fonts/Supplemental/Arial Bold.ttf",
    "/Library/Fonts/Arial Bold.ttf",
    "/System/Library/Fonts/HelveticaNeue.ttc",
]


def font(size):
    for p in FONT_CANDIDATES:
        if Path(p).exists():
            try:
                return ImageFont.truetype(p, size)
            except Exception:
                continue
    return ImageFont.load_default()


def grab(src, t, dst):
    subprocess.run([FFMPEG, "-y", "-ss", str(t), "-i", src, "-frames:v", "1", dst,
                    "-loglevel", "error"], check=True)


def cover(img, w, h):
    iw, ih = img.size
    s = max(w / iw, h / ih)
    img = img.resize((int(iw * s + 1), int(ih * s + 1)), Image.LANCZOS)
    iw, ih = img.size
    return img.crop(((iw - w) // 2, (ih - h) // 2, (iw - w) // 2 + w, (ih - h) // 2 + h))


def wrap_to_fit(draw, text, max_w, max_lines, hi=58, lo=34):
    """Pick the biggest font size where the uppercase text wraps to <= max_lines."""
    text = text.upper()
    for size in range(hi, lo - 1, -2):
        f = font(size)
        # greedy wrap by measured width
        words, lines, cur = text.split(), [], ""
        for wd in words:
            t = (cur + " " + wd).strip()
            if draw.textlength(t, font=f) <= max_w:
                cur = t
            else:
                lines.append(cur)
                cur = wd
        if cur:
            lines.append(cur)
        if len(lines) <= max_lines:
            return f, lines
    return font(lo), lines


def draw_quote(card, region, text):
    """Burn a quote onto the bottom of a frame region (x0,y0,x1,y1)."""
    x0, y0, x1, y1 = region
    rw = x1 - x0
    d = ImageDraw.Draw(card)
    f, lines = wrap_to_fit(d, f'โ€œ{text}โ€', rw - 120, 3)
    lh = f.size + 12
    block_h = lh * len(lines)
    # darken the bottom of the frame so text always reads
    grad = Image.new("L", (1, y1 - y0), 0)
    for i in range(y1 - y0):
        grad.putpixel((0, i), int(170 * max(0, (i - (y1 - y0) * 0.55) / ((y1 - y0) * 0.45))))
    shade = Image.new("RGBA", (rw, y1 - y0), (0, 0, 0, 255))
    shade.putalpha(grad.resize((rw, y1 - y0)))
    card.alpha_composite(shade, (x0, y0))
    d = ImageDraw.Draw(card)
    ty = y1 - block_h - 46
    for ln in lines:
        tw = d.textlength(ln, font=f)
        d.text((x0 + (rw - tw) / 2, ty), ln, font=f, fill=(255, 255, 255, 255),
               stroke_width=max(3, f.size // 14), stroke_fill=(0, 0, 0, 255))
        ty += lh
    # watermark bottom-left
    wf = font(22)
    d.text((x0 + 24, y1 - 64), "HIGH LVL", font=wf, fill=(255, 255, 255, 150))
    d.text((x0 + 24, y1 - 40), "CONVERSATIONS", font=wf, fill=(255, 255, 255, 150))


def brand_dollar(card, cy):
    """Red $ brand mark at the right edge, vertically centered at cy."""
    d = ImageDraw.Draw(card)
    f = font(120)
    red = (214, 40, 40, 255)
    d.text((W - 96, cy - 78), "$", font=f, fill=red, stroke_width=4, stroke_fill=(120, 0, 0, 255))
    # up/down arrow accents
    d.polygon([(W - 64, cy - 96), (W - 78, cy - 76), (W - 50, cy - 76)], fill=red)
    d.polygon([(W - 64, cy + 96), (W - 78, cy + 76), (W - 50, cy + 76)], fill=red)


def make_card(src, out, top, bottom=None):
    """top/bottom = (timestamp, quote). If bottom is None, single full-frame card."""
    card = Image.new("RGBA", (W, H), (0, 0, 0, 255))
    tmp = Path("/tmp/qcards"); tmp.mkdir(exist_ok=True)
    if bottom:
        fh = H // 2
        for idx, (t, q) in enumerate([top, bottom]):
            fp = str(tmp / f"f{idx}.png")
            grab(src, t, fp)
            frame = cover(Image.open(fp).convert("RGBA"), W, fh)
            card.alpha_composite(frame, (0, idx * fh))
            draw_quote(card, (0, idx * fh, W, idx * fh + fh), q)
        ImageDraw.Draw(card).line([(0, fh), (W, fh)], fill=(255, 255, 255, 60), width=2)
        brand_dollar(card, fh)
    else:
        t, q = top
        fp = str(tmp / "f0.png")
        grab(src, t, fp)
        card.alpha_composite(cover(Image.open(fp).convert("RGBA"), W, H), (0, 0))
        draw_quote(card, (0, 0, W, H), q)
        brand_dollar(card, H - 200)
    card.convert("RGB").save(out, quality=95)
    print("saved", out)


# ---- card definitions (timestamp seconds, quote) ----
CARDS = [
    ("card-1-money", ("double",),
     (1090, "It's harder to keep money than it is to make it."),
     (3004, "True wealth is being happy with what you have.")),
    ("card-2-consumer", ("double",),
     (3114, "Spending power is just a signal that you're a consumer."),
     (5262, "The point of wealth is not to compete โ€” it's to coordinate.")),
    ("card-3-sovereignty", ("double",),
     (2831, "Generational wealth needs to become generational sovereignty."),
     (2798, "The family becomes its own bank โ€” its own private government.")),
    ("card-4-wisdom", ("double",),
     (1715, "You can make money early and be dumb as hell."),
     (1957, "Discipline is the tax on misalignment.")),
    ("card-5-family", ("double",),
     (303, "We've lost the plot on what family is for."),
     (1662, "Afraid to start a family is afraid to continue the future.")),
    ("card-6-meaning", ("single",),
     (1917, "Are you making meaning, or are you making money?"), None),
]

if __name__ == "__main__":
    src, out_dir = sys.argv[1], sys.argv[2]
    Path(out_dir).mkdir(parents=True, exist_ok=True)
    for name, kind, top, bottom in CARDS:
        make_card(src, f"{out_dir}/{name}.png", top, bottom)