Mentra glasses video ingestion for Readwise workflows. The app starts a direct RTMP stream from Mentra Live, records it on your server, remuxes the recording to MP4, extracts an M4A audio track, stores both files, and creates a pending review draft. Nothing is sent to Readwise until a reviewer approves it.
The frame extraction/OCR path is intentionally a separate worker step, and this repo includes that worker. The web process stores video/audio first; the worker then extracts frames, scores and dedupes them, OCRs marked passages, transcribes optional voice notes, resolves book identity, and creates Telegram review candidates.
- Start the mini app in Mentra.
- Press the glasses button to start direct RTMP recording.
- Press the glasses button again to stop and save.
- The server stores the MP4 and optional M4A, then sends a Telegram draft notification if Telegram is configured.
- The separate worker processes queued recordings to extract frames, transcribe audio, and build candidate highlights.
- Approve only after the highlight text is correct.
The glasses session path is video-only. Manual video upload remains available at /webhooks/mentra/book-video.
npm install
npm run setup
npm run doctor
npm run dev:allnpm run setup copies .env.example to .env if needed and creates local runtime directories. Edit .env, then run npm run doctor again until required production checks pass.
For separate production-style processes:
npm start
npm run worker:videoFor one queued video draft:
npm run worker:video:onceFor checks:
npm test
npm run check
npm run test:sandboxThe web process records/remuxes streams; the worker owns frame extraction/OCR and should be deployed separately for production. npm run dev:all runs both together for local testing.
For the complete worker setup, provider choices, and deployment shape, see Full Pipeline.
The repo includes .env.sandbox.example for safe local/CI tests with fake keys and external providers disabled:
npm run test:sandboxSandbox mode runs doctor, syntax checks, and the full test suite without calling Mentra, Telegram, Readwise, OpenAI, OpenRouter, or OpenClaw APIs. Real API keys belong only in your local .env or in GitHub Actions secrets for your own private integration workflow.
Use Node.js 20, 21, 22, or 23 for local checks. The CI workflow runs on Node 20.
MENTRA_PACKAGE_NAME: Mentra package name, for examplecom.alerio.mentra.bookreadwiseMENTRA_API_KEY: app API key from the Mentra Developer ConsolePUBLIC_URL: HTTPS base URL for the appREADWISE_REVIEW_TOKEN: private review/dashboard token in productionREADWISE_RTMP_INGEST_ENABLED=1: enables direct RTMP recordingREADWISE_RTMP_PUBLIC_HOST: public host the glasses can reach for RTMP ingestREADWISE_RTMP_INGEST_SECRET: stable signing secret for RTMP publish URLsREADWISE_MEDIA_TOKEN_SECRET: signing secret for private Telegram media linksREADWISE_READING_CONTEXT_PATH: optional JSON path for active/recent book context; defaults todata/reading-context.jsonREADWISE_APPROVAL_ENABLED=1: allows Telegram approval callbacks to write approved highlights when live writes are also enabledREADWISE_TELEGRAM_FORWARD_TOKEN: shared secret for your Telegram webhook forwarder
READWISE_LIVE_WRITES=0 is the safe default. Set READWISE_TOKEN and switch READWISE_LIVE_WRITES=1 only after approval tests pass.
Readwise highlights need a real book title and author. This app never sends Unknown Book to Readwise: candidate approval is blocked until title and author are known.
When the worker confirms a book title and author from voice or vision, it saves that book to reading context with first/last seen timestamps, recent pages, recent draft ids, and a seenCount. Later recordings resolve book identity in this order:
- Confirmed title/author from voice or vision.
- Active-book fallback when the new clip has no title/author or a close match.
- Reading-history fuzzy match when OCR/voice has a partial title or author and one prior book clearly wins.
- Approval blocked when no reliable book can be chosen.
Fallbacks are marked in draft metadata and Telegram review cards as reading_context or reading_context_history, so reviewers can see when the book came from memory instead of the current clip.
Recorded stream drafts use this shape:
sourceKind: "book_video"target: "readwise"storage: stored MP4 metadataaudioStorage: stored M4A metadata when audio extraction succeedsprocessing.mode: "queued"until the worker processes the recordingframeUrls: populated only by the separate workermetadata.bookIdentity: title/author plus source, for examplevoice_transcript,vision_ocr,reading_context, orreading_context_history
Private media links are served from /media/private/:filename and can be opened with either a review token/cookie or a short-lived mediaToken query parameter.
POST /webhooks/mentra/book-videoPOST /webhooks/mentra/book-pagefor legacy/manual page draftsPOST /webhooks/mentra/book-page/:id/process-videoPOST /webhooks/mentra/book-page/:id/approvePOST /webhooks/mentra/book-page/:id/rejectGET /reviewGET /api/highlightsGET /api/mediaGET /api/sessionsGET /health
- Do not commit
.env,data/, recordings, pending highlight stores, or Telegram/Readwise/Mentra tokens. data/reading-context.jsoncan contain private reading history. Keep it out of git unless you intentionally export a redacted sample.- RTMP ingest needs a reachable TCP port. If a reverse proxy is in front of HTTP, RTMP still needs direct TCP routing.
- The app defaults to a long-stream profile for reliability: 1280x720, 30 fps, 2.5 Mbps video, and 128 kbps audio. For short high-quality tests, override
READWISE_STREAM_*to 1920x1080 and a higher bitrate.
