First commit, imported from the working folder: Android TV app, loopback guide server, design sources and per-release verification notes. Includes the 0.16 tuning screen, built and tested but not yet confirmed on the box. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
66 lines
4.9 KiB
Python
66 lines
4.9 KiB
Python
from pathlib import Path
|
|
from html import escape
|
|
from PIL import Image, ImageDraw, ImageFont
|
|
p=Path(__file__).parent
|
|
N='#071632';P='#12356b';Y='#ffde59';M='#b7cbe8';W='#ffffff';C='#f7eac8'
|
|
def build(section):
|
|
a=['<svg xmlns="http://www.w3.org/2000/svg" width="1920" height="1080" viewBox="0 0 1920 1080">']
|
|
def rect(x,y,w,h,c,stroke=None):a.append(f'<rect x="{x}" y="{y}" width="{w}" height="{h}" fill="{c}"'+(f' stroke="{stroke}" stroke-width="2"' if stroke else '')+'/>')
|
|
def text(x,y,s,size=28,c=W,bold=False,font='Barlow'):
|
|
a.append(f'<text x="{x}" y="{y}" font-family="{font}" font-size="{size}" fill="{c}" font-weight="{600 if bold else 400}">{escape(s)}</text>')
|
|
rect(0,0,1920,1080,N)
|
|
a.append(f'<circle cx="106" cy="87" r="32" fill="none" stroke="{Y}" stroke-width="7"/><ellipse cx="106" cy="87" rx="51" ry="14" transform="rotate(-25 106 87)" fill="none" stroke="white" stroke-width="4"/>')
|
|
text(175,108,'orbit',68,bold=True);text(360,103,'/ Classic',34,M)
|
|
text(1440,101,'Saturday 31 October',26);text(1770,101,'21:30',28,Y,True)
|
|
for x,label,active in [(64,'1 Menu',section=='menu'),(412,'2 TV Guide',False),(760,'3 Box Office',section=='office')]:
|
|
rect(x,150,340,64,'#194c91' if active else P,'#38629a');text(x+24,192,label,29,bold=active)
|
|
if section=='office':
|
|
text(64,296,'OrbOffice',68,Y,font='Barlow Condensed');text(370,288,'Your front-row seat.',32,M)
|
|
tx=64;ty=340;tw=1070;th=590;seam=tx+tw*.76
|
|
rect(tx,ty,tw,th,C)
|
|
a.append(f'<line x1="{seam}" y1="{ty}" x2="{seam}" y2="{ty+th}" stroke="{N}" stroke-opacity=".5" stroke-width="2" stroke-dasharray="10 10"/>')
|
|
for i in range(12):
|
|
for x in [tx,tx+tw]:a.append(f'<circle cx="{x}" cy="{ty+(i+.5)*th/12}" r="10" fill="{N}"/>')
|
|
for y in [ty,ty+th]:a.append(f'<circle cx="{seam}" cy="{y}" r="18" fill="{N}"/>')
|
|
text(110,408,'OrbOffice',48,N,font='Barlow Condensed');text(110,450,'Orbit premieres',26,N)
|
|
text(110,550,'The Last',72,N,font='Barlow Condensed');text(110,629,'Shuttle Home',72,N,font='Barlow Condensed')
|
|
text(110,745,'Tonight at 21:30',36,N,True);text(110,787,'One universe. Best seat in the house.',27,N)
|
|
rect(110,824,236,66,Y);text(137,870,'Admit one',39,N,font='Barlow Condensed')
|
|
text(seam+60,420,'orbit',42,N,True);text(seam+50,465,'Front row',32,N,font='Barlow Condensed')
|
|
text(seam+77,658,'01',105,N,font='Barlow Condensed');text(seam+84,700,'Seat',25,N)
|
|
text(seam+66,845,'ORB-001',30,N,font='Barlow Condensed');text(seam+75,883,'Preview',23,N)
|
|
text(64,976,'Preview ticket • Fictional event • Included in your collection',26,M)
|
|
text(1210,320,'Special transmissions',40,bold=True)
|
|
rect(1210,356,646,77,Y,W);text(1238,407,'Orbit premieres',32,N,True)
|
|
rect(1210,445,646,77,P,'#38629a');text(1238,496,'After dark',32)
|
|
for y,s in [(590,'One final journey brings an unlikely'),(634,'crew together. A fictional premiere'),(678,'for your own front-row seat.')]:text(1210,y,s,32,M)
|
|
text(1210,800,'Event playback comes with the',28,M);text(1210,838,'Emby connection.',28,M)
|
|
rect(1210,874,646,66,P,'#38629a');text(1240,918,'Return to TV Guide',29)
|
|
else:
|
|
rect(64,258,1792,690,P)
|
|
text(112,365,'Your evening starts here.',61,Y,True)
|
|
for y,s in [(455,'Your collection.'),(515,'Your channels.'),(575,'Your television.')]:text(112,y,s,46)
|
|
text(112,824,'Orbit Classic',48,M,font='Barlow Condensed');text(112,873,'The satellite spirit, in your universe.',30,M)
|
|
for y,s in [(400,"TV Guide / What's on now"),(510,'OrbOffice / Special transmissions'),(620,'Services / Orbit Interactive')]:
|
|
rect(1030,y,760,84,Y if y==400 else '#194c91',W if y==400 else '#38629a');text(1058,y+55,s,30,N if y==400 else W,y==400)
|
|
text(64,1030,'Arrows Browse OK Select Back Return to guide',25,M)
|
|
a.append('</svg>');(p/('orboffice.svg' if section=='office' else 'orbit-menu.svg')).write_text('\n'.join(a))
|
|
for section in ['office','menu']:build(section)
|
|
# A font comparison rendered from the actual local TTFs, without installing system fonts.
|
|
base=p/'fonts'
|
|
im=Image.new('RGB',(1600,1100),N);d=ImageDraw.Draw(im)
|
|
def line(x,y,s,path,size,color=W):d.text((x,y),s,font=ImageFont.truetype(str(path),size),fill=color)
|
|
def font(name):return base/name
|
|
line(65,45,'Orbit / type study',font('barlow-semibold.ttf'),60)
|
|
line(65,123,'Actual fonts from your Documents collection',font('barlow-regular.ttf'),28,M)
|
|
for i,(name,path,description) in enumerate([
|
|
('Barlow',font('barlow-regular.ttf'),'Selected for the new menu and event descriptions'),
|
|
('Barlow Condensed',font('barlow-condensed-medium.ttf'),'Selected for OrbOffice titles and ticket lettering'),
|
|
('Instrument Sans',font('instrument-sans.ttf'),'Alternative: a quieter contemporary UI')]):
|
|
y=215+i*285;d.rectangle((64,y,1536,y+245),fill=P)
|
|
line(90,y+23,name,path,36,Y)
|
|
line(90,y+82,'The Last Shuttle Home',path,62)
|
|
line(90,y+169,'101 Orbit One Tonight 21:30 Admit one',path,27)
|
|
line(90,y+210,description,font('barlow-regular.ttf'),22,M)
|
|
im.save(p/'font-comparison.png')
|