Repository navigation
Release 0.1.15: crash protection, Integrations (experimental), vertical without Twitch - #71
Merged
Merged
Conversation
With no Twitch destination, InstantClone now builds OBS's Enhanced Broadcasting config itself from what OBS reports: one track for the main canvas at its output size, plus the Additional canvas (e.g. Aitum Vertical) when OBS sends one, so vertical destinations work without Twitch. The encoder is the H.264 one of the GPU OBS runs on, but only when OBS's log lists it as available (OBS won't start a stream on an unknown encoder); otherwise x264. The old fallback always asked for three x264 encodes, which overloaded CPUs. - The dashboard no longer locks Vertical behind a Twitch destination. A waiting vertical destination shows why on its card, and the log says so once per OBS session. - The old --launch-eb shortcut leaves OBS 32.2+ config alone. - Fixes: Twitch ingest reset on first edit, the Twitch mobile-risk pill that never showed, VOD + EB resetting the audio track, a stale Vertical pill after OBS stops, HTML-rendered save errors. - Polish: wrapping log with a tail that respects reading, aligned destination cards, a shorter destination editor, GPU encoder advice on the OBS tab.
Version bump, changelog entry, and READMEs updated for vertical without Twitch and the 406-test count.
Crash protection keeps destinations live on a looping reconnect screen when OBS crashes (fan request). This lands everything except the live egress wiring, so the new code is not called yet outside its tests. - crash_protection.* settings, saved with the System > Behavior card, with a live preview drawn by the real renderer (GET /crash-protection/preview). - src/slate: a small rasterizer, an Inter TrueType renderer (fonts subset by scripts/subset_slate_fonts.py, OFL), a 5x7 pixel font, the Whisper and Arcade themes, and a lossless H.264 encoder (I_PCM, I16x16 without residual, P_Skip). ffmpeg decodes its 2 s loop back pixel for pixel. - The ingest tells a deliberate stop (FCUnpublish / deleteStream) from a dropped connection: Controller::last_ingest_end.
Brings the finished v0.1.15 work (vertical without Twitch, GPU-aware Enhanced Broadcasting, dashboard fixes) under the crash-protection work in progress. No conflicts; 441 tests pass.
- A reconnect screen keeps every destination live when OBS crashes, freezes or drops, Enhanced Broadcasting tracks included. It ends on OBS's return (rejoining behind the delay when one is armed), after the hold time, or from End now in the dashboard, dock, tray, hotkey or MIDI. - Enhanced Broadcasting asks Twitch for H.264 tracks while another destination is on, so HEVC-capable GPUs feed TikTok, Kick and vertical destinations. A vertical card names HEVC tracks instead of "No 9:16 canvas". - Ending a stream reaches the platform as a clean stop. - A disabled, incomplete destination no longer blocks saving settings.
…s as H.264 Twitch builds the vertical track as HEVC on GPUs that have it, whatever codecs OBS offers; only the main track follows supported_codecs. A live trace showed the narrowed request coming back as an H.264 1080p main track (down from HEVC 1440p) and a still-HEVC vertical one. - With a vertical destination on, the proxy switches the vertical track in Twitch's reply to the same vendor's H.264 encoder, keeping its size, frame rate and bitrate (AMD's Hevc-prefixed options renamed to match). - The request is narrowed to H.264 only for a horizontal destination besides Twitch, so a Twitch + TikTok stream keeps its HEVC main track. - The waiting log line names HEVC instead of "no 9:16 canvas".
…odecs alone Beta.2 switched Twitch's HEVC vertical track to H.264 in the config, and Twitch refused every publish of that session (10053 on connect): it checks each track against the config it issued. Reverted. Twitch streams 2K channels (Partners, Affiliates) in HEVC, vertical track included. The vertical track is now read from the config OBS was given (OBS numbers tracks in encoder_configurations order), so it is found in any codec, not only when its H.264 SPS can be measured, and forwarded as Twitch sent it. Restream takes H.265 and transcodes it to H.264. The request is still narrowed to H.264 for a horizontal destination besides Twitch, whose main track Twitch does follow.
- Beacon (radar rings), Orbit (spinner) and Studio (lower third, lifted clear of TikTok's buttons on vertical canvases) join Whisper and Arcade. Each animates in few steps over a small area; every style stays under 1 Mbps at 1080p (tested). - Idle dots and tracks follow the background, so they read on any colour. - Settings: style cards with live thumbnails, an animated preview with a 16:9 / 9:16 switch, a hold-time slider, text counters and a reset link. - The server is the single source of the style list, default backgrounds and default text; the dashboard keeps no copy. - Text alignment lives in font::Line; a circle is a ring with no hole.
Every change fetched 10 separate preview frames plus 5 thumbnails (15 requests, ~4 MB) and swapped the image from a 200 ms timer; picking a style reloaded the thumbnails too, though a style can't change them. - /crash-protection/preview?frames=N stacks N moments of the loop into one strip (Canvas::stacked); CSS slides it a frame at a time. - Thumbnails reload only when colours or text change. - The strip is fetched once and swapped in whole, so it never flashes.
- 37 unused CSS classes (old overlay picker, wizard rows, an unused switch and checkbox, st-* leftovers): 5.5 KB off the dashboard. - Two functions nothing calls: dock.js openOverlay and overlay-runtime defaultStateProps. - The audio egress trace built its line for the first 20 tags of every connection and every 200th after, even with tracing off; it is now gated like the video one.
- Sub-tabs regrouped: General (startup, auto-arm, buffer), Crash protection, Controls (hotkeys, MIDI; hidden where neither exists), Twitch & OBS, Access (ingest key, password, ports, LAN), Alerts & logs, About. Sections moved verbatim; old sub-tab names map to the new ones, so remembered tabs and jump links still land right. - Find a setting (or "/"): matches titles, data-find keywords and text, opens the sub-tab and highlights the section. - Save bar: sticky, shows while the form differs from what was loaded or saved (the whole form body is compared), with Discard, and a prompt before closing the page with unsaved changes. - The pane card's scroll box is capped at the room left in the window, so its bottom edge is always on screen.
…ter images - Style thumbnails went blank whenever settings reloaded (after a Save): the cards were rebuilt empty while the thumbnail cache still matched. - The preview sampled the loop 10 times, which lines up with none of the styles' 6, 8 and 12 steps, so steps were skipped and timing stuttered. It now shows 24 frames: every step at the moment the stream shows it. - Previews are PNG from a small built-in encoder (Sub/Up filters, run deflate): a 24-frame strip is ~150 KB where 10 BMP frames were 3.8 MB, thumbnails 1-3 KB instead of 61 KB. Checked lossless against ffmpeg. - Previews render on a blocking thread, so drawing a strip never stalls the stream's egress on the single-threaded runtime. - The save bar only appears while there are unsaved changes.
…themselves - Cards and sections ease in, 40 ms apart, when a tab or sub-tab opens (opacity, 8 px rise, slight blur); the class clears on animationend so hover transforms stay free. - A destination pulses once in its platform colour when it goes live. - Buttons press to scale(.96) instead of a 1 px drop. - Destination cards say "Goes live when OBS does", "Connecting…", "Connected, waiting for video" or "Off. Switch it on to stream here." instead of a dash; counters read "frames · cuts · reconnects" and appear once there is something to count. - Everything respects prefers-reduced-motion.
…tinations
- Ctrl+K (or Search in the header) opens a palette built from live state:
tabs, System settings (the Find index), destinations on/off/edit,
profiles, context delay actions (Activate when armed, Back to live when
active), and a typed delay ("45", "2m", "1m30s") to arm directly.
- Event log rows: time, source pill, message; severity from the text
(errors red, warnings amber, successes green). Filters for Problems and
Errors with counts, per source, and free text; "Jump to latest" when
scrolled up. New lines are appended instead of re-rendering the list.
- No destinations yet: "Where do you stream?" with a tile per platform
(including the local test sink) that opens the form preset.
It's a feature few people reach for; the effort goes into the visual refresh instead.
- Twitch, YouTube and Kick marks in their colours on destination cards, the empty-screen picker and the setup wizard (platformMark); Restream, Custom and the test sink get a plain symbol, Trovo keeps its letter. - Destinations shows the signal path: OBS -> InstantClone (current delay or buffering state) -> every platform, each link animating only while data flows; rebuilt only when the destination set changes. - Larger section titles, labels, tab icons (open tab in the accent) and stats numbers. - Stats says "No video yet" over the flat chart before OBS streams; an empty Profiles list explains itself; card counters read "1 cut".
- Three drifting lights behind the whole hero (not just the readout half), additively blended, with film grain to stop the gradients banding. - The state colours live on <html> (data-stream-state) with one @Property transition, shared by the hero and a faint ambient glow at the page top. - The hero's top edge catches the state colour; a bloom from the number marks every state change. - Primary buttons gain a gradient and top highlight; cards, tabs and sections a lighter top border. - Removed the signal path strip (didn't fit).
- Fixed header (platform mark in its colour, title, Enabled switch, close) and footer (message, Cancel, Save) around a scrolling body, so Save is always in reach past the Twitch VOD section. - The body is a two-column grid on wide screens: name | platform, server | stream key, format (or the Twitch note) | audio track. One column under 760 px; a full-screen sheet under 560 px. - Field labels in normal case; audio options shortened to fit a column, with the Auto behaviour spelled out in the help line. - Every field keeps its id and markup, so the platform logic is unchanged.
Delay engine (controller, buffer): - A delay now always airs at least the time you set: the cut lands on the newest keyframe at or before the target instead of the nearest one, so a long GOP can no longer make it air short. - With a delay on, a destination waits for the buffer to reach it before connecting, instead of going out live (OBS restart mid-delay, a destination switched on mid-stream). - Buffer trim keeps the keyframe the full-buffer delay needs. - Live recuts use the dead band and the nearest keyframe to live. - The pump re-checks the publisher and sequence header after every wait. - Keyframe lookups use primary-track keyframes only. - Crash protection claims a fresh EB session instead of dropping it when the hold ends first, and a frozen OBS expires the hold before resuming. Enhanced Broadcasting without Twitch (local_eb_config, obs_register, web): - The fallback config copies the streamer's own OBS stream settings (encoder, encoder settings, bitrate, rescale; bitrate in Simple mode). - The Additional canvas is encoded only for an enabled vertical destination, with the same bits per pixel as the main track, on the streamer's codec when every vertical destination plays it, H.264 otherwise. Hardening from the test audit: - Dashboard: proper 400s for bad bodies, dock token no longer sees custom RTMP URLs, /overlay rules apply to GET only, URL rewriter fix. - Config: lossy UTF-8 startup load that never overwrites an unreadable file, legacy "Main" migration fix, OBS service registration keeps an empty services list valid. - H.264 zero-length NAL crash and scaling-list parsing, RTMP chunk and AMF0 edge cases, slate level 6.1, autostart test isolated from the real Run key. Tests: delay, hold and pump simulations, route-access net, full settings round trip, parser fuzz cases and the fallback settings matrix.
- Delay button eases between Arm, Activate, Adjust and Cut; only the number flips when only the number changes. - Delay field rolls its digits; limits at 0 and 600 answer back, and a typed value past 600 settles on 600 (and counts as 600 meanwhile). - A profile too big for the buffer explains itself when clicked. - "Waiting for OBS" listens with a sweep and dots, and blooms once on connect. - A clicked profile sends a spark into the button it arms; profile chips are patched in place so the armed fill never replays. - The status pill glides between states. - Aurora: five pools on slow orbits replace the three drifting blobs; the light grows with the buffer while arming; holds still under reduced motion. - The per-tick render writes nothing when nothing changed (panel and hero). - Fixes: primary and warning buttons turned grey on hover; idle destination cards squeezed their message; Add destination button restyled.
Changelog for crash protection, vertical without Twitch, the Enhanced
Broadcasting fallback (explained as a fallback, with Twitch's own config
used whenever Twitch is on at Start Streaming), the delay fixes and the
dashboard polish. READMEs: delay wording ("at least N seconds back") and
switching the Vertical destination on before streaming.
Delay - A delay longer than the buffer holds runs at what a full buffer holds instead of waiting forever; logged once per armed value. - Cuts must move in their direction, so a keyframe interval longer than measured never replays the same frames. Crash protection - The delay tail airs in full when the hold runs out; End now ends at once. - A frozen OBS that outlasts the hold ends instead of looping the tail. - A freeze rejoin keeps the full delay; a destination switched on mid-hold joins the reconnect screen. Egress - Destination edits (URL, format, audio track) stop the pump cleanly and restart it; a stopped pump releases its place in the buffer. - Connect timeout, backoff that resets only after a stable session, and reconnects counted only on real drops. - Track 2 destinations never get the live track once the second is sent. - Replies matched by transaction id; unflattenable headers are skipped. Ingest - Handshake and unpublished-idle timeouts, a command size cap and a cap on unfinished messages per connection. - A hung publisher hands the slot to a restarted OBS; refused publishes are log-throttled; the test sink binds to loopback. - End-of-stream markers no longer count as keyframes; 1 and 2 byte NAL lengths are scanned.
OBS dock
- Buffering counts down on the main button ("Ready in 0:23") and fills
toward Activate, instead of a spinner rebuilt four times a second.
- Crash hold: the dock says Holding, and the hold card (countdown, held
destinations, End now) takes the place of the delay controls, or sits
on top in a dock without them.
- Destinations follow the state stream instead of a 4 s poll, show
Connecting and Holding, and are patched in place so an open confirm
survives updates.
- Guarded writes: an unchanged state push touches nothing. The phase chip
glides, the readout pops only on a user change, the idle setter hugs
its digits, and Cut delay goes straight to Activate.
Fixes
- The inbound and per-destination bitrate held their last value after
OBS stopped; one RateMeter now reads 0 once its window goes stale.
- Hovers took a third hue from blending the accent with the state
colour (green while arming); each state now has one tint.
- "Applying" waited out its 2.5 s fallback after Cut or Disarm because
the pending check compared a raw phase with a display state.
OBS's clear_archive_encoder (StartStreaming -> SetupVodTrack, every version 28-32) calls obs_encoder_release() on the index-1 encoder it only borrowed. On the plain stream outputs (adv_stream / simple_stream) that stray release destroyed our freshly attached encoder before the stream started, so OBS sent a single audio track and Twitch had no VOD audio. Only Enhanced Broadcasting worked, because it streams through a separate multitrack output that clear_archive_encoder never touches. On plain outputs, hand our create-reference to OBS instead of releasing it: the stray release consumes it and the output stays the sole owner. The post-start check now always warns when an attached track went missing, instead of only in verbose mode.
The reconnect screen's decode tests require ffmpeg on Linux CI and fail without it. ci.yml's test-linux already installs it; release.yml's did not, so the v0.1.15-rc.1 release run failed.
Integrations react to what happens in InstantClone (a crash, a cut, a
chat command, a timer, a web call) by posting to Discord or Twitch chat,
pushing to a phone, calling a web service, running a program, writing a
file, or acting on the delay itself.
- One model for everything: an integration is handlers (trigger + steps).
Catalog modules are ordinary integrations with a `preset`, so anything
added from the catalog can be edited freely.
- Steps: messages, web requests (response usable as variables), checks
with then/otherwise, waits, "wait for the delay to air", delay actions,
VOD markers, clips, counters, programs and files.
- Templates: {var} and {var|fallback}; JSON bodies pass through as-is.
- The engine runs on its own thread and runtime, so nothing it does can
delay the stream. Events arrive through a bounded, never-blocking queue
that works from any thread; concurrency, cooldowns and waits are capped.
- Twitch: Device Code login (no secret, no server), token refresh before
expiry and hourly validation, chat over IRC/TLS with reconnect and rate
pacing, markers and clips through Helix.
- The old single Discord webhook migrates once to a Discord connection
plus a "Stream alerts" integration sending the same messages.
- Recipes share integrations as text without connections or secrets;
imports arrive switched off and programs/files need an explicit yes.
- Minimal JSON parser (no serde) for the API and recipes.
The dashboard side of integrations, built from the Destinations pieces so it feels native: a module is a destination card whose screen shows exactly what it sends, and every editor is the destination modal. - Grid and list views, filters and search; problems surface as one line with a Fix button. - Catalog of ready-made integrations and starter packs. - Module editor: moments as a timeline, the message edited on the preview itself with variable tokens, every option as a chip with its panel, and test runs that show every step. - Step builder: any trigger, nested if/otherwise, reorder/duplicate, per-step inspector, variables panel, test runs. - Connections: Discord channels, Twitch login by device code (main and bot accounts), phone pushes through ntfy. - Recipes: share and import as text. - The System tab's Discord webhook field points to Integrations; a settings reset keeps integrations, a factory reset clears them and the Twitch login. - Release builds take the Twitch app id from the TWITCH_CLIENT_ID repo variable. Fix: the HTTPS client handed schannel ureq's bundled root list, and schannel refused Discord's chain, so Discord webhooks never delivered. It now uses the OS certificate store.
Bugs: - "Set the delay" (!setdelay) used the hotkey's arm action, which toggles: a repeat disarmed the delay and a delay on air refused it. It now sets the delay, changing it live when one is on air. - A refused Twitch token left chat down until the next 5-minute upkeep round; the chat connection now asks for a refresh right away. - A bot account set to the streamer's own account made every command the streamer typed get ignored as "our own bot". Safety: - Program and file paths can't use variables, and neither can a web request's host: a chat message could otherwise pick what runs, where a file lands, or which machine gets the request. Recipes carrying any of these are refused. - Test runs never ping (@here/@everyone) a Discord server. - A destination stuck reconnecting alerts at most once per 5 minutes for each of live, dropped and all-down, instead of on every round. - The delay on/off chat notice has a 30 s cooldown. UX: - A switched-off integration saves as a draft with what is still missing; switched on, it must be complete. Errors read as instructions ("Pick a Discord channel") instead of field names. - Adding a Discord channel from inside an editor (or an import) comes back to it with the work intact and the new channel picked; an editor that lacks a channel opens on the chip that fixes it. - Newly added modules open switched on, so finishing them turns them on. - A catalog webhook's address applies to all its events at once. - Live refreshes no longer steal the search caret or wipe the Discord and phone forms mid-typing; cards and list rows work from the keyboard. - Honest test-run wording, clearer delay action help, and a note in the catalog when a build can't log in to Twitch.
UI - Redraws morph the live DOM instead of replacing innerHTML, so focus, carets, scroll and transitions survive. Keyed items slide to their new place, fade in and fade out; panels animate their height. - One modal shell that swaps content in place, closes with an animation, traps focus and gives it back. - Unsaved changes show a bar (Discard / Keep editing / Save) instead of confirm(); destructive buttons confirm on a second click. - Save is lit only with changes; Ctrl+S saves; spinners on every button that waits on the app; the toggle flips at once and rolls back on error. - Cards flag "Failing", "Finish setup", "Needs a fix" and "Needs Twitch" from the new /integrations issues map; per-module icons; skeleton on first load; sliding pills on segmented controls; toggle pills for roles. - Builder: steps joined by a spine, trigger tabs read "When the hold runs out", the insertion point can no longer drift into a hidden list. - Late replies never draw over another modal, and saves only close the editor that started them. Leaks - Engine prunes per-viewer and per-trigger cooldowns, flap records and stats of deleted integrations every minute. - Programs started by integrations are reaped without a thread each and capped at 16 running. - Failed step details lose URL paths and queries (webhook tokens). - Recipes never carry web addresses, headers or file and program paths.
System > Twitch & OBS gets a "Twitch login app" section: paste the Client ID of your own Twitch app and Integrations log in through it instead of the one built into releases. The five steps to make one are right there, with the redirect URL to copy and the one setting that matters (Public). - New `twitch_client_id` setting, saved with the System save bar and checked before saving (letters and digits only, so a stray space or a pasted secret never reaches Twitch). A settings reset keeps it. - The Twitch client id is switchable at run time. A real change cancels a login in progress and logs both accounts out, revoking them with the app that made them, then tells the user to connect again. A token refresh that finishes after the switch is dropped instead of bringing the old login back. - Precedence: your own app, then INSTANTCLONE_TWITCH_CLIENT_ID, then the release's built-in app. - A copy with no app at all opens the section by itself, and Integrations > Connections > Twitch links straight to it instead of a dead end.
…en secrets API commands - "Try it" under a selected web request: values for the variables it uses, Send request (new POST /integrations/fetch, same code path as a step), and the answer as a tree. Click a value, then "Reply in chat with it" fills the chat step after the request (or adds one). The real answer's fields join "Variables here" with their real values. - Security: values inserted into a request are escaped for where they land. URL values are percent-encoded (a viewer can't add parameters or change the path), JSON bodies get JSON-string escaping, and headers lose control characters (no injected header lines). Covered by tests. UX - Card and row menu (Edit, Duplicate, Share as a recipe, Delete), so deleting no longer means opening the integration first. - Undo toasts after adding and deleting; a deleted integration comes back in its old place. - Back button for views opened from another: the catalog (from Build your own, Import, or a module that needs a detail, where Back also removes the just-added draft), the simple editor (from Open in builder, keeping the edits) and the editor that opened Connections. - Recent activity rows open their integration. Privacy - Phone topic, Discord webhook link, request headers and the secret web call address are masked until Show, and hide again when the modal closes. Connection pills say "connected" instead of the topic or login. Addresses in cards, chips and steps show only scheme and host.
- A new integration (and "Another trigger") opens on "How does it start?": five big tiles with what each kind is and an example, instead of a dropdown that quietly preselected "OBS crashes". Saving or testing before picking shows the question again. - "Something happens" continues to "What happens?": every event as a tile with an icon and one line on what it means, grouped like before. An existing event trigger shows its event as a card with Change, which opens the same tiles and can go back without changing anything. - The inspector's trigger kind is tiles too; picking a chat command or a timer focuses its first field with the placeholder selected. - Variable chips show a card on hover or focus: what the variable holds, an example value and "Click to insert". Descriptions cover every built-in variable plus web answers, counters and remembered values.
- Secret fields decode letter by letter when shown: each character scrambles briefly in the accent colour, then settles, one after another; hiding folds them back into dots from the end. Drawn on a layer over the field with one animation frame loop that writes only what changed; typing ends it at once. A new random ntfy topic decodes in the same way. - The Show/Hide eye swaps icons with the scale, opacity and blur cross-fade. - Counts and chip values roll in when they change (up when a number grew, down when it shrank), from morph's text patch, on data-tick elements only. - Undo toasts drain a thin countdown bar; pointing at one holds both the bar and the timer. - Save shows "Saved" with its check popping in for a beat before the editor closes. - A card that switches on gives one soft pulse in its own colour. - Menu items, test run rows and a request's answer tree ease in, staggered; a new answer is a new tree, so it eases in again. - All of it is skipped under prefers-reduced-motion.
- Catalog module "Command from a website": a chat command that fetches an
address and answers with what came back, or a fallback line if the site
doesn't answer. It opens on its address chip until one is set.
- Paste a command from Nightbot ($(urlfetch)), Fossabot ($(customapi)),
StreamElements (${customapi.}) or Streamlabs ({readapi.}) into the
address: the new botcmd module (POST /integrations/convert, with tests)
returns the address, the reply around the answer and the command name,
translating their variables ($(user), ${1}, ${1:}, $(touser),
$(querystring), escape helpers). $(eval) is refused with the reason;
variables with no match stay as written and are listed.
- "Try it" moved out of the builder into shared functions, so the simple
editor has it too: send the request, click a value in the answer, and
"Use in the reply" swaps it in for the whole answer.
- New chat variable {target} (the name after the command without @, or
the viewer) and {arg4} to {arg9}. The chat variables now live in one
list, runner::CHAT_VARS, used by previews, validation and the catalog.
- Discord steps can edit the last message they posted (PATCH, falling
back to a new one when it was deleted). The crash alert is now one
message: "dropped" with a live <t:{hold_ends_at}:R> countdown, edited
to "back" or "ended".
- New trigger: a global hotkey or a MIDI pad. Registered by the tray next
to the delay actions (never one of their keys), pads forwarded by the
MIDI listener, learned from the dashboard. New "Clip button" preset.
- New step "Show on stream": cards on a public /alerts browser source,
queued, with a restart-safe feed. Tests never put anything on stream.
- New step "Edit text": first line, between, replace, cut, round, group
digits, upper/lower, random pick.
- Ready-made website commands (!uptime, !accountage, !followers, !game)
that answer in the editor right away; previews follow checks.
- Quiet hours per integration, a persisted {uses} counter, and each
card's last runs as dots. Recipes leave hotkeys and pads out.
Security: - Refuse non-public routes whose Host is a domain when no password is set (DNS rebinding could save an integration that runs a program). - Access-Control-Allow-Origin only on public routes and /state. - Program arguments are split before chat values are filled in. - Recipes drop request bodies and program arguments; import cleans the same way export does. - HTTP steps no longer follow redirects; dot segments are escaped. Fixes: - Twitch upkeep panicked in the first hour after boot (Instant math). - Twitch HTTP timeouts, refresh races with logout and re-login, live token for chat reconnects, per-run chat state, bot stops on revoke. - Delay bindings can't take an integration's key; toggle checks keys; refused integration hotkeys show on cards; MIDI knobs fire once. - Duplicate starts off with a fresh web call token and no shortcut. - Cooldowns count only started runs; busy runs don't reset failures. - Ping kept on Discord repost; uses flushed on exit; durable store. - wait_delay underflow, 501-char chat replies, URL credentials, counter cap, timer re-enable, webhook mask, Linux dead-code build. - Phone layouts for the editor and builder, top-anchored modals, Esc closes an open chip first, menu resize crash.
…y deck - Chat steps wait for Twitch's answer (USERSTATE or a msg_* NOTICE), so a message refused for slow mode, a duplicate or a ban fails its step with Twitch's reason instead of passing. - The flap guard keeps each destination's newest held-back event and sends it once the window ends if it changes what was last reported. - The MIDI listener also opens the devices integration pads were learned on, and every device during an integration learn; presses from those devices never reach a delay action. - The Controls MIDI learn refuses a pad an integration uses and says which; pad conflicts are checked by overlap (any-device bindings included) through model::shortcut_owner and pads_overlap.
Catalog rows sized to their tiles (auto rows shrank to share the grid's height and cut off the buttons), Add tinted in each tile's colour instead of solid cyan, descriptions clamped to three lines, and shorter copy for Command from a website, Clip button and the Twitch note.
…l label - Events for the delay's whole life: armed, ready, cancelled (on top of on, changed and off), from Controller::phase each engine tick. - New "Delay status" alert: one Discord message that follows them all. - Delay steps can disarm; a delay changed by an integration toasts as "Integration", not "Hotkey". - Builder: a trigger's switch and Remove sit under its name (the confirm no longer overflows), alike triggers are numbered, integration-wide settings are grouped apart, every block says what it does, and an empty trigger says how to start. - Simple editor: many moments wrap as chips that keep their switches. - Segmented controls wrap with a row-aware indicator; message boxes and indicators follow window resizes. - Integrations are labelled experimental in the tab and the changelog.
…iew fixes Stream path: - Stopping in OBS airs the rest of the delay, then ends (also an unprotected crash); End now and Cut delay cut it short; restarting OBS mid-tail reconnects at the delay. Supervisor waits for the tail. - OBS ends Enhanced Broadcasting streams with SequenceEnd tags stamped 0: expand_ts pins steps back over 10 s instead of rewinding the timeline. - After a freeze hold ended (End now or expiry), a later pump or cut never seeds on a pre-freeze keyframe (fresh_from_seq), so nothing re-airs. - A 9:16 destination gets a grace period before teardown and is never torn down during a hold, so OBS coming back doesn't drop it. - Egress backoff resets after a clean session of 5 s or more; one that ends at once backs off instead of redialling every second. Switching protection off clears the freeze latch. Security: - Program and File steps can only be saved, switched on or imported from the streaming PC itself, or with a dashboard password (UI shows them as "Streaming PC only" with the reason). - Chat values in program arguments keep only letters, digits, spaces and _ . , : # + = / -, collapse "..", and can't start with an option. - Web request addresses are masked like webhook links. Integrations: - Import preview lists each trigger with who can fire it, and what it reaches (programs, files, delay changes from anyone in chat in red). - !setdelay takes whole seconds only (new "is a whole number" check); 0 turns the delay off. - Shared Chat partners' messages are ignored unless the new Connections > Twitch option is on. - Dock's dead Discord webhook row now opens the Integrations tab (/#integrations); End now answers 409 when nothing is left to end. - Message boxes keep their height across redraws; busy buttons stay disabled when a redraw asked for it. Also: 10 streamer-profile pump sims, stream-day session tests, issue form for integrations, experimental note with a Report it link, CHANGELOG and README for 0.1.15, version 0.1.15. 843 tests.
Each integration in a recipe being imported gets two folds: "See every step" lists its triggers and steps in the builder's words (checks with their branches nested, risky steps tinted), and "Show the raw settings" shows the exact JSON that would be added. Steps whose program, file, address or OBS scene a recipe can't carry say the importer picks it. The disarm action now reads "Turn the delay off", which is what it does on air too.
Each integration of a recipe is a card whose edge shows the worst it can do, easing in after the one above. "See every step" and "Raw settings" are rows that open smoothly (interpolate-size, plain open elsewhere), remember their state across redraws, and count the steps. Steps get numbered badges tinted by risk, triggers a bolt, the raw settings colored JSON with a Copy button. Reduced motion turns the animations off.
…box, UI edges) Stream path: - The never-re-air floor after a freeze only applies when the hold was already over (ended or run out) before OBS recovered, or protection was switched off mid-freeze. A short freeze OBS recovered from in time no longer makes a new destination wait out the whole delay (new sim). - On a full ring after a freeze, a destination waits for the ring to roll past the freeze instead of joining far short of the delay and looping. Security: - Program and File steps need a dashboard password while the dashboard is open to the network or reached through a reverse proxy (X-Forwarded-For, Forwarded, Via, X-Real-IP); a page loaded from this PC is no longer enough. Catalog entries with a File step follow the same rule. A proxied request can't set the first password either. - Overlay pages are served with CSP sandbox allow-scripts: they still paint from the open feeds but can't act as the dashboard. - Chat values in program args are ASCII only (no best-fit look-alikes), lose : and #, and no word of them can start with - or /. - !setdelay with a number too big to read caps at 600 s. - Logins in web addresses no longer show in step summaries. UI: - A secret field's label focuses the field instead of revealing it. - Save settles back to disabled after a builder save. - Locked integrations: the card switch, Ctrl+S and the unsaved bar say why instead of doing nothing; the footer explains on tap. - Delay action labels match the builder (Turn the delay off, Back to live now); dock and banner say the same thing about Normal Mode; capped stagger delays; Copy no longer covers the raw settings; #integrations is cleared after opening the tab; Needs OBS setup fits the card.
Security: - Every web request InstantClone makes carries Via: 1.1 instantclone, so a step aimed at the dashboard itself counts as proxied: it can't set the first password or a Run a program step (tested end to end). - Setting up local steps, and the first password, also need the Host to name loopback: a port forwarder on this PC (netsh portproxy, ssh -R) connects from loopback but keeps the dialled address. - The Studio bake escapes every < in its stylesheet (\3c) and the data-kind attribute, and drops widgets of unknown kinds, so an overlay file planted in the folder can't break out of <style> and run on the dashboard's origin. The ten built-in overlays bake unchanged. - program_arg keeps a leading - only on a real negative number (-5x goes). - maskUrl hides a login up to the last @ (user:p@ss@host). Stream path: - The replay floor is also set when the delay has moved past everything sent before the freeze: a freeze longer than the delay that OBS recovers from in the hold no longer replays its last GOP to a reconnecting destination and sits silent across the gap (new sim; it fails without the fix). - Switching protection off mid-freeze ends the hold, as an OBS drop without protection would, instead of leaving the screen up.
The stagger animation ended at opacity 1, which overrode a switched-off destination's dim look until it finished, so the card flashed bright for a moment and then snapped grey. It now fades to each card's own opacity.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
0.1.15
Everything since 0.1.14, including what shipped in the
0.1.15-rc.1pre-release (thefeature/crash-protectionbranch, merged in here).Highlights
Security
Review
Three review rounds (core, security, UI, dock) before release; every finding fixed, with tests. 846 tests pass, clippy and fmt clean.
Full details in
CHANGELOG.md.