forked from genewildish/Mainline
feat: add a --firehose mode with a dynamic bottom display zone and include standard Python ignores in .gitignore.
This commit is contained in:
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
__pycache__/
|
||||
*.pyc
|
||||
.mainline_venv/
|
||||
86
mainline.py
86
mainline.py
@@ -56,6 +56,7 @@ HEADLINE_LIMIT = 1000
|
||||
FEED_TIMEOUT = 10
|
||||
MIC_THRESHOLD_DB = 50 # dB above which glitches intensify
|
||||
MODE = 'poetry' if '--poetry' in sys.argv or '-p' in sys.argv else 'news'
|
||||
FIREHOSE = '--firehose' in sys.argv
|
||||
|
||||
# Poetry/literature sources — public domain via Project Gutenberg
|
||||
POETRY_SOURCES = {
|
||||
@@ -461,6 +462,7 @@ def fetch_poetry():
|
||||
|
||||
# ─── STREAM ───────────────────────────────────────────────
|
||||
_SCROLL_DUR = 3.75 # seconds per headline
|
||||
FIREHOSE_H = 6 # firehose zone height (terminal rows)
|
||||
_mic_db = -99.0 # current mic level, written by background thread
|
||||
_mic_stream = None
|
||||
|
||||
@@ -676,6 +678,51 @@ def _make_block(title, src, ts, w):
|
||||
return content, hc, len(content) - 1 # (rows, color, meta_row_index)
|
||||
|
||||
|
||||
def _firehose_line(items, w):
|
||||
"""Generate one line of rapidly cycling firehose content."""
|
||||
r = random.random()
|
||||
if r < 0.35:
|
||||
# Raw headline text
|
||||
title, src, ts = random.choice(items)
|
||||
text = title[:w - 1]
|
||||
color = random.choice([G_LO, G_DIM, W_GHOST, C_DIM])
|
||||
return f"{color}{text}{RST}"
|
||||
elif r < 0.55:
|
||||
# Dense glitch noise
|
||||
d = random.choice([0.45, 0.55, 0.65, 0.75])
|
||||
return "".join(
|
||||
f"{random.choice([G_LO, G_DIM, C_DIM, W_GHOST])}"
|
||||
f"{random.choice(GLITCH + KATA)}{RST}"
|
||||
if random.random() < d else " "
|
||||
for _ in range(w)
|
||||
)
|
||||
elif r < 0.78:
|
||||
# Status / program output
|
||||
sources = FEEDS if MODE == 'news' else POETRY_SOURCES
|
||||
src = random.choice(list(sources.keys()))
|
||||
msgs = [
|
||||
f" SIGNAL :: {src} :: {datetime.now().strftime('%H:%M:%S.%f')[:-3]}",
|
||||
f" ░░ FEED ACTIVE :: {src}",
|
||||
f" >> DECODE 0x{random.randint(0x1000, 0xFFFF):04X} :: {src[:24]}",
|
||||
f" ▒▒ ACQUIRE :: {random.choice(['TCP', 'UDP', 'RSS', 'ATOM', 'XML'])} :: {src}",
|
||||
f" {''.join(random.choice(KATA) for _ in range(3))} STRM "
|
||||
f"{random.randint(0, 255):02X}:{random.randint(0, 255):02X}",
|
||||
]
|
||||
text = random.choice(msgs)[:w - 1]
|
||||
color = random.choice([G_LO, G_DIM, W_GHOST])
|
||||
return f"{color}{text}{RST}"
|
||||
else:
|
||||
# Headline fragment with glitch prefix
|
||||
title, _, _ = random.choice(items)
|
||||
start = random.randint(0, max(0, len(title) - 20))
|
||||
frag = title[start:start + random.randint(10, 35)]
|
||||
pad = random.randint(0, max(0, w - len(frag) - 8))
|
||||
gp = ''.join(random.choice(GLITCH) for _ in range(random.randint(1, 3)))
|
||||
text = (' ' * pad + gp + ' ' + frag)[:w - 1]
|
||||
color = random.choice([G_LO, C_DIM, W_GHOST])
|
||||
return f"{color}{text}{RST}"
|
||||
|
||||
|
||||
def stream(items):
|
||||
random.shuffle(items)
|
||||
pool = list(items)
|
||||
@@ -687,13 +734,15 @@ def stream(items):
|
||||
sys.stdout.flush()
|
||||
|
||||
w, h = tw(), th()
|
||||
fh = FIREHOSE_H if FIREHOSE else 0
|
||||
sh = h - fh # scroll zone height
|
||||
GAP = 3 # blank rows between headlines
|
||||
dt = _SCROLL_DUR / (h + 15) * 2 # 2x slower scroll
|
||||
dt = _SCROLL_DUR / (sh + 15) * 2
|
||||
|
||||
# active blocks: (content_rows, color, canvas_y, meta_idx)
|
||||
active = []
|
||||
cam = 0 # viewport top in virtual canvas coords
|
||||
next_y = h # canvas-y where next block starts (off-screen bottom)
|
||||
next_y = sh # canvas-y where next block starts (off-screen bottom)
|
||||
noise_cache = {}
|
||||
|
||||
def _noise_at(cy):
|
||||
@@ -703,22 +752,26 @@ def stream(items):
|
||||
|
||||
while queued < HEADLINE_LIMIT or active:
|
||||
w, h = tw(), th()
|
||||
fh = FIREHOSE_H if FIREHOSE else 0
|
||||
sh = h - fh
|
||||
|
||||
# Enqueue new headlines when room at the bottom
|
||||
while next_y < cam + h + 10 and queued < HEADLINE_LIMIT:
|
||||
while next_y < cam + sh + 10 and queued < HEADLINE_LIMIT:
|
||||
t, src, ts = _next_headline(pool, items, seen)
|
||||
content, hc, midx = _make_block(t, src, ts, w)
|
||||
active.append((content, hc, next_y, midx))
|
||||
next_y += len(content) + GAP
|
||||
queued += 1
|
||||
|
||||
# Draw frame
|
||||
top_zone = max(1, int(h * 0.25)) # 25% fade zone at top (exit)
|
||||
bot_zone = max(1, int(h * 0.10)) # 10% fade zone at bottom (entry)
|
||||
# Draw scroll zone
|
||||
top_zone = max(1, int(sh * 0.25))
|
||||
bot_zone = max(1, int(sh * 0.10))
|
||||
buf = []
|
||||
for r in range(h):
|
||||
for r in range(sh):
|
||||
cy = cam + r
|
||||
row_fade = min(1.0, min(r / top_zone, (h - 1 - r) / bot_zone))
|
||||
top_f = min(1.0, r / top_zone)
|
||||
bot_f = 1.0 if FIREHOSE else min(1.0, (sh - 1 - r) / bot_zone)
|
||||
row_fade = min(top_f, bot_f)
|
||||
drawn = False
|
||||
for content, hc, by, midx in active:
|
||||
cr = cy - by
|
||||
@@ -743,13 +796,20 @@ def stream(items):
|
||||
else:
|
||||
buf.append(f"\033[{r+1};1H\033[K")
|
||||
|
||||
# Glitch — base rate + mic-reactive spikes
|
||||
# Draw firehose zone
|
||||
if FIREHOSE and fh > 0:
|
||||
for fr in range(fh):
|
||||
fline = _firehose_line(items, w)
|
||||
buf.append(f"\033[{sh + fr + 1};1H{fline}\033[K")
|
||||
|
||||
# Glitch — base rate + mic-reactive spikes (scroll zone only)
|
||||
mic_excess = max(0.0, _mic_db - MIC_THRESHOLD_DB)
|
||||
glitch_prob = 0.32 + min(0.9, mic_excess * 0.16)
|
||||
n_hits = 4 + int(mic_excess / 2)
|
||||
if random.random() < glitch_prob and buf:
|
||||
for _ in range(min(n_hits, h)):
|
||||
gi = random.randint(0, len(buf) - 1)
|
||||
g_limit = sh if FIREHOSE else len(buf)
|
||||
if random.random() < glitch_prob and g_limit > 0:
|
||||
for _ in range(min(n_hits, g_limit)):
|
||||
gi = random.randint(0, g_limit - 1)
|
||||
buf[gi] = f"\033[{gi+1};1H{glitch_bar(w)}"
|
||||
|
||||
sys.stdout.write("".join(buf))
|
||||
@@ -833,6 +893,8 @@ def main():
|
||||
mic_ok = _start_mic()
|
||||
if _HAS_MIC:
|
||||
boot_ln("Microphone", "ACTIVE" if mic_ok else "OFFLINE · check System Settings → Privacy → Microphone", mic_ok)
|
||||
if FIREHOSE:
|
||||
boot_ln("Firehose", "ENGAGED", True)
|
||||
|
||||
time.sleep(0.4)
|
||||
slow_print(" > STREAMING...\n")
|
||||
|
||||
Reference in New Issue
Block a user