Commit Graph

18 Commits

Author SHA1 Message Date
Keysat 621af7ca14 Add self-contained shareable HTML export for YouTube recaps
New 'Share page (HTML)' entry in the Export menu generates a single
standalone .html file: the recap record inlined as JSON plus a small
baked-in renderer that reproduces the embedded YouTube player and the
expandable timestamped summaries. The recipient opens it with no account
and no calls back to the server. On mobile it hands the file to the
native share sheet (navigator.share with files); on desktop or where
unsupported it downloads.

YouTube only by design — podcasts have no portable audio and are
rejected with a toast. The generator runs inside index.html's own
<script>, so closing tags are split and inlined JSON escapes '<' to
avoid premature script termination.

Ship as 0.2.160.
2026-06-16 07:57:30 -05:00
Keysat cb961cd2d9 Accept YouTube /live/ and /shorts/ URLs in extractVideoId
The video-id regex only matched /watch?v=, youtu.be, /embed/, and /v/
forms, so youtube.com/live/<id> and youtube.com/shorts/<id> links were
rejected with "Invalid YouTube URL". Add both forms to the server and
frontend extractors (kept in sync) and cover them with tests.

Ship as 0.2.159.
2026-06-15 23:29:57 -05:00
Keysat b4fa5d7be8 Add opt-in Daily Digest (daily email of last 24h of library recaps)
Multi-mode, off by default. Each new recap is synthesized into a 1-2
paragraph overview via the relay (operator-absorbed) and cached onto the
session JSON; a daily 08:00 scan emails opted-in users their fresh
recaps, deduped by a per-user watermark that never skips a failed or
over-cap recap. One-click tokenized unsubscribe; settings-modal toggle;
admin test trigger. Bumps to 0.2.158.
2026-06-15 19:50:48 -05:00
Keysat 693bb981ff Fix mobile/UX bug cluster: video minimize, audio interrupt, scroll reset, redundant box
Four fixes in public/index.html, all reported against recaps.cc on mobile:

- Video minimize no longer shows a black frame on expand. toggleVideoMinimize()
  used to call render(), rebuilding the YouTube iframe inside the display:none
  minimized container, which wedged the IFrame API. Minimize now toggles the
  .results-left.minimized class in place; a !videoMinimized guard on render()'s
  needsMount plus a new ensureYtMounted() (called from the expand paths) keep the
  player from ever being created in a hidden container.

- Background processing no longer interrupts podcast audio or resets the
  transcript scroll. The ~60s relay-credit poll calls render(), which rebuilt the
  <audio> element and chunks-scroll. render() now preserves the live <audio> node
  across the innerHTML swap (replaceWith when the src matches) and restores
  chunks-scroll scrollTop; initPodcastPlayer() is idempotent so the preserved node
  doesn't get duplicate listeners.

- Removed the redundant centered "Processing..." box; the staged pizza-tracker
  breadcrumb already covers that window.

- Added -webkit-overflow-scrolling/overscroll-behavior to .chunks-scroll for the
  mobile can't-scroll-to-top report (best-effort, needs on-device verification).

Ships as 0.2.157. Reviewer pass clean; inline JS syntax checked with node --check.
2026-06-15 17:38:32 -05:00
Keysat 91af0b711e Harden iOS sign-in against stale-connection POST failures
iPad users hit a spurious "network error" on the first tap of
"Send sign-in link", with a second tap succeeding. Cause is iOS
Safari dispatching the POST onto a pooled keep-alive socket the
server/proxy already closed; unlike a GET it isn't transparently
re-sent, so it surfaces as a transport TypeError. The single 500ms
auto-retry was too quick and reused the same dead socket.

Both sign-in entry points (auth.html postWithRetry, index.html
fetchWithRetry) now retry 3x with growing backoff (0 -> +400ms ->
+1.6s) to outlast Safari evicting the socket. Frontend-only.

Ships as 0.2.156.
2026-06-15 16:35:13 -05:00
Keysat 0ae59f3550 Add multi-tenant cloud mode: self-serve purchase, credit metering, core-decoupling
Introduces RECAP_MODE=multi alongside single-mode self-host:
- Tenant auth + accounts (magic-link via System SMTP), per-tenant credit pool,
  anonymous trial minting with per-IP/-64 caps
- Self-serve Pro/Max purchase: inline Lightning (BTCPay) + card (Zaprite),
  prepaid 30-day periods, expiry-reminder emails
- Core-decoupling: relay owns cloud tier/expiry keyed by Recaps user-id
- SQLite (better-sqlite3) schema for multi-mode; filesystem unchanged for single
- StartOS actions/versions through 0.2.155
2026-06-13 14:25:05 -05:00
Keysat 373d10595b Pluggable AI providers, relay credit system, picker UX overhaul
Captures roughly forty version bumps (v0.2.6 → v0.2.47) of work that
accumulated without commits.

- Pluggable provider system under server/providers/: gemini, anthropic,
  openai, openai-compatible, ollama, whisper-compatible, relay. Mix and
  match transcription + analysis per request via the picker UI.
- Relay backend integration. Hardcoded relay URL in server/relay-default.js
  (operator-controlled at build time, not user-configurable). New
  /api/relay/{status,policy} endpoints proxy to the relay; balance pings
  populate a cached credit display.
- Per-install identity in server/install-id.js for relay credit accounting.
  Sent to the relay as X-Recap-Install-Id; persists across upgrades, lost
  on a full uninstall + reinstall. Not surfaced in the UI.
- Admin login gate (server/admin-auth.js + setAdminPassword action). Scrypt
  password hash + HMAC-signed session cookie.
- Entitlement scheme rename: pro / max (each paired with subscriptions and
  relay_pro / relay_max), replacing the misleading "core" entitlement
  that conflicted with the user-facing "Core" tier name.
- Activation screen: dynamic credit count pulled from /api/relay/policy,
  "Skip — use free mode" button, accurate paid-feature list.
- Top toolbar: inline credit-balance pill (or "BYO configured" fallback),
  Upgrade + "I have a key" buttons.
- Picker UI: per-provider sections with Save/Test/Delete buttons, sections
  collapsible by chevron, default-collapsed unless currently selected,
  "Use comped credits (reset to relay)" link when the user has strayed,
  green hint under inputs whose values are server-configured.
- Activity log: chevron-collapsible groups per video, refresh-survival via
  localStorage + a 500-entry server-side buffer, explicit Clear button.
- YouTube captions fast-path with user toggle (skips audio download + AI
  transcription when captions are available — uncheck for speaker labels).
- Cancel button: AbortController plumbed through every provider SDK call;
  retryAPI short-circuits on AbortError; cancellation events surface in
  the activity log instead of silent retries.
- Long-video analysis: auto-coalesce transcript entries before building the
  analysis prompt so local-model context windows (32k-ish) don't overflow.
  Original entries preserved for transcript display via an index map; the
  analyzer sees a coarser view but click-to-seek timestamps stay precise.
- StartOS action grouping (Setup / AI Providers) so the actions list is
  navigable.
- Manifest description rewritten to reflect multi-provider support and
  free-tier relay credits.
- Smaller fixes: summarize-button enablement no longer requires a Gemini
  key when other providers are configured; analysis fallback chain handles
  context-length and 503 capacity errors; single-segment expansion for
  providers that don't return per-segment timestamps (Parakeet et al.);
  many other UX polish items.
2026-05-11 23:46:20 -05:00
Keysat 25ec40f345 Bump version to 0.2.5
Tighter license-poll cadence + opportunistic online refresh.
2026-05-09 19:36:47 -05:00
Keysat 823b9e0375 Bump version to 0.2.4
Critical fix for v0.2.3 startup crash (missing modules in Docker
image). Also refreshes vendored Keysat client to v0.2.0 ahead of
in-app buy flow.
2026-05-09 11:57:58 -05:00
Keysat fe07580a12 Bump version to 0.2.3
New 'Set Recap License' StartOS action; split server/index.js into
8 focused modules (util, gemini-helpers, audio, ytdlp, cookies,
config, license-middleware, history); 'Recap license' copy fix.
2026-05-09 07:06:36 -05:00
Keysat 3bd50f8429 Bump version to 0.2.2
Live-reload of the Gemini API key, and fix a startup-crash bug in
0.2.0 / 0.2.1 where the vendored keysat client could not resolve its
@noble/* deps inside the running container.
2026-05-08 16:38:44 -05:00
Keysat eb152cc97c Bump version to 0.2.1
Reword short description on the registry card.
2026-05-08 14:13:44 -05:00
Keysat 2c2ccfae05 Bump version to 0.2.0
Renamed to Recap. New StartOS package id, new Keysat product slug,
new display name everywhere user-visible. StartOS will treat this as
a fresh install.
2026-05-08 13:35:47 -05:00
Keysat 1aaa7a453a Bump version to 0.1.18
Fix Settings modal failing to open for licensed users.
2026-05-08 13:00:29 -05:00
Keysat f6c1a1e830 Bump version to 0.1.17
See pending notes
2026-05-08 12:26:53 -05:00
Keysat 8d9b384e32 Bump version to 0.1.16
Release notes:
  Add free mode for unlicensed users (one video at a time, no library).
  Online license revocation check via Keysat /v1/validate.
  Fix blank-screen bug after processing long videos.
  Ship deploy-script polish (no double-bump prompt).
2026-05-08 11:34:28 -05:00
Keysat 574a16d9fa Save in-progress keysat integration and StartOS 0.4 work
Snapshot of the working tree before cleanup. Captures:
- Keysat licensing: server/license.js, /api/license/* endpoints in
  server/index.js, activation modal in public/index.html, embedded
  Ed25519 issuer key (assets/issuer.pub).
- StartOS 0.4 expansion: setApiKey action, version files v0.1.1
  through v0.1.15, file-models/config.json.ts, manifest updates.
- Self-hosted registry server (startos-registry/).
- Build/deploy scripts (bin/bump-version.sh, bin/deploy.sh, vendored
  yt-dlp binary), .gitignore, .deploy.env.example.
- Recent design docs (KEYSAT_INTEGRATION.md, UPGRADE-DESIGN.md) —
  retained here so they remain recoverable when removed in the
  follow-up cleanup commit.
2026-05-08 09:39:17 -05:00
MacPro 68ec875ee7 Add StartOS 0.4.0 packaging 2026-04-09 15:03:31 -05:00