This document explains how to set up the HackerNews cron job on Val.Town.
The hackernews-cron.ts file contains a cron job that:
- Runs every hour
- Fetches the top 10 HackerNews stories
- Automatically generates videos for new URLs (no duplicates)
- Uses your default video generation prompt
- Skips text posts (Ask HN, Show HN, etc.) - only processes external URLs
- Go to Val.Town
- Create a new Val
- Copy the contents of
hackernews-cron.tsinto the Val - Save the Val
The cron job directly imports and uses your existing video generation logic, so it requires the same environment variables as your main application:
Required:
GEMINI_API_KEY- Your Google Gemini API key (required for video generation)
Optional:
ADMIN_PASSWORD- Your admin password (if you want to use the admin interface)
Note: No additional environment variables are needed! The cron job directly calls the generateVideoAsync function instead of making HTTP requests, making it simpler and more efficient.
In Val.Town:
- Click on the "Schedule" button in your Val
- Set the schedule to run every hour:
0 * * * *(cron syntax) - Save the schedule
You can easily adjust the number of stories to process by editing the constant at the top of the file:
const TOP_STORIES_LIMIT = 10; // Change this to process more or fewer stories
Common values:
10- Top 10 stories (default, recommended for cost control)20- Top 20 stories50- Top 50 stories100- Top 100 stories
- Fetch Stories: The cron job fetches the list of top story IDs from HackerNews
- Get Story Details: For each story (up to TOP_STORIES_LIMIT), it fetches the full story details
- Filter: It filters out:
- Stories without external URLs (text posts, Ask HN, Show HN)
- URLs that already exist in the database (duplicates)
- Generate Videos: For each new URL, it directly calls the
generateVideoAsync()function with the default prompt - Log Results: It logs a summary of processed, skipped, and failed stories
The cron job uses the getVideoByUrl() database query to check if a URL has already been processed. This ensures:
- Cost control: You don't regenerate videos for the same URL
- Efficiency: No wasted API calls or processing time
- Data integrity: Each URL is only processed once
Note: If you want to regenerate a video for a URL, you can do so through the admin interface.
The cron job only processes stories with external URLs. It skips:
- Ask HN posts (text-only discussions)
- Show HN posts without URLs
- Job postings without URLs
- Any other text-only content
This ensures you only generate videos for stories that have actual web content to summarize.
The cron job logs detailed information about its execution:
[HN Cron] Starting HackerNews processing at 2025-10-21T22:00:00.000Z
[HN Cron] Processing top 10 stories
[HN Cron] Retrieved 500 total story IDs
[HN Cron] Processing 10 stories
[HN Cron] Story 12345: Generating video for https://example.com
[HN Cron] Story 12345: Video generation started successfully
[HN Cron] Story 12346: URL already processed, skipping
[HN Cron] Story 12347: No external URL, skipping
[HN Cron] Processing complete:
[HN Cron] - Processed: 3
[HN Cron] - Skipped: 7
[HN Cron] - Errors: 0
You can view these logs in Val.Town's execution logs.
Before setting up the cron schedule, you can test the job manually:
- In Val.Town, open your cron job Val
- Click the "Run" button to execute it immediately
- Check the logs to see the results
- Verify that videos are being generated in your application
Possible causes:
- All URLs might already be in the database (check logs for "duplicate" messages)
- All stories might be text posts without URLs (check logs for "no URL" messages)
- The
GEMINI_API_KEYmight be missing or invalid
Solution: Check the execution logs for specific error messages.
Solution: Reduce the TOP_STORIES_LIMIT constant to a lower number (e.g., 5 instead of 10).
The cron job is designed with cost control in mind:
- Configurable limit: Easy to adjust the number of stories processed
- Duplicate prevention: Never processes the same URL twice
- Efficient filtering: Skips text posts before making API calls
- Batched processing: All video generations are triggered at once, then processed asynchronously
To further control costs:
- Set
TOP_STORIES_LIMITto a lower number - Run the cron less frequently (e.g., every 2-3 hours instead of hourly)
- Monitor the logs to see how many new stories are typically found
The cron job integrates seamlessly with your existing system:
- Uses the same video generation endpoint (
/api/videos/generate) - Leverages the existing database schema and queries
- Works with the existing polling mechanism for video completion
- Respects the default video prompt from your configuration
- Videos can be managed through the admin interface like any other video
No changes to your existing code are required - the cron job is completely standalone.