We are looking for private testers before official release. Contact us on X @Jencryzthers

Changelog

What shipped

Full history from client/CHANGELOG.md - Keep a Changelog, semantic versions, TestFlight build numbers. 9 releases, 125 notes, April 2026 → 1.0.1.

Current
1.0.1
Build
158
Releases
9
Notes
125

Recorder resume, remote storage

Admin impersonation actually scopes the console. Recordings keep going when a live ingest drops, and finished files can move off the box.

Added

  • Optional HEVC CRF 23 compress of finished recordings (Storage toggle, off by default). Same resolution, fMP4 HLS that AVPlayer can play.
  • Remote storage via rclone: finished .grec files upload, verify, drop the local copy, and play back in byte ranges.
  • Recorder resume: if ingest dies mid-stream, the same recording continues instead of finalizing. No 4 hour cap.
  • Admin impersonation now filters Dashboard, Devices, Recordings, Rules, Storage stats, Twitch, Pairing and MCP to that account.

Changed

  • Linking Twitch is one sign-in sheet (Settings, onboarding, expired banner).
  • The server admin web matches the app and is fully translated in French.

Fixed

  • Auto-record did not restart after an ingest drop. Manual Stop stays manual.
  • Operator pair-code devices could not set the storage cap.
  • Downloads listed only the first 50 recordings.

Twitch link sheet, recording cards

One Twitch sign-in sheet everywhere, stream-title recording names, and a handful of playback and Downloads fixes.

Added

  • Linking Twitch is one sign-in sheet everywhere: Settings, onboarding and the expired-session banner. Twitch's own login page fills the sheet, linking starts as soon as you are signed in, and pasting a token stays available as a fallback.
  • Offline channel pages show the latest recording, or a player placeholder saying there is no video right now.
  • Avatars, box art, banners and emotes shown larger than their source are upscaled with MetalFX.

Changed

  • Recordings are named after the stream title, not the streamer name.
  • Recording cards look like every other card, and every page uses the Following page card size.
  • The server admin web matches the app design and is fully translated in French.

Fixed

  • The emote picker was empty: the emote catalog now comes from Twitch GraphQL.
  • Downloads listed only the first 50 recordings.
  • The image upscaler swapped red and blue on some images. It can be turned off with glitcho.images.aiUpscale.
  • The latest-recording play button now sits where the featured VOD card puts it.

First 1.0

Playback reliability, portrait player, Downloads, chat, chat replay, offline, sidebar, upscaler v3, per-account isolation, page layout, localization.

Added

  • Chat replay: recordings capture Twitch chat (encrypted sidecar) and play it back read-only, in sync with the video.
  • Emote/emoji picker in the composer; Twitch emotes render inline; your own messages appear immediately with a pending/failed state.
  • Recording favorites (star) that the server refuses to delete; Favorite/Delete from the recording player page.
  • Offline mode: persistent disk image cache for every thumbnail, avatar and box art; live streams play straight from Twitch's CDN when the Glitcho server is unreachable.
  • AI upscaler v3: Auto / Ultra / Balanced / Performance (linear-light chain, deblock/deband, motion-compensated temporal accumulation, adaptive sharpening) plus a debug HUD.
  • Sidebar live refresh loop plus server stream_online / stream_offline push.
  • Collapsible groups in Downloads. Category box art in Search. Drops via Twitch persisted queries.
  • One page header and margin density on every page (PageHeader, pageContainer).

Fixed

  • “Cannot be played, hit retry”: client retries with backoff before showing an error; server waits for the first segment (503 + Retry-After otherwise) and caps concurrent spawns.
  • Hero, hover previews and clips no longer go through the streamlink→ffmpeg relay - they use direct CDN URLs (cached), which was starving real playback.
  • A transient empty stream map for a clip no longer surfaces as “Twitch restricted this media” (451); it is retried and reported as 503.
  • Category streams: the category name was percent-encoded twice; a logging bug turned a null game into a 500.
  • Whole-screen 2 Hz rebuilds on every media route; Catalyst window snapping back mid-drag on launch.
  • Portrait iPhone: primary controls always visible with 44 pt targets, secondary in a menu, safe area respected.
  • Mass delete did nothing (selection of active recordings, premature local reconciliation); bulk sheet is now closable and auto-dismisses.
  • Hover preview hid the card actions. Thumbnails never loaded when the placeholder was empty.
  • Recording poster is taken from the middle of the stream, never the intro.
  • 225 UI strings that were missing from both catalogs (French shows French now); “Categories” no longer shouts.

Security

  • Per-account isolation: admin web is admin-only, per-user MCP tokens and scoped tools, Twitch cookie route admin-only, storage events per user, full cascade on account deletion, AUTOINCREMENT ids (a deleted account's bearers can no longer authenticate as the next signup).
  • Independent audit: PASS.

Player zoom, upscaler fidelity

Player zoom actually works without the AI toggle, and the upscaler no longer wrecks the picture.

Fixed

  • Zoom without sparkles did nothing: layoutSubviews reset AVPlayerLayer.frame over the affine transform. Zoom now enlarges the layer and clips.
  • AI upscaler stretched 16:9 into a taller view. Destination is letterboxed to the crop aspect.
  • AI at 1× 1080p was a MetalFX 2×-then-down soup plus temporal smear. Now YUV BT.709, MetalFX only up to dest, mid-frequency clarity + RCAS. Sparkles on always takes this path.

Hover-to-play and self-host server

Player, Mac Catalyst polish, and a matching self-host server.

Added

  • Hover-to-play on stream / VOD / clip / recording cards (pointer only, muted HLS, stops on leave).
  • Right-click Record this stream (one-shot, not the auto-record allowlist).
  • In-player AI upscaler (sparkles toggle, off by default): MetalFX spatial chain, 2× overshoot at 1×, CAS + temporal history.
  • Player zoom/pan for live, VOD, clip and recording: pinch, wheel, double-tap, - / % / +, max 5x.
  • Fullscreen control restored on the player chrome (in-window theater).
  • Collapsed Mac sidebar fits traffic lights; For You stories stay a single stacked disc.

Fixed

  • Home “unexpected response” + sidebar Not signed in when prod lacked /v1/account/twitch/status (setup fallback, no identity wipe, lossy decode).
  • Native VOD black screen (Twitch EVENT+ENDLIST treated as live edge; unmuted prefix 403). Playlist rewrite coerces VOD and swaps to public muted segments.
  • Zoom-out glitch when the upscaler stole AVPlayerItemVideoOutput from the player layer.
  • Catalyst PiP eligibility and Mac audio-session hogging (mixable session, attach PiP in-window).
  • Downloads bulk delete / favorite retention paths.

Server

  • Account isolation for follows, recordings, Twitch status, featured cache, chat mods (self-serve vs operator).
  • GET /v1/account/twitch/status plus client fallback to /v1/setup/twitch/status.
  • Recording thumbnails (skip black JPEGs) and is_favorite.
  • Twitch chat send via GQL SendChatMessage (fail-closed if unlinked).

P0 TestFlight follow-up

Fixes for TestFlight build 48 feedback (10 new reports submitted while build 49 was in flight). Plan validated before execution.

Fixed

  • Auto-record allowlist wipe regression, partial guard: put_rules now preserves the server-side allowlist when a legacy client PUTs mode pinned / followed / pinned_and_followed with an empty DTO allowlist. Empty in the DTO from a legacy schema means “not populated”, not “user picked empty”.
  • French localization not applying. CFBundleLocalizations [en, fr] added; Settings → Language writes AppleLanguages. Restart prompt on toggle.
  • Foreground notifications silently suppressed. GlitchoNotificationDelegate installed at launch; Settings → Notifications shows permission state with Allow / Open Settings / Send test.
  • Double Twitch relink prompt. 30 s debounce per (event_name, user_id) on twitch_relink_needed. Three publishers (GQL 401, streamlink 401, session_service) now dedup at the bus.
  • Activity feed silently empty. Server emits twitch_session_invalid when the initial snapshot can't reach Twitch; client maps it to a relink banner. Activity header dot: green “Live updates on” / orange “Reconnecting…”.
  • Activity filter now persists in UserDefaults (glitcho.activity.filter).
  • Recording section copy wrap. “Record every channel below as soon as it goes live” → “Auto-record the channels listed below.” No .lineLimit clipping - accessibility preserved.

Library, haptics, App Store bundle

Quality-of-life polish plus the App Store submission documentation bundle.

Added

  • HapticEngine semantic-event vocabulary: recordStart (ascending triple-tap), recordStop (descending double-tap), privacyLock (heavy thud + 150 ms purr), privacyUnlock (ramping continuous + sharp success tick), subscribeStart, streamWentLive. Falls back to UIFeedbackGenerator on iPad without Taptic, no-op on Mac Catalyst, NSHapticFeedbackManager on native macOS.
  • LocalCache actor in GlitchoServerClient. Persistent JSON cache under ~/Library/Caches/GlitchoCache. 600 s TTL, 300 s stale window. Wraps followedStreams / recordings / channelInfo / twitchStatus. Falls back only on transport-level serverUnreachable.
  • Downloads grouping picker - None / Streamer / Date / Type. User-selected sort still orders items within each section.
  • Recording kind field (stream | vod | clip) on the server model + DTO + DownloadsScreen sections. Backwards-compatible decode, default “stream” for legacy rows.
  • Pinch-to-zoom + double-tap-to-zoom on the player. simultaneousGesture so native controls (volume, scrubber, AirPlay, PiP) keep working. Snap detents at 1×, 2×, 3×.
  • App Store submission documentation bundle: App Review notes, publishing playbook, privacy policy, SECURITY.md, MILESTONE.md, Keep-a-Changelog.

Changed

  • Sidebar reveal flow: detail nav bar is hidden only when the sidebar is fully visible. Collapse to .detailOnly or .doubleColumn and the system sidebar-toggle comes back.
  • Watch panel layout: Record moved into the … overflow menu (Stop becomes destructive when a recording is running). Viewer count / uptime / REC sit inline in the meta column. Compact-width stacks the meta block so the streamer name and title get full width.
  • Library / Pinned localized strings dropped baked-in ALL-CAPS - cosmetic uppercase is applied at the sidebar groupHeader, so the tab bar shows “Library” in Title Case.
  • Settings title no longer slides under the Mac Catalyst / native macOS traffic-light cluster. CompactTabShell applies a 28 pt safeAreaInset(.top) on Catalyst + macOS only.
  • Server scalability (Tier 0): SQLite WAL + synchronous=NORMAL + 5 s busy_timeout. Recorder per-segment AES + DB-write batch moved to asyncio.to_thread so the API stays responsive while a recording crunches chunks.
  • Repo reorg: Sources/ + Apps/iOS/ → apps/{ios,mac,server-bar}/ + shared/, propagated through imports, scripts, and tests.

Fixed

  • Recordings split into ~2-minute slices. The recorder now heartbeats the ingest runner on every poll tick so the streamlink janitor doesn't reap ingest after IDLE_TTL_SECONDS=120 when no client is also watching live. Recordings stay continuous. Regression guard: test_recorder_heartbeats_ingest.
  • Activity feed sat at “Waiting for activity” forever for viewer-mode users. /v1/events was silently filtering stream_online; that envelope is allow-listed AND a snapshot of currently-live followed channels is emitted on WS connect.
  • LIBRARY rendered in ALL CAPS in the iPhone tab bar. Uppercase now lives on a .textCase(.uppercase) modifier on the sidebar header only.
  • “Exit theater” button moved to the top-leading corner of the player so it no longer overlaps “Restart stream” at the top-trailing.
  • AVAudioSession setup failed debug print in PlaybackController.swift gated behind #if DEBUG so production binaries don't log to the device console.
  • Privacy unlock grace period survives navigation. Unlock state lifted from per-screen @State into AppCoordinator; grace timer honoured across route changes.

Server

  • Recorder heartbeat + per-segment threadpool offload.
  • /v1/events SERVER_EVENTS allow-list extended with stream_online + WS-connect snapshot.
  • auto_recorder poll cadence 60 s → 15 s (bounded by Twitch GQL ingest latency).
  • Recording.kind column + migration.
  • SQLite WAL + synchronous=NORMAL + busy_timeout=5000.
  • Dockerfile reordered so Playwright + Chromium download survives source COPY.

Adaptive chrome, recorder, Activity

Cross-platform adaptive chrome, recorder stability, and a usable Activity feed.

Added

  • Cross-platform compact-width detection. Custom \.glitchoCompactWidth environment value driven by a GeometryReader at the chrome root. Same boolean on iPhone, iPad (Slide Over / Stage Manager), Mac Catalyst (resized windows), and native macOS. Threshold: 700 pt.
  • iPhone-class CompactTabShell now also activates on resized Mac Catalyst and native macOS windows - not just iPhone.
  • Pinch-to-zoom + double-tap-to-zoom on the player. Works on the native-controls path (AVPlayerViewController) and the chromeless path via simultaneousGesture. Snap detents at 1×, 2×, 3×.
  • LocalCache actor in GlitchoServerClient - persistent JSON cache under ~/Library/Caches/GlitchoCache. Fallback for followed-channels, recordings, channel metadata, and Twitch session status when the server is temporarily unreachable. 5-minute stale window.
  • Recording kind field (stream | vod | clip). Threaded from the server Recording model to the iOS UI so Downloads can distinguish live recordings from past-broadcast / clip downloads.
  • Downloads grouping picker - None / Streamer / Date / Type. Sort applies within each section.
  • HapticEngine semantic-event vocabulary in GlitchoCore/Haptics: recordStart, recordStop, privacyLock, privacyUnlock, subscribeStart, streamWentLive.
  • Activity feed snapshot on /v1/events WS connect. Emits stream_online envelopes for currently-live followed channels so Activity is useful immediately.
  • Sidebar reveal button when the sidebar is collapsed - the system's NavigationSplitView toggle is exposed on iOS / Catalyst when relevant.
  • Privacy lock survives navigation. Unlock state lifted into AppCoordinator; grace timer honoured across route changes.
  • Watch panel rework. Record button moved into the … overflow menu. Viewer count / uptime / REC inline with the channel meta block. Meta stacks vertically on compact widths.

Changed

  • Repo reorg: Sources/ + Apps/iOS/ → apps/{ios,mac,server-bar}/ + shared/. Existing imports and Package.swift target paths updated; xcodegen regenerated.
  • Settings / Library / Pinned localized strings dropped ALL-CAPS values - cosmetic uppercase is applied at the sidebar section-header render site rather than baked into the .strings file.
  • Settings title no longer slides under Mac Catalyst / native macOS traffic-light cluster. CompactTabShell applies a 28-pt safeAreaInset(.top) on Catalyst + macOS only.
  • “Exit theater” button repositioned to the top-leading corner of the player so it no longer overlaps “Restart stream”.
  • POLL_INTERVAL_SECONDS for the auto-recorder lowered from 60 s to 15 s. Auto-record now catches an offline→live transition within ~15-30 s of Twitch's GQL ingest-to-visibility latency.

Fixed

  • Recordings split into ~2-minute slices. The recorder now heartbeats the ingest runner on every poll tick so the streamlink janitor doesn't reap the ingest after IDLE_TTL_SECONDS=120 while no client is also watching live.
  • Activity feed stayed at “Waiting for activity” forever for viewer-mode users. /v1/events was silently filtering stream_online; that envelope is allow-listed AND a snapshot of currently-live followed channels is emitted on WS connect.
  • LIBRARY rendered in ALL CAPS in the tab bar. Uppercase now lives on a .textCase(.uppercase) modifier on the sidebar header only.
  • Server response stalls under load. SQLite WAL mode enabled (PRAGMA journal_mode=WAL, synchronous=NORMAL, busy_timeout=5000). Recorder per-segment AES encryption + chunk-write batch moved to asyncio.to_thread.
  • Glitcho-server Dockerfile cache miss on every deploy. pip install playwright + playwright install chromium reordered into its own layer BEFORE the source COPY, so Python source edits no longer trigger a ~300 MB Chromium re-download.

Server

  • Multi-user accounts (argon2id password hashing).
  • 2FA TOTP enrollment + recovery codes.
  • Passkey (WebAuthn) registration + sign-in.
  • Multi-user-aware admin web UI.
  • Scripts/deploy_remote.sh for one-command Docker-host installs.

Build-out of the original roadmap

TestFlight-only iterations during the original 28-phase roadmap. Full pre-0.2.0 history lives in the archived planning docs and git log.

Shipped across builds 1-31

  • SwiftPM scaffold and the shared design system.
  • Pairing over Bonjour, QR, and a manual server URL.
  • Shared UI components used by iPhone, iPad and Mac.
  • Live, recordings, Activity and Downloads screens.
  • Mac enhancements: motion interpolation, 4K upscaler, neural clarity, clip extractor.
  • iPad shell.
  • Twitch link flow.
  • Onboarding and status banners.
  • Localization passes (English + French).
  • Hero carousel with live previews.
  • Route stability, hero / billboard refinements, privacy-mode hardening.

Roadmap

  • Clips - cut and export a range from any recording.
  • iOS companion polish for watching the library remotely.