Overview
The caption generation system supports:- Smart Captions: Detailed AI captions for uploaded images (50-400 characters)
- Session Captions: Short captions for slideshow playback (50-150 characters)
- Manual Captions: Contextual captions that maintain narrative continuity
- Batch Generation: Generate captions for multiple slides at once
OpenRouter is recommended for production use as it provides access to multiple models, better rate limits, and fallback options.
Prerequisites
Choose one of the following:- OpenRouter (Recommended)
- OpenAI (Direct)
- OpenRouter account at openrouter.ai
- API key from OpenRouter Keys
- Credits loaded (pay-as-you-go)
Setup Steps
1
Get API Key
- OpenRouter
- OpenAI
- Sign up at openrouter.ai
- Navigate to Keys
- Click “Create Key”
- Name it (e.g., “JOIP Production”)
- Copy the key (starts with
sk-or-)
2
Configure Environment
Add your key to
.env:If both are set, OpenRouter takes precedence. OpenAI is used as fallback only.
3
Choose Model (OpenRouter Only)
Configure which AI model to use for caption generation:Recommended Models:
google/gemini-2.0-flash-exp- Fast, cheap, good quality (default)google/gemini-2.5-pro- Highest quality, contextual understandinganthropic/claude-3.5-sonnet- Excellent balance of speed and qualityopenai/gpt-4o- OpenAI’s latest, reliable
4
Verify Configuration
Start the server and check logs:
Implementation Details
Caption Generation Architecture
JOIP uses a sophisticated prompting system for NSFW content:Context-Aware Generation
For manual sessions, captions consider surrounding context:Theme System
JOIP provides pre-built caption themes:- JOI (Jerk Off Instruction)
- Forced-Bi
- Beta/Humiliation
- CBT (Cock & Ball Torture)
- Cuckold
- Custom
Dominant, teasing control with explicit edging/denial commands.
Gemini Safety Settings
For Google Gemini models, JOIP disables content filtering:Without these settings, Gemini returns empty responses for NSFW content.
API Usage Examples
Generate Smart Caption
Generate Session Caption
Generate Contextual Caption
Batch Generation
Media Compatibility
JOIP validates media before generation:Error Handling
Automatic Retries
JOIP automatically retries failed generations:Common Error Codes
429 Rate Limited
429 Rate Limited
Cause: Too many requestsSolution:
- OpenRouter: Check rate limits at openrouter.ai/docs
- OpenAI: Upgrade tier for higher limits
- Implement user-level throttling
- Add delays between batch generations
400 Bad Request
400 Bad Request
Cause: Invalid request parametersSolution:
- Check image URL is accessible
- Verify image size < 20MB
- Ensure model supports vision
- Review error message for specifics
Empty Response / Content Filtered
Empty Response / Content Filtered
Cause: Model refuses to generate NSFW contentSolution:
- Use Google Gemini models (best NSFW support)
- Verify safety settings are applied
- Try alternative model if issue persists
- Check if prompt is too explicit (ironically)
Cost Optimization
Model Pricing (OpenRouter)
Prices are approximate. Check OpenRouter Pricing for current rates.
Tips to Reduce Costs
- Use Flash Models:
gemini-2.0-flash-expis free and high quality - Cache Results: JOIP caches captions in IndexedDB (24hr TTL)
- Batch Wisely: Generate all captions at once vs. one-by-one
- Optimize Prompts: Shorter prompts = lower input costs
- Set Max Tokens: Limit output length to reduce costs
Advanced Configuration
Custom Model Parameters
Enable Reasoning (Extended Thinking)
For complex contextual captions:Troubleshooting
Debug Logging
Test Caption Generation
Security Best Practices
API Key Security
- Store keys in
.envonly - Never commit to version control
- Rotate keys periodically
- Use different keys per environment
Input Validation
- Validate image URLs before API calls
- Block localhost/private IPs (SSRF protection)
- Enforce size limits (20MB max)
- Check content-type headers
Rate Limiting
- Implement user-level quotas
- Track API usage per user
- Add cooldowns for abuse prevention
- Monitor costs in real-time
Error Handling
- Don’t expose API errors to users
- Log detailed errors server-side
- Provide generic user-facing messages
- Implement graceful fallbacks