• Townie
    AI
  • Blog
  • Docs
  • Pricing
  • We’re hiring!
Log inSign up
larryhudson

larryhudson

saveTextToPodcast

Public
Like
5
saveTextToPodcast
Home
Code
5
README.md
C
cleanup.ts
H
index.ts
C
process_chunks.ts
test.html
Branches
1
Pull requests
Remixes
History
Environment variables
3
Val Town is a collaborative website to build and scale JavaScript apps.
Deploy APIs, crons, & store data – all from the browser, and deployed in milliseconds.
Sign up now
Code
/
Code
/
Search
index.ts
https://larryhudson--e3d0db065a7711f0bc3bf69ea79377d9.web.val.run
README.md

Text-to-Speech API with Deepgram Callbacks

This HTTP Val converts text files to speech using Deepgram's TTS API with async callbacks to handle large files without timeouts.

Setup

  1. Set the DEEPGRAM_API_KEY environment variable in your Val Town settings
  2. Set the AUTH_TOKEN environment variable for API authorization
  3. The SQLite database tables will be created automatically on first use

How It Works

The system uses Deepgram's TTS callback feature to handle large text files:

  1. Text Chunking: Large texts are split into chunks of ~900 characters
  2. Async Processing: Each chunk is submitted to Deepgram with a callback URL
  3. Callback Handling: Deepgram posts completed audio chunks to /deepgram-callback
  4. Chunk Storage: Audio chunks are temporarily stored in blob storage with SQLite tracking
  5. Concatenation: A cron job checks for completed jobs and concatenates chunks into final MP3s
  6. Cleanup: Temporary chunk files are deleted after final MP3 creation

Quick Start

  1. Visit /test for a web interface to upload and convert text files
  2. Or use the API endpoints directly (see below)

API Endpoints

GET /test

Web interface for testing file uploads and conversions.

POST /convert

Starts async text-to-speech conversion. Requires authorization.

Headers:

  • Authorization: Bearer YOUR_AUTH_TOKEN

Parameters:

  • file (File): Text file to convert
  • name (string): Name/title for the episode

Example using curl:

curl -X POST https://your-val-url.web.val.run/convert \ -H "Authorization: Bearer your-auth-token-here" \ -F "file=@your-text-file.txt" \ -F "name=My First Episode"

Response:

{ "success": true, "message": "Text-to-speech conversion started", "data": { "job_id": "job_1234567890_abc123", "name": "My First Episode", "text_length": 1250, "status": "processing", "note": "Audio will be available once all chunks are processed. Check /job-status/:job_id for updates." } }

GET /job-status/:job_id

Check the progress of a conversion job.

Response:

{ "success": true, "job_id": "job_1234567890_abc123", "name": "My First Episode", "status": "completed", "progress": { "total_chunks": 3, "completed_chunks": 3, "failed_chunks": 0, "pending_chunks": 0, "percentage": 100 }, "mp3_url": "https://your-val-url.web.val.run/audio/tts_job_1234567890_abc123_1234567890.mp3", "created_at": "2024-01-01 12:00:00", "completed_at": "2024-01-01 12:05:00", "chunks": [ { "index": 0, "status": "completed", "completed_at": "2024-01-01 12:02:00", "error": null } ] }

POST /deepgram-callback

Internal callback endpoint for Deepgram to post completed audio chunks. Not for direct use.

GET /audio/:filename

Serves MP3 audio files from storage.

GET /episodes

Lists all converted episodes.

GET /stats

Get database statistics and cleanup information.

POST /cleanup

Manually trigger cleanup of old episodes. Requires authorization.

GET /podcast.xml

Generates an RSS podcast feed from all episodes. Requires authorization.

Database Schema

Episodes Table (tts_episodes_v2)

  • id (INTEGER PRIMARY KEY)
  • name (TEXT) - Episode title
  • text (TEXT) - Original text content
  • mp3_url (TEXT) - URL to the final MP3 file
  • text_length (INTEGER) - Length of original text
  • chunk_count (INTEGER) - Number of chunks processed
  • date_added (DATETIME) - Timestamp when added

Jobs Table (tts_jobs_v1)

  • id (INTEGER PRIMARY KEY)
  • job_id (TEXT UNIQUE) - Unique job identifier
  • name (TEXT) - Episode title
  • text (TEXT) - Original text content
  • text_length (INTEGER) - Length of original text
  • chunk_count (INTEGER) - Number of chunks
  • status (TEXT) - Job status: 'processing', 'completed', 'failed'
  • created_at (DATETIME) - When job was created
  • completed_at (DATETIME) - When job was completed
  • mp3_url (TEXT) - Final MP3 URL when completed

Pending Chunks Table (tts_pending_chunks_v1)

  • id (INTEGER PRIMARY KEY)
  • job_id (TEXT) - References job
  • chunk_index (INTEGER) - Order of chunk in sequence
  • chunk_text (TEXT) - Text content of chunk
  • audio_blob_key (TEXT) - Blob storage key for audio chunk
  • status (TEXT) - 'pending', 'completed', 'failed'
  • created_at (DATETIME) - When chunk was created
  • completed_at (DATETIME) - When chunk was completed
  • error_message (TEXT) - Error details if failed

Cron Jobs

Chunk Processing (/process_chunks.ts)

  • Frequency: Every few minutes (configurable in Val Town UI)
  • Purpose: Checks for completed jobs and concatenates audio chunks
  • Actions:
    • Finds jobs where all chunks are completed
    • Downloads and concatenates audio chunks
    • Creates final episode records
    • Cleans up temporary chunk files
    • Handles failed jobs and cleanup

Episode Cleanup (/cleanup.ts)

  • Frequency: Weekly
  • Purpose: Removes old episodes and associated files
  • Actions: Same as before - cleans up episodes older than 30 days

Features

  • ✅ Async Processing: No timeout issues with large files
  • ✅ Chunk-based: Handles files of any size by splitting into chunks
  • ✅ Progress Tracking: Real-time status updates via job status endpoint
  • ✅ Automatic Concatenation: Seamlessly combines chunks into final audio
  • ✅ Error Handling: Robust error handling and recovery
  • ✅ Cleanup: Automatic cleanup of temporary files and old jobs
  • ✅ Authorization: Protected endpoints for content creation
  • ✅ SQLite Storage: Reliable job and chunk tracking
  • ✅ Blob Storage: Efficient audio file storage

Security

  • POST /convert: Requires Authorization: Bearer YOUR_TOKEN header
  • POST /deepgram-callback: Validates dg-token header matches DEEPGRAM_API_KEY
  • GET /podcast.xml: Requires ?key=YOUR_TOKEN query parameter
  • All other endpoints (episodes list, audio serving, test page, job status) are public

Usage Notes

  • Uses Deepgram's aura-2-draco-en TTS model
  • Text is automatically chunked at ~900 characters with sentence boundary detection
  • Audio chunks are temporarily stored during processing
  • Final MP3 files are stored permanently in Val Town's blob storage
  • Jobs older than 1 hour are automatically cleaned up if stuck
  • All endpoints include proper error handling and logging
HTTP
  • index.ts
    larryhudson--e3…d9.web.val.run
Cron
  • cleanup.ts
  • process_chunks.ts
Code
README.md
C
cleanup.ts
H
index.ts
C
process_chunks.ts
test.html
FeaturesVersion controlCode intelligenceCLI
Use cases
TeamsAI agentsSlackGTM
ExploreDocsShowcaseTemplatesNewestTrendingAPI examplesNPM packages
PricingNewsletterBlogAboutCareers
We’re hiring!
Brandhi@val.townStatus
X (Twitter)
Discord community
GitHub discussions
YouTube channel
Bluesky
Terms of usePrivacy policyAbuse contact
© 2025 Val Town, Inc.