Skip to content

broomva/hexu

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

hexu

A self-hostable clone of Huxe — the AI audio app shutting down 2026-05-28. Produces a daily personalized podcast briefing from your Gmail, Google Calendar, and RSS feeds, with two-host NotebookLM-style dialogue and an RSS feed any podcast client can subscribe to.

Built on OSS components: Podcastfy for the audio pipeline, edge-tts (free, keyless default) or your own local OmniVoice Studio for voices, LiteLLM for model routing (Gemini 2.5 Pro by default, Claude as fallback), and the standard Google API client for inbox + calendar access.

Quickstart

# 1. install
git clone https://github.com/broomva/hexu && cd hexu
uv sync

# 2. configure (edit values)
cp .env.example .env

# 3. authorize Gmail + Calendar (one-time)
uv run hexu setup-oauth

# 4. run the first briefing
uv run hexu brief

# 5. serve the feed locally + subscribe in any podcast app
uv run hexu serve
# → http://localhost:8080/feed.xml

Architecture

sources → context → dialogue → audio → publish
─────     ───────   ────────   ─────   ───────
Gmail              LiteLLM     TTS     RSS feed
Calendar    →      Gemini 2.5  Sesame  feedgen
RSS feeds          (Claude     Kokoro  Local / S3
                    fallback)  Podcastfy

Each stage is a pluggable layer:

Stage Module Default Alternatives
Email fetchers.gmail Gmail API
Calendar fetchers.calendar Google Calendar API
News fetchers.rss feedparser
LLM dialogue.generator gemini/gemini-2.5-pro any LiteLLM provider
TTS tts.* podcastfy omnivoice (local, recommended), sesame (GPU), kokoro (CPU ONNX)
Feed feed.publisher feedgen → iTunes-compatible RSS minimal RSS fallback
Storage feed.storage local s3 (AWS, R2, MinIO)

Configuration

All settings load from environment variables or .env (see .env.example).

Variable Required? Default Meaning
GEMINI_API_KEY yes (if Gemini) "" Google AI Studio key for Gemini 2.5 Pro
ANTHROPIC_API_KEY optional None Claude fallback
GOOGLE_OAUTH_CLIENT_SECRET_PATH yes ./client_secret.json Downloaded from Google Cloud Console
GMAIL_LOOKBACK_HOURS no 24 How far back to scan inbox
CALENDAR_LOOKAHEAD_HOURS no 24 How far forward to scan calendar
RSS_FEEDS no [] JSON array of feed URLs
TTS_PROVIDER no podcastfy omnivoice / podcastfy / sesame / kokoro
OMNIVOICE_BASE_URL no http://127.0.0.1:3900 OmniVoice backend (omnivoice only)
OMNIVOICE_STEPS / _LANGUAGE / _SPEED no 16 / Auto / 1.0 OmniVoice tuning
VOICE_HOST_A, VOICE_HOST_B no provider-specific Voice IDs
OUTPUT_DIR no ./output Audio, manifest, feed.xml
FEED_BASE_URL no http://localhost:8080/ Base URL embedded in feed
LLM_MODEL no gemini/gemini-2.5-pro LiteLLM provider/model
STORAGE_BACKEND no local local or s3
S3_BUCKET yes for S3 None Bucket name
USER_NAME no friend Threaded into the dialogue

TTS provider selection

omnivoice (recommended — the self-owned keystone) — Points the clone at a local OmniVoice Studio backend (FastAPI on 127.0.0.1:3900). Voice cloning, 646 languages, free at any volume, nothing leaves your machine. This is the functional path that makes the stack fully durable — no rented voice, no per-character billing. Start the backend, then:

# one-time: install the skill helpers + start the backend
npx skills add broomva/omnivoice-skill
bash ~/.claude/skills/omnivoice/scripts/start-backend.sh   # health: GET /system/info

# create voice profiles in the UI (localhost:3901), note the profile_ids, then:
#   TTS_PROVIDER=omnivoice  VOICE_HOST_A=<profile_id_a>  VOICE_HOST_B=<profile_id_b>
uv run hexu brief

If the backend is unreachable the renderer raises an actionable error (or emits a silent placeholder when HEXU_ALLOW_SILENT=1). Tune OMNIVOICE_STEPS (8 draft / 16 balanced / 32 quality), OMNIVOICE_LANGUAGE, OMNIVOICE_SPEED.

podcastfy (zero-setup default) — Works out of the box, no GPU required. Hands the rendered transcript to Podcastfy's built-in pipeline. Selects the backend via PODCASTFY_TTS_MODEL: edge (default — free, keyless, no quota), openai (needs OPENAI_API_KEY + billing), elevenlabs, or gemini. Match voice names to the model (edge: en-US-AndrewNeural / en-US-AvaNeural; openai: onyx / nova). Fastest path to your first episode — but it rents the voice (except free edge), so for full durability swap to omnivoice.

sesame — Closest open-weight model to NotebookLM voice quality. Requires a CUDA GPU and the [sesame] extras:

uv pip install -e '.[sesame]'

The first run downloads model weights (~6GB). When torch isn't installed, falls back to a silent WAV so the rest of the pipeline still produces a valid episode.

kokoro — Tiny CPU-only ONNX model. Useful when you have no GPU and want something faster than podcastfy:

uv pip install -e '.[kokoro]'

Publishing the feed

Local (recommended for testing)

uv run hexu serve --host 0.0.0.0 --port 8080
# subscribe in your podcast app to http://<your-machine>:8080/feed.xml

S3 / Cloudflare R2 / MinIO

export STORAGE_BACKEND=s3
export S3_BUCKET=my-hexu
export FEED_BASE_URL=https://cdn.example.com/
uv pip install -e '.[s3]'
uv run hexu brief

The publisher writes audio + feed.xml to the bucket, and embeds CDN URLs in the RSS.

Cron setup

# crontab -e
30 6 * * * /Users/you/path/to/hexu/scripts/cron-daily.sh

Logs land under output/logs/YYYYMMDD.log. The wrapper script runs the briefing via uv run so the right venv is picked up.

Cost estimate

Per daily episode, with Gemini 2.5 Pro and Podcastfy/OpenAI TTS:

Component Cost / episode
Gemini 2.5 Pro (script generation, ~5k input + 1.5k output tokens) ~$0.02
TTS (Podcastfy → OpenAI TTS, ~1000 words, 6-min audio) ~$0.15
Google APIs (Gmail + Calendar reads) $0 (free tier)
Storage (local) $0
Storage (S3, ~5MB / episode) <$0.001
Total ~$0.17 / day, ~$5 / month

Switch TTS_PROVIDER=omnivoice to drive the voice cost to zero with a local OmniVoice backend (free at any volume, voice cloning, 646 languages) — the recommended path. Sesame CSM or Kokoro also drive TTS to zero at the price of GPU electricity or slightly worse voice quality.

Roadmap

  • ✅ Voice cloning (via omnivoice — 3-sec reference → personalized hosts).
  • ✅ Multi-language (via omnivoice — 646 languages, OMNIVOICE_LANGUAGE).
  • Web UI (one-click subscribe, episode browser).
  • Slack / Telegram delivery (audio + transcript link instead of RSS).
  • Smart deduplication across RSS sources (cluster + summarize related stories).
  • Memory of past episodes (don't re-cover the same stories two days running).

Why this exists

When Huxe announced their 2026-05-28 sunset, there was no drop-in OSS replacement. The full background on the decision, the OSS landscape, and the rationale for each component choice is in research/notes/2026-05-22-huxe-sunset-investigation-raw.md (workspace root).

License

This project sits on top of OSS components — see each dependency's license. The hexu glue code itself is provided as-is under the same license as the parent monorepo.

About

Self-hosted, anti-churn daily AI podcast briefing — a successor to the sunset Huxe. Gmail/Calendar/RSS → two-host dialogue → RSS feed. Podcastfy + LiteLLM + local TTS (edge / OmniVoice). MIT.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors