π Intelligent Discord Notifications for Jellyfin Media Server
Advanced webhook service with quality upgrade detection, multi-channel routing, and rich customization
- Overview
- Key Features
- Quick Start
- Templates
- Configuration
- How It Works
- API Endpoints
- Troubleshooting
- Documentation
- Contributing
- License
- Acknowledgments
- Support
Jellynouncer is an advanced intermediary webhook service that bridges Jellyfin media server with Discord, providing intelligent notifications for media library changes. It goes beyond simple "new item" alerts by detecting quality upgrades, managing multi-channel routing, and offering extensive customization through Jinja2 templates.
The service acts as a smart filter between Jellyfin's webhook events and Discord notifications, analyzing changes to determine what's truly noteworthy - distinguishing between new content additions and quality improvements like resolution upgrades (1080p β 4K) or HDR additions.
Warning
ALPHA SOFTWARE NOTICE
This software is currently in alpha development. While core functionality may sometimes be stable, you may encounter critical bugs at any time while ongoing development occurs.
π Real-time Dashboard - Monitor service health and statistics
Overview Page Features:
- Jellyfin Server Stats: Total library items, server version, online status
- Local Database Sync: Items synced, database size, last sync time, sync progress
- Processing Pipeline: Live webhook counts, filtering statistics, notification success rates
- Activity Charts: 24-hour trend graphs for webhooks and notifications
- Content Distribution: Visual breakdown by media type (Movies, TV, Music)
- Discord Routing: Channel-specific message counts and distribution
- Filtering Intelligence: Real-time spam prevention statistics
- Recent Notifications: Last 10 processed items with status and details
- System Health: Service status, CPU/memory usage, uptime metrics
Auto-refresh: Dashboard updates every 30 seconds automatically
βοΈ Configuration Editor - Manage all settings through an intuitive interface
Configuration Sections:
- Jellyfin Settings: Server URL, API key, user ID, client identification
- Discord Webhooks: Multiple webhook URLs with individual enable/disable toggles
- Content Routing: Automatic routing rules for movies, TV, and music
- Notification Options: Customize triggers for upgrades, filtering, and grouping
- External APIs: Configure OMDb, TMDb, and TVDb for enhanced metadata
- Database Settings: Path, maintenance intervals, performance tuning
- Server Options: Ports, logging levels, development mode
- Security: Authentication, JWT secrets, webhook authentication
- SSL/TLS: Certificate management for HTTPS support
Features:
- Hover tooltips explaining each setting
- Instant validation with error messages
- Save individual sections or all at once
- Environment variable override indicators
- Test buttons for Jellyfin and Discord connections
π Template Editor - Customize Discord notifications with Jinja2
Editor Features:
- Syntax Highlighting: Full Jinja2 syntax support with color coding
- Template Types: Edit templates for new items, upgrades, deletions, and grouped messages
- Live Preview: See how your templates will look (coming soon)
- Variable Reference: Built-in documentation of available variables
- Undo/Redo: Full editing history
- Save & Restore: Backup and restore template versions
- Hot Reload: Changes apply immediately without restart
Available Templates:
new_item.j2- Single new item notificationsupgraded_item.j2- Quality upgrade notificationsdeleted_item.j2- Item removal notificationsnew_items_grouped.j2- Batched new itemsupgraded_items_grouped.j2- Batched upgrades
πΎ Backup Management - Automated backup and restore system
Backup Features:
- Scheduled Backups: Automatic daily/weekly/hourly backups
- Manual Backups: Create backups on-demand with descriptions
- Selective Restore: Choose which components to restore (config, database, templates, SSL, logs)
- Retention Policies: Automatic cleanup based on age and count
- Compression: Optional compression to save disk space
- Pre-Restore Backups: Automatic safety backup before restore operations
- Real-time Statistics: View backup sizes, counts, and next scheduled time
Backup Dashboard:
- System status indicator (enabled/disabled)
- Next backup countdown
- Estimated backup size
- List of all backups with sizes and timestamps
- One-click restore with component selection
- Backup deletion with confirmation
π Authentication System - Secure access control with multiple auth methods
Authentication Features:
- Setup Wizard: Guided setup to prevent lockouts
- JWT Tokens: Short-lived access tokens (30min) with refresh tokens (7 days)
- Password Management: Secure bcrypt hashing with salt
- Emergency Recovery: Access recovery via JWT_SECRET_KEY environment variable
- Audit Logging: Track all authentication events
Webhook Authentication:
- API Keys: Non-expiring keys for service integrations
- Basic Auth: Username/password for simple integrations
- Bearer Tokens: JWT tokens for advanced integrations
- IP Validation: Optional IP-based trust for service tokens
- Usage Tracking: Monitor API key usage and last access
Security Dashboard:
- Manage API keys with descriptions
- View usage statistics per key
- Revoke keys with audit trail
- Password change interface
- Session management
π Notification History - Track all notification events
History Features:
- Complete Tracking: Every notification attempt logged
- Status Monitoring: Pending, sent, failed states
- Error Details: Response codes and error messages
- Time Tracking: Created and sent timestamps
- Filtering: By status, type, time range
- Pagination: Efficient browsing of large histories
Statistics:
- Hourly and daily aggregation
- Success/failure rates
- Content type distribution
- Performance metrics
π Log Viewer - Browse and filter logs with color coding
Log Viewer Features:
- Dual Log Support: View both
jellynouncer.logandjellynouncer-web.log - Real-time Updates: Logs update automatically as new entries arrive
- Color-Coded Levels: DEBUG (gray), INFO (blue), WARNING (yellow), ERROR (red)
- Advanced Filtering:
- Filter by log level (DEBUG, INFO, WARNING, ERROR)
- Filter by component (webhook, discord, jellyfin, database, etc.)
- Search for specific text
- Time range selection
- Line Limits: View last 100, 500, or 1000 lines
- Export: Download filtered logs for analysis
- Performance: Virtual scrolling for smooth handling of large logs
π Security Features - JWT authentication and SSL/TLS support
Authentication System:
- JWT-based: Short-lived access tokens (30min) with refresh tokens (7 days)
- Bcrypt Hashing: Industry-standard password encryption
- Setup Wizard: Guided initial authentication setup
- Password Requirements: Minimum 8 characters, complexity optional
- Session Management: Automatic token refresh, secure logout
- Recovery Options: JWT secret override for emergency access
SSL/TLS Support:
- Certificate Formats: PEM (separate cert/key) or PFX/PKCS12 (bundled)
- Port Configuration: Default HTTPS on port 9000
- Auto-redirect: Optional HTTP to HTTPS redirect
- Certificate Upload: Web-based certificate management
- Validation: Automatic certificate validation and expiry warnings
π― Intelligent Analysis - Distinguishes between new content and quality upgrades
The system uses sophisticated logic to differentiate between:
- New Content: Items that have never been seen before in your library
- Quality Upgrades: Existing items replaced with better versions
- Metadata Updates: Changes to descriptions, artwork, or tags (ignored)
- File Reorganization: Moving or renaming files without content changes
This intelligence prevents duplicate notifications while ensuring you're aware of meaningful changes.
π Technical Detection - Identifies resolution, codec, and audio improvements
Automatically detects and reports technical improvements:
- Resolution Upgrades: 480p β 720p β 1080p β 4K β 8K
- Video Codec Improvements: MPEG-2 β H.264 β H.265/HEVC β AV1
- Audio Enhancements:
- Channel upgrades: Mono β Stereo β 5.1 β 7.1 β Atmos
- Codec improvements: MP3 β AAC β FLAC/DTS β TrueHD
- HDR Additions: SDR β HDR10 β HDR10+ β Dolby Vision
- Subtitle Changes: Added or removed subtitle tracks
Each improvement is clearly identified in the Discord notification.
π Content Hashing - Blake2b fingerprinting prevents duplicate notifications
Jellynouncer uses a sophisticated content hashing system to detect quality upgrades:
Algorithm: Blake2b (32-byte digest) - Faster than SHA-256 with equal security
Fields Included in Hash:
- Core:
name,item_type - Video:
video_height,video_width,video_codec,video_profile,video_range,video_framerate,video_bitrate,video_bitdepth - Audio:
audio_codec,audio_channels,audio_bitrate,audio_samplerate - Subtitles:
subtitle_count,subtitle_languages(sorted),subtitle_formats(sorted) - File:
file_size
Not Included (intentionally excluded to prevent false positives):
- Metadata fields (
overview,genres,studios,tags) - External IDs (
imdb_id,tmdb_id,tvdb_id) - File paths (allows moving/renaming without triggering upgrades)
- Timestamps and dates
This design ensures that only actual quality changes trigger upgrade notifications, while metadata updates and file reorganization are ignored.
βοΈ Customizable Triggers - Configure which changes warrant notifications
Jellynouncer allows you to customize exactly which types of quality changes trigger upgrade notifications:
Available Triggers (watch_changes configuration):
| Trigger | Default | Description | Example Changes |
|---|---|---|---|
resolution |
true |
Video resolution changes | 720p β 1080p β 4K |
video_codec |
true |
Video codec improvements | H.264 β H.265/HEVC β AV1 |
audio_codec |
true |
Audio codec upgrades | MP3 β AAC β FLAC, AC3 β DTS |
audio_channels |
true |
Audio channel changes | Stereo (2.0) β 5.1 β 7.1 β Atmos |
hdr_status |
true |
HDR format changes | SDR β HDR10 β HDR10+ β Dolby Vision |
file_size |
false |
Significant size changes (>10%) | Complete re-encodes |
subtitles |
true |
Subtitle track/language changes | Added/removed subtitle tracks |
How Triggers Work:
- When an existing item is updated, the content hash changes (indicating actual file changes)
- The system compares old vs new technical specifications
- Only enabled triggers are checked for changes
- If any enabled trigger detects a change, an "Upgraded" notification is sent
- The notification includes details about what changed
Configuration Examples:
{
"notifications": {
"watch_changes": {
"resolution": true, // Always notify for resolution upgrades
"video_codec": true, // Notify for codec improvements
"audio_codec": true, // Important for audio enthusiasts
"audio_channels": true, // Track surround sound upgrades
"hdr_status": true, // Critical for HDR displays
"file_size": false, // Ignore size-only changes
"subtitles": false // Don't care about subtitle changes
}
}
}Use Cases:
- Home Theater Enthusiast: Enable all audio and HDR triggers for complete awareness
- Bandwidth Conscious: Enable
video_codecto track compression improvements - Visual Quality Focus: Enable
resolutionandhdr_status, disable audio triggers - Minimal Notifications: Enable only
resolutionfor major upgrades
Important Notes:
- These triggers only apply to upgrades (when an existing item changes)
- New items always trigger notifications (subject to other filters)
- Triggers work in conjunction with the content hash - metadata-only changes never trigger upgrades
- File size trigger requires >10% change to avoid minor fluctuations
To configure via environment variables:
# Format: WATCH_CHANGES_<TRIGGER>=true/false
WATCH_CHANGES_RESOLUTION=true
WATCH_CHANGES_VIDEO_CODEC=true
WATCH_CHANGES_AUDIO_CODEC=false
WATCH_CHANGES_HDR_STATUS=trueπ Rename Filtering - Automatically detects and filters file renames
Jellynouncer uses two complementary methods to detect when files are renamed or moved without content changes:
Method 1: Content Hash Comparison
- Always active regardless of configuration
- Since
file_pathis excluded from the hash, moving or renaming files doesn't change the hash - When a "new" item arrives with the same hash but different
item_id, it's detected as a rename
Method 2: Deletion Queue Matching
- Used when Jellyfin sends both
ItemDeletedandItemAddedwebhooks - Matches deletions with additions based on content hash
- Provides additional rename detection for complex scenarios
Configuration: filter_renames (default: true)
- When enabled: Renames/moves are silently handled - database updated, no notifications sent
- When disabled: Renames/moves trigger "New Item" notifications, allowing users to track file reorganization
Examples:
- Moving
/movies/Action/movie.mkvto/movies/SciFi/movie.mkvβ Same hash, detected as rename - Renaming
movie.1080p.mkvtomovie.REMASTERED.1080p.mkvβ Same hash, detected as rename - Actual quality upgrade
movie.1080p.mkvtomovie.4K.mkvβ Different hash, detected as upgrade
To receive notifications for file moves/renames, set filter_renames: false in configuration or use environment variable FILTER_RENAMES=false.
β¬οΈ Upgrade Detection - Intelligently handles file upgrades and deletion filtering
Jellynouncer intelligently detects when files are being upgraded to prevent confusing notification pairs:
How It Works:
- Deletion Queue: When an item is deleted, it's queued for 30 seconds instead of immediately notifying
- Upgrade Detection: If an
ItemAddedarrives for the same item name within 30 seconds:- Same content hash β Rename/move (filtered based on
filter_renamessetting) - Different content hash β Quality upgrade (sends "Upgraded" notification only)
- No matching add β True deletion (sends "Deleted" notification after timeout)
- Same content hash β Rename/move (filtered based on
- Background Cleanup: A task monitors the queue and processes true deletions after 30 seconds
Configuration: filter_deletes (default: true)
- When enabled:
- Upgrades trigger only "Upgraded" notifications (no delete+add pair)
- True deletions are notified after 30-second delay
- Provides clean, meaningful notifications
- When disabled:
- All deletions trigger immediate notifications
- Upgrades result in "Deleted" followed by "Added" notifications
- Useful for tracking all library changes
Example Scenarios:
| Scenario | With Filtering | Without Filtering |
|---|---|---|
| 1080p β 4K Upgrade | Single "Upgraded to 4K" notification | "Deleted" then "Added in 4K" notifications |
| File Deleted | "Deleted" notification (after 30s) | "Deleted" notification (immediate) |
| File Renamed | No notification (if filter_renames=true) |
Based on filter_renames setting |
Queue Monitoring: The deletion queue status is visible in:
- Web interface dashboard
/statsAPI endpoint- Debug logs showing queue size and pending items
To receive immediate deletion notifications without filtering, set filter_deletes: false in configuration or use environment variable FILTER_DELETES=false.
π‘ Content-Type Routing - Route different media to different Discord channels
Routing Configuration:
- Movie Channel: All movie notifications to dedicated channel
- TV Channel: Series and episode updates to TV channel
- Music Channel: Audio content to music channel
- Default Channel: Fallback for unmatched content types
Routing Rules:
{
"routing": {
"enabled": true,
"movie_types": ["Movie"],
"tv_types": ["Series", "Season", "Episode"],
"music_types": ["Audio", "MusicAlbum", "AudioBook"],
"fallback_webhook": "default"
}
}Smart Features:
- Automatic type detection from Jellyfin metadata
- Override routing with custom rules
- Test routing with preview mode
- Statistics per channel in dashboard
π Flexible Webhooks - Unlimited webhooks with granular control
Webhook Management:
- Multiple Webhooks: Configure unlimited Discord webhook URLs
- Individual Controls: Enable/disable each webhook independently
- Custom Names: Assign meaningful names to webhooks
- Priority System: Set webhook priority for fallback chains
- Testing: Test each webhook individually from web interface
Per-Webhook Settings:
- Grouping mode (none, time-based, smart)
- Delay minutes for batching
- Maximum items per message
- Custom embed colors
- Mention roles (@everyone, @here, custom)
β±οΈ Smart Grouping - Batch notifications intelligently
Grouping Modes:
1. No Grouping (Default)
- Each item gets its own notification
- Best for low-volume libraries
- Immediate notifications
2. Time-Based Grouping
- Batch all notifications within time window
- Configurable delay (1-60 minutes)
- Ideal for library scans
- Reduces notification spam
3. Smart Grouping
- Groups by content type and event
- Separate groups for movies, TV, music
- Separate groups for new vs upgraded
- Maximum 10 items per embed (Discord limit)
Configuration:
{
"grouping": {
"mode": "smart",
"delay_minutes": 5,
"max_items": 10
}
}π¦ Rate Limiting - Respect Discord's API limits
Rate Limit Management:
- Discord Limits: 30 messages per minute per channel
- Automatic Queueing: Excess messages queued automatically
- Smart Distribution: Spreads messages across time to avoid bursts
- Retry Logic: Failed messages retry with exponential backoff
- Queue Monitoring: Real-time queue size in dashboard
Queue Features:
- Maximum 500 queued notifications
- Priority processing for important events
- Persistent queue survives restarts
- Manual queue flush option
- Queue statistics and history
Configuration:
{
"rate_limit": {
"requests_per_period": 30,
"period_seconds": 60,
"max_queue_size": 500
}
}π Jinja2 Templates - Full control over Discord embed formatting
Template Engine Features:
- Full Jinja2 Support: Variables, conditionals, loops, filters
- Custom Filters: Format file sizes, dates, durations
- Macro Support: Reusable template components
- Inheritance: Base templates for consistency
- Error Handling: Graceful fallbacks for missing data
πΌοΈ Rich Media Display - Posters, ratings, and technical details
Discord Embed Components:
Thumbnail/Image:
- Jellyfin poster/backdrop images
- Automatic fallback chain: Primary β Backdrop β Logo
- Image verification before sending
- CDN caching for performance
Fields Display:
- Ratings: IMDb, TMDb, TVDb scores with stars
- Technical: Resolution, codec, HDR, audio format
- File Info: Size, bitrate, duration
- Cast & Crew: Top 5 actors, director
- Metadata: Genres, studios, release date
- Subtitles: Available languages and formats
Color Coding:
- Green (5686453): New content
- Blue (3447003): Quality upgrades
- Red (15158332): Deletions
- Custom colors per webhook
π Multiple Templates - Different layouts for different events
Template Types:
| Template | Purpose | Key Features |
|---|---|---|
new_item.j2 |
Single new item | Full details, poster, ratings |
new_item-simple.j2 |
Minimal new item | Title and basic info only |
upgraded_item.j2 |
Quality upgrade | Before/after comparison |
deleted_item.j2 |
Removed content | Reason and timestamp |
new_items_grouped.j2 |
Multiple new items | Compact list format |
upgraded_items_grouped.j2 |
Multiple upgrades | Change summary |
new_items_by_type.j2 |
Grouped by media type | Organized sections |
Dynamic Selection:
- Automatic template selection based on event
- Override templates per webhook
- Fallback templates for errors
- A/B testing support
βοΈ Web Editor Features - Professional template editing experience
Editor Capabilities:
- Syntax Highlighting: Jinja2, JSON, and Markdown support
- Auto-completion: Variable and filter suggestions
- Error Detection: Real-time syntax validation
- Split View: Edit and preview side-by-side
- Version Control: Save, restore, and compare versions
- Hot Reload: Changes apply without restart
- Import/Export: Share templates with community
Helper Tools:
- Variable explorer with descriptions
- Filter documentation
- Example snippets
- Common patterns library
- Discord embed validator
β Rating Services - IMDb, TMDb, and TVDb integration
Supported Services:
OMDb (IMDb Data):
- IMDb ratings and vote counts
- Rotten Tomatoes scores
- Metacritic scores
- Awards and nominations
- Box office data
- Free tier: 1,000 requests/day
TMDb (The Movie Database):
- TMDb user ratings
- Popularity scores
- Release dates and certifications
- Cast and crew details
- High-quality poster URLs
- Free with registration
TVDb (TV Database):
- Episode-specific ratings
- Series and season data
- Air dates and networks
- Episode thumbnails
- Requires subscription for API v4
Priority Chain:
- Movies: TMDb β OMDb β Jellyfin
- TV Shows: TVDb β TMDb β Jellyfin
- Music: Jellyfin metadata only
πΌοΈ Poster Management - Automatic thumbnail handling
Image Processing:
- Source Priority: Primary image β Backdrop β Logo β Generic
- Verification: HTTP HEAD request to verify image availability
- Caching: 1-hour LRU cache for verified URLs
- CDN Support: Direct Jellyfin CDN URLs for performance
- Fallback Chain: Automatic fallback to next available image
Image Types:
- Primary: Main poster or cover art
- Backdrop: Background/fanart image
- Logo: Series or movie logo
- Thumbnail: Episode-specific images
- Banner: Wide format images
Configuration:
{
"thumbnail_source": "primary",
"fallback_enabled": true,
"cache_duration_hours": 1
}π Fallback Handling - Graceful degradation
API Failure Handling:
- Retry Logic: 3 attempts with exponential backoff
- Service Fallback: Automatic switch to next service
- Cache Usage: Use cached data during outages
- Partial Data: Send notification with available data
- Error Logging: Detailed logs for troubleshooting
Degradation Levels:
- All external services available β Full metadata
- Some services down β Partial metadata
- All services down β Jellyfin data only
- Network issues β Cached data
- Complete failure β Basic notification
πΎ Metadata Caching - Intelligent data storage
Cache Strategy:
- Duration: 24 hours for successful responses
- Storage: In-memory LRU cache
- Key Format:
service:type:id(e.g.,tmdb:movie:550) - Size Limit: 1000 entries per service
- Invalidation: Manual or on configuration change
Cache Benefits:
- Reduced API calls by 70-80%
- Faster notification generation
- Protection against rate limits
- Resilience during API outages
- Lower costs for paid APIs
Configuration:
{
"cache_duration_hours": 24,
"cache_size": 1000,
"cache_enabled": true
}ποΈ Database Persistence - SQLite with WAL mode for reliability
Database Features:
- WAL Mode: Write-Ahead Logging for concurrent reads
- Transactions: Batch operations for performance
- Indexes: Optimized queries on item_id, content_hash, type
- Maintenance: Automatic VACUUM and ANALYZE
- Backup: Point-in-time backup support
Schema Design:
- Slim Storage: Only technical fields stored (~30 fields)
- Content Hashing: Blake2b for change detection
- Historical Tracking: Complete change history
- Statistics Tables: Aggregated metrics for dashboard
- Size Optimization: 70% smaller than full metadata storage
Performance:
- Handles 100,000+ items efficiently
- Batch inserts up to 100 items/transaction
- Sub-millisecond query times with indexes
- Concurrent read access during writes
π¦ Intelligent Queue System - Never lose notifications
Queue Features:
- Capacity: Up to 500 queued notifications
- Persistence: Survives service restarts
- Priority: Important events processed first
- Rate Limiting: Respects Discord's 30/minute limit
- Retry Logic: 3 attempts with exponential backoff
Queue Management:
- Real-time statistics in dashboard
- Manual flush/clear options
- Dead letter queue for failed items
- Automatic overflow handling
- Queue health monitoring
Processing Logic:
# Exponential backoff formula
delay = min(300, (2 ** attempt) * 10)
# Attempt 1: 20 seconds
# Attempt 2: 40 seconds
# Attempt 3: 80 secondsπ Background Sync - Catch missed webhooks
Sync Features:
- Interval: Configurable (default 30 minutes)
- Incremental: Only processes changes since last sync
- Batch Processing: 100 items per batch
- Progress Display: Console progress bars
- Library Filtering: Sync specific libraries only
Sync Process:
- Connect to Jellyfin API
- Fetch library items with metadata
- Compare with local database
- Detect new/upgraded/deleted items
- Queue notifications for changes
- Update database state
Configuration:
{
"sync": {
"enabled": true,
"interval_minutes": 30,
"batch_size": 100,
"libraries": [] // Empty = all libraries
}
}π Health Monitoring - Built-in diagnostics
Health Endpoints:
/health- Basic service health/stats- Detailed statistics/metrics- Prometheus-compatible metrics/debug/memory- Memory usage stats/debug/connections- Active connections
Monitoring Features:
- Service status (webhook, web, database)
- Queue sizes and processing rates
- API response times
- Error rates and types
- Resource usage (CPU, memory, disk)
Integration:
- Docker health checks
- Prometheus/Grafana compatible
- JSON and plain text formats
- Webhook for health alerts
- Status page support
π Structured Logging - Comprehensive application logs
Logging Features:
- Multiple Levels: DEBUG, INFO, WARNING, ERROR
- Component Tagging: [webhook], [discord], [database], etc.
- Structured Format: Timestamp, level, component, message
- Color Coding: Terminal colors for readability
- Rotation: Automatic log rotation at 10MB
- Dual Output: Console and file simultaneously
Log Files:
jellynouncer.log- Main service logjellynouncer-web.log- Web interface logjellynouncer-debug.log- Debug level (when enabled)
Configuration:
{
"log_level": "INFO",
"log_to_file": true,
"log_to_console": true,
"log_rotation_size_mb": 10,
"log_retention_days": 30
}π³ Docker-First Design - Optimized containerization
Container Features:
- Multi-stage Build: Separate build and runtime stages for smaller images
- Non-root User: Runs as user 1000 for security
- Layer Caching: Efficient rebuilds with dependency caching
- Health Checks: Built-in Docker health monitoring
- Volume Mounts: Persistent data, config, and logs
- Network Modes: Bridge, host, or custom networks supported
Resource Limits:
- Default: 256MB RAM, 0.5 CPU
- Recommended: 512MB RAM, 1.0 CPU
- Large libraries: 1GB RAM, 2.0 CPU
π§ Environment Configuration - Override any setting via environment
Environment Variable System:
- Full Coverage: Every config option has an environment variable
- Nested Support: Use double underscores for nested settings
- Type Conversion: Automatic string to boolean/integer conversion
- Priority: Environment variables override config.json
Common Variables:
# Jellyfin Connection
JELLYFIN_SERVER_URL=http://jellyfin:8096
JELLYFIN_API_KEY=your_api_key
JELLYFIN_USER_ID=your_user_id
# Discord Webhooks
DISCORD_WEBHOOK_URL=https://discord.com/api/webhooks/...
DISCORD_WEBHOOK_MOVIES_URL=https://discord.com/api/webhooks/...
# Service Control
JELLYNOUNCER_RUN_MODE=all # all, webhook, or web
SERVER_PORT=1984
WEB_PORT=1985
LOG_LEVEL=INFO
# Features
FILTER_RENAMES=true
FILTER_DELETES=true
WATCH_CHANGES_RESOLUTION=true
WATCH_CHANGES_VIDEO_CODEC=trueβ Configuration Validation - Automatic validation with helpful errors
Validation Features:
- Type Checking: Ensures correct data types for all fields
- Range Validation: Ports, intervals, and limits checked
- URL Validation: Verifies webhook and server URLs
- Required Fields: Checks for mandatory configuration
- Default Values: Applies sensible defaults for missing options
Error Reporting:
ERROR: Configuration validation failed:
- jellyfin.server_url: Invalid URL format
- discord.webhooks.default.url: Required field missing
- server.port: Must be between 1-65535 (got: 70000)
Validation Process:
- Load config.json
- Apply environment overrides
- Validate all fields
- Report errors with context
- Exit if critical errors found
π Background Sync Process - Automatic library synchronization
Sync Features:
- Automatic Scheduling: Runs every 30 minutes (configurable)
- Full Library Sync: Processes entire Jellyfin library
- Batch Processing: 100 items per database transaction
- Memory Efficient: Streaming processing for large libraries
- Progress Display: Console progress bars during sync
- Change Detection: Identifies new, upgraded, and deleted items
Sync Process:
- Connect to Jellyfin API
- Stream library items in batches
- Compare with local database
- Queue notifications for changes
- Update sync timestamp
- Log statistics
Performance:
- Small library (<1,000 items): ~30 seconds
- Medium library (10,000 items): ~2-3 minutes
- Large library (100,000 items): ~15-20 minutes
Note: Currently syncs entire library - no per-library filtering available
βοΈ Service Management - Graceful operations and monitoring
Startup Process:
- Load and validate configuration
- Initialize database connections
- Test Jellyfin connectivity
- Start webhook service (port 1984)
- Start web interface (port 1985)
- Begin background tasks
Graceful Shutdown:
- Signal Handling: Responds to SIGTERM/SIGINT
- Queue Processing: Completes pending notifications
- Database Cleanup: Flushes writes and closes connections
- State Preservation: Saves queue for next startup
- Timeout: 30-second grace period
Health Monitoring:
/health- Basic health check (200 OK)/stats- Detailed statistics JSON/ready- Readiness probe for Kubernetes- Docker HEALTHCHECK instruction included
Service Modes:
# Run both services (default)
JELLYNOUNCER_RUN_MODE=all
# Webhook service only
JELLYNOUNCER_RUN_MODE=webhook
# Web interface only
JELLYNOUNCER_RUN_MODE=webπ Monitoring & Observability - Built-in metrics and logging
Metrics Available:
- Webhook processing rate
- Discord queue size
- Notification success/failure rates
- Database size and item count
- API response times
- Memory and CPU usage
Logging System:
- Structured Format:
[timestamp] [level] [component] message - Component Tags: [webhook], [discord], [database], [jellyfin]
- Log Levels: DEBUG, INFO, WARNING, ERROR
- Rotation: Automatic at 10MB
- Retention: 30 days default
Integration Points:
- Prometheus metrics endpoint (planned)
- JSON logs for parsing
- Syslog forwarding support
- CloudWatch/Datadog compatible
Dashboard Metrics:
- Real-time statistics every 30 seconds
- 24-hour activity graphs
- Processing pipeline visualization
- System health indicators
Jellynouncer provides enterprise-grade security features to protect your media server integrations:
- Web Interface: JWT-based authentication with refresh tokens
- Webhook Endpoints: Multiple auth methods for flexibility
- API Keys for permanent service integration
- Basic Authentication for simple tools
- Bearer tokens for advanced integrations
- Emergency Access: Recovery mechanism via JWT_SECRET_KEY
- Bcrypt password hashing with salt
- Short-lived access tokens (30 minutes)
- Refresh tokens with 7-day expiry
- Audit logging for all authentication events
- IP-based validation for service tokens
- Rate limiting on authentication endpoints
- Initial setup wizard prevents lockouts
- Create admin account before enabling auth
- Generate API keys for Jellyfin webhook
- Configure webhook authentication if needed
Comprehensive backup system to protect your configuration and data:
- Scheduled: Daily, weekly, or hourly backups
- Components: Selective backup of config, database, templates, SSL certs, logs
- Retention: Automatic cleanup based on age and count
- Compression: Optional compression to save space
- Selective Restore: Choose which components to restore
- Pre-Restore Backup: Automatic safety backup before restore
- Point-in-Time: Restore to any previous backup
- Zero Downtime: Hot restore without service interruption
{
"backup": {
"enabled": true,
"schedule": "daily",
"backup_time": "02:00",
"retention_days": 30,
"max_backups": 10,
"compress": true
}
}- Jellyfin Server 10.8+ with Webhook Plugin installed
- Discord Server with webhook creation permissions
- Docker (recommended) or Python 3.13+ for manual installation
π¦ View Docker Compose Setup
- Create directory structure:
mkdir jellynouncer && cd jellynouncer
mkdir config data logs templates- Create
docker-compose.yml:
version: '3.8'
services:
jellynouncer:
image: markusmcnugen/jellynouncer:latest
container_name: jellynouncer
restart: unless-stopped
ports:
- "1984:1984" # Webhook service
- "1985:1985" # Web interface (HTTP)
- "9000:9000" # Web interface (HTTPS - optional)
environment:
# Required
- JELLYFIN_SERVER_URL=http://your-jellyfin-server:8096
- JELLYFIN_API_KEY=your_api_key_here
- JELLYFIN_USER_ID=your_user_id_here
- DISCORD_WEBHOOK_URL=https://discord.com/api/webhooks/your/webhook
# Optional: Content-specific webhooks
- DISCORD_WEBHOOK_URL_MOVIES=https://discord.com/api/webhooks/movies
- DISCORD_WEBHOOK_URL_TV=https://discord.com/api/webhooks/tv
- DISCORD_WEBHOOK_URL_MUSIC=https://discord.com/api/webhooks/music
# Optional: External APIs for enhanced metadata
- OMDB_API_KEY=your_omdb_key
- TMDB_API_KEY=your_tmdb_key
- TVDB_API_KEY=your_tvdb_key
# Optional: Web interface security
- JWT_SECRET_KEY=your-secret-key-here # Auto-generated if not set
# System
- PUID=1000
- PGID=1000
- TZ=America/New_York
- LOG_LEVEL=INFO
- JELLYNOUNCER_RUN_MODE=all # all, webhook, or web
volumes:
- ./config:/app/config
- ./data:/app/data
- ./logs:/app/logs
- ./templates:/app/templates
# Optional: Custom web assets
# - ./web/public:/app/web/dist/assets:ro
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:1984/health"]
interval: 300s
timeout: 10s
retries: 3
start_period: 10s- Start the service:
docker-compose up -d-
Access the services:
- Web Interface:
http://your-server:1985 - Webhook Endpoint:
http://your-server:1984/webhook
- Web Interface:
-
Configure Jellyfin Webhook Plugin:
- Go to Jellyfin Dashboard β Plugins β Webhook
- Add new webhook with URL:
http://your-server:1984/webhook - Enable "Item Added" event
- Enable "Item Deleted" event (optional, for deletion notifications)
- Check "Send All Properties"
- Save configuration
π³ View Docker Run Command
docker run -d \
--name jellynouncer \
--restart unless-stopped \
-p 1984:1984 \
-p 1985:1985 \
-p 9000:9000 \
-e JELLYFIN_SERVER_URL=http://jellyfin:8096 \
-e JELLYFIN_API_KEY=your_api_key \
-e JELLYFIN_USER_ID=your_user_id \
-e DISCORD_WEBHOOK_URL=https://discord.com/api/webhooks/... \
-v ./config:/app/config \
-v ./data:/app/data \
-v ./logs:/app/logs \
-v ./templates:/app/templates \
markusmcnugen/jellynouncer:latestπ οΈ View Manual Installation Steps
- Python 3.13+
- SQLite 3
- Git
- Node.js 20+ (for building web interface)
- Clone repository:
git clone https://github.com/MarkusMcNugen/Jellynouncer.git
cd Jellynouncer- Create virtual environment:
python -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activate- Install Python dependencies:
pip install -r requirements.txt- Build web interface:
cd web
npm install
npm run build
cd ..- Configure:
cp config/config.json.example config/config.json
# Edit config.json with your settings- Run:
python main.py- Access services:
- Web Interface:
http://localhost:1985 - Webhook:
http://localhost:1984
- Web Interface:
Create /etc/systemd/system/jellynouncer.service:
[Unit]
Description=Jellynouncer Discord Webhook Service
After=network.target
[Service]
Type=simple
User=jellynouncer
WorkingDirectory=/opt/jellynouncer
Environment="PATH=/opt/jellynouncer/venv/bin"
ExecStart=/opt/jellynouncer/venv/bin/python main.py
Restart=always
[Install]
WantedBy=multi-user.targetEnable and start:
sudo systemctl enable jellynouncer
sudo systemctl start jellynouncerJellynouncer uses Jinja2 templates for complete control over Discord embed formatting. Templates can be edited through the web interface or by modifying files directly.
| Type | Description | Files |
|---|---|---|
| Individual | Single item notifications | new_item.j2, upgraded_item.j2, deleted_item.j2 |
| Grouped by Event | Group by notification type | new_items_by_event.j2, upgraded_items_by_event.j2 |
| Grouped by Type | Group by content type | new_items_by_type.j2, upgraded_items_by_type.j2 |
| Fully Grouped | Combined grouping | new_items_grouped.j2, upgraded_items_grouped.j2 |
π Complete Template Guide β
π View API Key Instructions
- API Key: Dashboard β API Keys β Add Key
- User ID: Dashboard β Users β Select User β Copy ID from URL
- Server Settings β Integrations β Webhooks
- Create webhook for desired channel
- Copy webhook URL
- OMDb: Free key at omdbapi.com (1,000 requests/day)
- TMDb: Free at themoviedb.org
- TVDB: Register at thetvdb.com
Configuration can be managed through:
- Web Interface (recommended) -
http://your-server:1985 - Environment Variables - Override any setting
- config.json - Manual file editing
π View config.json Example
{
"jellyfin": {
"server_url": "http://jellyfin:8096",
"api_key": "your_key",
"user_id": "your_id"
},
"discord": {
"webhooks": {
"movies": {
"url": "https://discord.com/api/webhooks/...",
"enabled": true,
"grouping": {
"mode": "both",
"delay_minutes": 5,
"max_items": 20
}
}
},
"routing": {
"enabled": true,
"fallback_webhook": "default"
}
},
"notifications": {
"watch_changes": {
"resolution": true,
"codec": true,
"audio_codec": true,
"hdr_status": true
},
"filter_renames": true,
"filter_deletes": true
},
"web_interface": {
"auth_enabled": false,
"ssl_enabled": false,
"port": 1985,
"ssl_port": 9000
}
}π Complete Configuration Guide β
graph TD
%% External Systems
A[Jellyfin Server] -->|ItemAdded/ItemDeleted| B[FastAPI webhook]
%% Core Orchestration
B --> C[WebhookService]
C --> D{Event Type?}
%% Deletion Flow
D -->|ItemDeleted| E[Deletion Queue<br/>30s delay]
E --> F{Upgrade Detection}
F -->|True Delete| G[deleted_item.j2]
F -->|Upgrade| H[Filter Event]
%% Addition Flow
D -->|ItemAdded| I[Check Deletion Queue]
I -->|Found Match| H
I -->|No Match| J[JellyfinAPI.get_item]
%% Processing Pipeline
H --> J
J --> K[Convert to MediaItem]
K --> L[DatabaseManager]
L --> M{Existing Item?}
M -->|Yes| N[ChangeDetector]
M -->|No| O[New Item]
%% Change Detection
N --> P{Changes?}
P -->|Quality Upgrade| Q[upgraded_item.j2]
P -->|Metadata Only| R[Update DB Only]
%% Metadata Enrichment
O --> S[MetadataService]
S --> T[OMDb API]
S --> U[TMDb API]
S --> V[TVDb API]
%% Template Processing
Q --> W[Jinja2 Environment<br/>+Cache]
G --> W
O --> W
W --> X[Render Template]
%% Discord Routing
X --> Y[DiscordNotifier]
Y --> Z{Content Router}
Z -->|Movies| AA[Movies Webhook]
Z -->|TV Shows| AB[TV Webhook]
Z -->|Music| AC[Music Webhook]
Z -->|Default| AD[General Webhook]
%% Web Interface
WEB[Web Interface<br/>Port 1985] --> C
WEB --> L
WEB --> Y
%% Database Layer
L --> AE[(SQLite + WAL)]
AE --> AF[Concurrent Access]
%% Background Services
C --> AG[Background Tasks]
AG --> AH[Library Sync<br/>Producer/Consumer]
AG --> AI[Deletion Cleanup]
AG --> AJ[Database Vacuum]
%% Health Monitoring
B --> AK[Health Endpoint]
B --> AL[Stats Endpoint]
B --> AM[Sync Endpoint]
%% Jellyfin Gradient Colors (Purple to Blue)
style A fill:#aa5cc3,stroke:#8a3db3,stroke-width:2px,color:#fff
style B fill:#a15dc5,stroke:#8144b5,stroke-width:2px,color:#fff
style C fill:#975fc7,stroke:#774bb8,stroke-width:2px,color:#fff
%% Web Interface (Teal)
style WEB fill:#26a69a,stroke:#00897b,stroke-width:3px,color:#fff
%% Event Processing (Purple-Blue transition)
style D fill:#8e61c9,stroke:#6e52ba,stroke-width:2px,color:#fff
style E fill:#8563cb,stroke:#6559bc,stroke-width:2px,color:#fff
style F fill:#7b65cd,stroke:#5c60bf,stroke-width:2px,color:#fff
style G fill:#7267cf,stroke:#5367c1,stroke-width:2px,color:#fff
style H fill:#6969d1,stroke:#4a6ec4,stroke-width:2px,color:#fff
style I fill:#5f6bd3,stroke:#4175c6,stroke-width:2px,color:#fff
%% Core Processing (Blue)
style J fill:#566dd5,stroke:#387cc9,stroke-width:2px,color:#fff
style K fill:#4d6fd7,stroke:#2f83cb,stroke-width:2px,color:#fff
style L fill:#4371d9,stroke:#268ace,stroke-width:2px,color:#fff
style M fill:#3a73db,stroke:#1d91d0,stroke-width:2px,color:#fff
style N fill:#3175dd,stroke:#1498d3,stroke-width:2px,color:#fff
style O fill:#2877df,stroke:#0b9fd5,stroke-width:2px,color:#fff
%% Detection & Analysis (Light Blue)
style P fill:#1e79e1,stroke:#02a6d8,stroke-width:2px,color:#fff
style Q fill:#157be3,stroke:#00addb,stroke-width:2px,color:#fff
style R fill:#0c7de5,stroke:#00b4de,stroke-width:2px,color:#fff
%% External APIs (Cyan)
style S fill:#00acc1,stroke:#00838f,stroke-width:2px,color:#fff
style T fill:#00bcd4,stroke:#0097a7,stroke-width:2px,color:#fff
style U fill:#00bcd4,stroke:#0097a7,stroke-width:2px,color:#fff
style V fill:#00bcd4,stroke:#0097a7,stroke-width:2px,color:#fff
%% Template Engine (Teal)
style W fill:#26a69a,stroke:#00897b,stroke-width:2px,color:#fff
style X fill:#4db6ac,stroke:#00897b,stroke-width:2px,color:#fff
%% Discord (Discord Blue)
style Y fill:#5865f2,stroke:#4752c4,stroke-width:2px,color:#fff
style Z fill:#5865f2,stroke:#4752c4,stroke-width:2px,color:#fff
style AA fill:#5865f2,stroke:#4752c4,stroke-width:2px,color:#fff
style AB fill:#5865f2,stroke:#4752c4,stroke-width:2px,color:#fff
style AC fill:#5865f2,stroke:#4752c4,stroke-width:2px,color:#fff
style AD fill:#5865f2,stroke:#4752c4,stroke-width:2px,color:#fff
%% Database (Green)
style AE fill:#4caf50,stroke:#2e7d32,stroke-width:2px,color:#fff
style AF fill:#66bb6a,stroke:#388e3c,stroke-width:2px,color:#fff
%% Background (Orange)
style AG fill:#ff9800,stroke:#e65100,stroke-width:2px,color:#fff
style AH fill:#ffa726,stroke:#ef6c00,stroke-width:2px,color:#fff
style AI fill:#ffb74d,stroke:#f57c00,stroke-width:2px,color:#fff
style AJ fill:#ffc947,stroke:#f9a825,stroke-width:2px,color:#fff
%% Health (Pink)
style AK fill:#ec407a,stroke:#c2185b,stroke-width:2px,color:#fff
style AL fill:#f06292,stroke:#e91e63,stroke-width:2px,color:#fff
style AM fill:#f48fb1,stroke:#f06292,stroke-width:2px,color:#fff
| Endpoint | Method | Description |
|---|---|---|
/webhook |
POST | Main webhook receiver from Jellyfin |
/health |
GET | Service health and status |
/stats |
GET | Comprehensive statistics |
/sync |
POST | Trigger manual library synchronization |
/validate-templates |
GET | Validate all templates with sample data |
/test-webhook |
POST | Send test notification |
| Endpoint | Method | Description |
|---|---|---|
/api/overview |
GET | Dashboard statistics and health |
/api/config |
GET/POST | Configuration management |
/api/templates |
GET/POST | Template management |
/api/logs |
GET | Log retrieval with filtering |
/api/auth/login |
POST | User authentication |
/api/auth/refresh |
POST | Token refresh |
/api/auth/setup |
POST | Initial admin account setup |
/api/security |
GET/POST | Security settings |
/api/ssl |
GET/POST | SSL/TLS configuration |
/api/backup/status |
GET | Backup system status |
/api/backup/list |
GET | List available backups |
/api/backup/create |
POST | Create manual backup |
/api/backup/restore/{name} |
POST | Restore from backup |
/api/backup/config |
PUT | Update backup configuration |
/api/webhook-keys |
GET/POST | Webhook API key management |
/api/notifications |
GET | Notification history with pagination |
π View API Examples
# Check service health
curl http://localhost:1984/health
# View statistics (includes queue metrics)
curl http://localhost:1984/stats
# Trigger sync
curl -X POST http://localhost:1984/sync
# Test specific webhook
curl -X POST "http://localhost:1984/test-webhook?webhook_name=movies"
# Get dashboard data
curl http://localhost:1985/api/overview
# View current configuration
curl http://localhost:1985/api/config
# Get logs (last 100 entries)
curl "http://localhost:1985/api/logs?limit=100"β No notifications received
- Verify Jellyfin webhook plugin is configured correctly
- Check webhook URL points to
http://your-server:1984/webhook - Confirm Discord webhook URLs are valid
- Review logs in web interface or at
logs/jellynouncer.log - Use the test webhook feature in the web interface
β Database errors
# Check permissions
ls -la data/
# Reset database (loses history)
rm data/jellynouncer.db
docker restart jellynouncerβ Rate limiting issues
- Reduce
max_itemsin grouping configuration - Increase
delay_minutesfor batching - Check Discord rate limits in logs
- Monitor queue status in web interface dashboard
β Web interface not accessible
- Ensure port 1985 is exposed in Docker
- Check firewall rules
- Verify
JELLYNOUNCER_RUN_MODEincludes web (default: "all") - Check logs for web service startup errors
Enable comprehensive debug logging to troubleshoot issues:
# Docker Compose
environment:
- LOG_LEVEL=DEBUG# Manual
export LOG_LEVEL=DEBUG
python main.pyWhen LOG_LEVEL=DEBUG, the service will log:
- Complete HTTP request headers (with sensitive values masked)
- Raw request body content
- JSON structure and field analysis
- Webhook payload validation details
- Item deletion queue status
- Metadata API responses
- Discord notification attempts and results
| Location | Description |
|---|---|
logs/jellynouncer.log |
Main application log |
logs/jellynouncer-debug.log |
Debug log (when DEBUG enabled) |
docker logs jellynouncer |
Container logs |
http://your-server:1985 β Logs tab |
Web interface log viewer |
| Document | Description |
|---|---|
| Configuration Guide | Complete configuration reference |
| Template Guide | Template customization and examples |
| Web Interface Guide | Detailed web interface documentation |
| API Reference | Complete API endpoint documentation |
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/AmazingFeature) - Commit your changes (
git commit -m 'Add some AmazingFeature') - Push to the branch (
git push origin feature/AmazingFeature) - Open a Pull Request
- Python 3.13+ with type hints
- PEP 8 compliance (Black formatter, 88 char limit)
- Google-style docstrings
- Comprehensive error handling
- React 19 with TypeScript for web interface
- Tailwind CSS for styling
| Layer | Technology |
|---|---|
| Backend | Python 3.13, FastAPI, SQLite, Pydantic v2 |
| Frontend | React 19, Vite 7, Tailwind CSS, FontAwesome |
| Tools | Docker, ESLint 9, Prettier |
| Testing | Pytest, React Testing Library |
This project is licensed under the MIT License - see the LICENSE file for details.
- Jellyfin for the amazing media server
- Discord for the webhook API
- FastAPI for the modern Python web framework
- React and Vite for the web interface
- All contributors and users of this project
- Issues: GitHub Issues
- Discussions: GitHub Discussions
- Wiki: GitHub Wiki
Made with β by Mark Newton
If you find this project useful, please consider giving it a β on GitHub!