Create your own: design for approval (on the TV, or from a phone by QR code)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,147 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Create your own, design proposal (Paul, 6 November 2026): make a channel on the TV, or from your phone by scanning a QR
|
||||
code on the TV. The phone opens a page the TV itself serves on the home network: no app to install, no account, no password.
|
||||
Channels go in a new 700s block, Yours. The QR pattern drawn here is illustrative only. For approval: nothing is built yet."""
|
||||
import ast, random
|
||||
from pathlib import Path
|
||||
HERE = Path(__file__).resolve().parent
|
||||
SRC = HERE.parent / 'profiles' / 'build-profiles.py'
|
||||
tree = ast.parse(SRC.read_text())
|
||||
keep = []
|
||||
for n in tree.body:
|
||||
if isinstance(n, ast.Expr) and isinstance(n.value, ast.Constant): continue
|
||||
if isinstance(n, ast.Expr) and isinstance(n.value, ast.Call) and getattr(n.value.func, 'id', '') == 'screen': break
|
||||
keep.append(n)
|
||||
ns = {'__file__': str(SRC)}
|
||||
exec(compile(ast.Module(keep, []), str(SRC), 'exec'), ns)
|
||||
globals().update({k: v for k, v in ns.items() if k not in ('HERE', '__file__', 'save')})
|
||||
from PIL import Image
|
||||
def save(name): ns['im'].resize((W, H), Image.LANCZOS).save(HERE / name); print(name)
|
||||
def D(): return ns['d']
|
||||
GREEN = (110, 220, 170); LATER = (15, 44, 92); TEAL = (110, 240, 216)
|
||||
|
||||
def head(title, line, step=None):
|
||||
text((80, 70), title, anton(48), WHITE)
|
||||
if step: text((1840, 84), step, semi(26), MUTED, 'ra')
|
||||
text((80, 140), line, reg(26), MUTED)
|
||||
|
||||
def qr(x, y, size):
|
||||
"""An illustrative QR pattern: three finder squares and a scatter of modules."""
|
||||
n = 29; m = size / n
|
||||
box(x - 16, y - 16, size + 32, size + 32, fill=WHITE, r=10)
|
||||
rnd = random.Random(7)
|
||||
for i in range(n):
|
||||
for j in range(n):
|
||||
finder = any(fi <= i < fi + 7 and fj <= j < fj + 7 for fi, fj in ((0, 0), (0, n - 7), (n - 7, 0)))
|
||||
if finder:
|
||||
a, b = (i % 7 if i < 7 else (i - (n - 7)) % 7), (j % 7 if j < 7 else (j - (n - 7)) % 7)
|
||||
on = a in (0, 6) or b in (0, 6) or (2 <= a <= 4 and 2 <= b <= 4)
|
||||
else: on = rnd.random() < .45
|
||||
if on: D().rectangle((S(x + j * m), S(y + i * m), S(x + (j + 1) * m), S(y + (i + 1) * m)), fill=NAVY)
|
||||
|
||||
def phone(x, y, w=430, h=880):
|
||||
box(x, y, w, h, fill=(20, 20, 24), r=48); box(x + 14, y + 14, w - 28, h - 28, fill=NAVY, r=38)
|
||||
box(x + w / 2 - 50, y + 26, 100, 22, fill=(20, 20, 24), r=11)
|
||||
|
||||
def chip(x, y, label, on=False, w=None):
|
||||
w = w or (len(label) * 13 + 40)
|
||||
box(x, y, w, 44, fill=YELLOW if on else PANEL, r=22); text((x + w / 2, y + 8), label, semi(22), NAVY if on else WHITE, 'ma')
|
||||
return w
|
||||
|
||||
# 1. Where it starts: on this TV, or on your phone.
|
||||
screen()
|
||||
head('Create a channel', 'Make a channel of your own from your library. It joins the guide in Yours, from 701.', 'Settings › Channels')
|
||||
box(140, 230, 760, 620, fill=NAVY, r=20, outline=YELLOW, width=4)
|
||||
text((520, 280), 'On this TV', anton(44), WHITE, 'ma')
|
||||
for i, line in enumerate(['Name it and pick its number', 'Add shows, or let the Director\'s Chair suggest them', 'Choose how it plays: in order, shuffled,', 'blocks or marathon', 'Choose when it\'s on']):
|
||||
text((200, 380 + i * 52), ('· ' if i != 3 else ' ') + line, reg(28), WHITE)
|
||||
box(380, 740, 280, 70, fill=YELLOW, r=12); text((520, 754), 'Start', semi(34), NAVY, 'ma')
|
||||
box(1020, 230, 760, 620, fill=PANEL, r=20)
|
||||
text((1400, 280), 'On your phone', anton(44), WHITE, 'ma')
|
||||
qr(1200, 370, 300)
|
||||
text((1560, 380), 'Scan with', reg(26), MUTED); text((1560, 416), 'your phone\'s', reg(26), MUTED); text((1560, 452), 'camera', reg(26), MUTED)
|
||||
text((1560, 520), 'Or go to', reg(26), MUTED); text((1560, 556), 'orbit.local', semi(28), WHITE); text((1560, 600), 'and enter', reg(26), MUTED)
|
||||
box(1560, 640, 170, 60, fill=NAVY, r=10); text((1645, 650), '4827', semi(38), YELLOW, 'ma')
|
||||
text((1400, 720), 'Same Wi-Fi as this TV. No app, no account, no password.', reg(24), MUTED, 'ma')
|
||||
text((1400, 756), 'The code works once and lasts two minutes.', reg(24), MUTED, 'ma')
|
||||
text((960, 900), 'Grown-ups only: the phone asks for the PIN if one is set, and Kids never sees these screens.', reg(26), MUTED, 'ma')
|
||||
keys([('◄►', 'Choose'), ('OK', 'Start on this TV'), ('Back', 'Channels')])
|
||||
save('create-1-start.png')
|
||||
|
||||
# 2. Shows: search, or describe the channel and the Chair proposes.
|
||||
screen()
|
||||
head('Shows', 'Search your library, or describe the channel and the Director\'s Chair suggests what fits.', 'Step 2 of 4')
|
||||
box(80, 200, 1060, 76, fill=PANEL, r=12); text((110, 218), 'Describe it:', semi(28), MUTED); text((290, 216), '90s sci-fi, late evenings', semi(30), WHITE)
|
||||
box(1160, 200, 220, 76, fill=YELLOW, r=12); text((1270, 216), 'Suggest', semi(30), NAVY, 'ma')
|
||||
text((80, 310), 'THE CHAIR SUGGESTS', semi(24), MUTED)
|
||||
props = [('The X-Files', 'Series · 1993 · 202 episodes', 'Sci-fi, 1990s, mystery: a strong fit'), ('Babylon 5', 'Series · 1993 · 110 episodes', 'Space opera from the decade'),
|
||||
('Stargate SG-1', 'Series · 1997 · 214 episodes', 'Sci-fi adventure, long run'), ('Sliders', 'Series · 1995 · 88 episodes', 'Also on Paradox: it can be on both')]
|
||||
for i, (t, d, why) in enumerate(props):
|
||||
y = 350 + i * 130; focus = i == 0
|
||||
box(80, y, 1060, 112, fill=NAVY if focus else PANEL, r=12, outline=YELLOW if focus else None, width=4)
|
||||
text((110, y + 14), t, semi(32), WHITE); text((110, y + 58), d, reg(22), MUTED); text((560, y + 22), why, reg(24), WHITE)
|
||||
box(890, y + 30, 110, 52, fill=YELLOW if focus else BLUE, r=10); text((945, y + 38), 'Add', semi(26), NAVY if focus else WHITE, 'ma')
|
||||
box(1015, y + 30, 100, 52, fill=BLUE, r=10); text((1065, y + 38), 'Skip', semi(26), WHITE, 'ma')
|
||||
box(1200, 310, 640, 640, fill=(10, 30, 66), r=16)
|
||||
text((1230, 330), 'IN YOUR CHANNEL', semi(24), MUTED)
|
||||
for i, t in enumerate(['Star Trek: Deep Space Nine', 'Farscape', 'Quantum Leap']):
|
||||
text((1230, 380 + i * 52), '· ' + t, reg(28), WHITE)
|
||||
text((1230, 560), 'Add and skip both teach the Chair', reg(22), MUTED); text((1230, 592), 'what this channel is for.', reg(22), MUTED)
|
||||
keys([('▲▼', 'Shows'), ('OK', 'Add'), ('►', 'Skip'), ('Back', 'Name')])
|
||||
save('create-2-shows.png')
|
||||
|
||||
# 3. How it plays, and when it's on.
|
||||
screen()
|
||||
head('How it plays', 'One style for the whole channel; you can change it later. It shapes what\'s planned from then on, never what\'s booked.', 'Step 3 of 4')
|
||||
styles = [('In order', 'Each show from where it left off,', 'taking turns with the others'), ('Shuffled', 'Episodes in a mixed order,', 'every one before any repeats'),
|
||||
('Blocks', 'Half-hour or hour slots by show;', 'short episodes grouped as on ToonZ'), ('Marathon', 'One show at a time, several', 'episodes back to back')]
|
||||
for i, (t, a, b) in enumerate(styles):
|
||||
x = 80 + i * 450; focus = i == 2
|
||||
box(x, 220, 420, 300, fill=NAVY if focus else PANEL, r=16, outline=YELLOW if focus else None, width=4)
|
||||
for k in range(3 if i != 3 else 1):
|
||||
for j in range(4 if i != 3 else 6):
|
||||
col = [(255, 138, 61), (110, 220, 170), (120, 190, 255)][((k * 4 + j) % 3) if i == 0 else ((k + j * 2) % 3)] if i != 3 else (255, 138, 61)
|
||||
if i == 2: col = [(255, 138, 61), (110, 220, 170), (120, 190, 255)][k]
|
||||
box(x + 30 + j * (90 if i != 3 else 60), 250 + k * 40, (80 if i != 3 else 50), 28, fill=col, r=5)
|
||||
text((x + 30, 400), t, anton(40), WHITE); text((x + 30, 452), a, reg(22), MUTED); text((x + 30, 482), b, reg(22), MUTED)
|
||||
text((80, 570), 'When it\'s on', semi(32), WHITE)
|
||||
w = chip(80, 620, 'All day', True); w2 = chip(80 + w + 20, 620, 'Evenings, 18:00 to midnight'); chip(80 + w + w2 + 40, 620, 'Set your own hours')
|
||||
text((80, 700), 'Outside its hours the channel shows a "back at 18:00" card, as Kids sees a channel that\'s asleep.', reg(24), MUTED)
|
||||
box(80, 780, 1760, 150, fill=(10, 30, 66), r=16)
|
||||
box(110, 810, 200, 90, fill=(40, 20, 70), r=10); text((210, 828), '701', anton(44), YELLOW, 'ma')
|
||||
text((340, 812), '90s Sci-Fi', anton(40), WHITE); text((340, 866), 'Yours · 7 shows · Blocks · All day · planned like any Orbit channel', reg(24), MUTED)
|
||||
keys([('◄►', 'Style'), ('▼', 'Hours'), ('OK', 'Choose'), ('Back', 'Shows')])
|
||||
save('create-3-style.png')
|
||||
|
||||
# 4. The phone: the page the QR opens, served by the TV itself.
|
||||
screen()
|
||||
head('On your phone', 'The page the QR code opens. It comes from the TV itself, over your home Wi-Fi: nothing to install.', 'Phone companion')
|
||||
phone(120, 190); x = 150
|
||||
text((x + 20, 240), 'Orbit', anton(40), WHITE); text((x + 20, 292), 'Living Room TV · connected', reg(22), GREEN)
|
||||
text((x + 20, 340), 'YOUR CHANNELS', semi(20), MUTED)
|
||||
for i, (n, t, s) in enumerate([('701', '90s Sci-Fi', 'Blocks · All day'), ('702', 'Sunday Westerns', 'Marathon · Sundays'), ('703', 'Cartoon Club', 'Shuffled · 07:00 to 12:00')]):
|
||||
y = 376 + i * 130
|
||||
box(x + 12, y, 346, 114, fill=PANEL, r=14); text((x + 32, y + 14), n, semi(24), YELLOW); text((x + 90, y + 12), t, semi(28), WHITE); text((x + 32, y + 56), s, reg(20), MUTED)
|
||||
box(x + 32, y + 78, 150, 28, fill=BLUE, r=8); text((x + 107, y + 80), 'Watch on TV', semi(18), WHITE, 'ma')
|
||||
box(x + 196, y + 78, 90, 28, fill=BLUE, r=8); text((x + 241, y + 80), 'Edit', semi(18), WHITE, 'ma')
|
||||
box(x + 12, 790, 346, 70, fill=YELLOW, r=14); text((x + 185, 808), '+ Create a channel', semi(28), NAVY, 'ma')
|
||||
phone(640, 190); x = 670
|
||||
text((x + 20, 240), 'New channel', anton(36), WHITE); text((x + 20, 290), 'Step 2 of 4 · Shows', reg(22), MUTED)
|
||||
box(x + 12, 330, 346, 56, fill=PANEL, r=12); text((x + 30, 344), 'Search your library…', reg(22), MUTED)
|
||||
text((x + 20, 410), 'THE CHAIR SUGGESTS', semi(20), MUTED)
|
||||
for i, (t, why) in enumerate([('The X-Files', 'Sci-fi, 1990s, mystery'), ('Babylon 5', 'Space opera, 1990s'), ('Stargate SG-1', 'Sci-fi adventure')]):
|
||||
y = 446 + i * 110
|
||||
box(x + 12, y, 346, 96, fill=PANEL, r=12); text((x + 30, y + 12), t, semi(26), WHITE); text((x + 30, y + 52), why, reg(19), MUTED)
|
||||
box(x + 250, y + 22, 92, 50, fill=YELLOW, r=10); text((x + 296, y + 32), 'Add', semi(22), NAVY, 'ma')
|
||||
box(x + 12, 790, 346, 70, fill=YELLOW, r=14); text((x + 185, 808), 'Next: how it plays', semi(26), NAVY, 'ma')
|
||||
box(1160, 190, 680, 400, fill=PANEL, r=16)
|
||||
text((1190, 214), 'ON THE TV MEANWHILE', semi(24), MUTED)
|
||||
box(1190, 260, 620, 150, fill=NAVY, r=12); text((1214, 280), 'Paul\'s phone is connected', semi(32), WHITE); text((1214, 330), 'Making a channel: 90s Sci-Fi', reg(24), MUTED); text((1214, 366), 'Back on the remote disconnects it', reg(22), MUTED)
|
||||
text((1190, 440), '"Watch on TV" tunes the TV to that channel, like a remote:', reg(24), WHITE)
|
||||
text((1190, 474), 'no screen casting needed, and the picture stays full quality.', reg(24), WHITE)
|
||||
box(1160, 620, 680, 330, fill=(10, 30, 66), r=16)
|
||||
text((1190, 644), 'KEEPING IT SAFE', semi(24), MUTED)
|
||||
for i, line in enumerate(['Home Wi-Fi only; the page is never on the internet', 'A one-time code from the TV, lasting two minutes', 'The PIN, if Grown-ups has one', 'Your Emby sign-in never leaves the TV',
|
||||
'Settings › Phones lists phones and removes them']):
|
||||
text((1190, 690 + i * 48), '· ' + line, reg(24), WHITE)
|
||||
save('create-4-phone.png')
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 240 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 256 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 216 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 331 KiB |
Reference in New Issue
Block a user