A microservice architecture for processing Jellyfin media server events via webhooks. The system receives events from a Jellyfin server, queues them through RabbitMQ, and processes them asynchronously, with special handling for media processing tasks such as Dolby Vision conversion.
- Webhook Reception: Secure endpoint for receiving Jellyfin webhook events
- Asynchronous Processing: Decoupled architecture using RabbitMQ for reliable message queuing
- Media Processing: Specialized handlers for different Jellyfin events
- Dolby Vision Conversion: Automatic conversion from Dolby Vision Profile 7.x to 8.x format
- User Configuration: Support for custom webhook and service configuration via YAML/JSON
- Prioritized Services: Execute services in configurable priority order
- Metadata Management: Configurable metadata updates based on file paths or content patterns
- Multi-platform Support: Docker images for both ARM64 and AMD64 architectures
Jellyhook consists of two main components:
- API Service: A Flask application that receives webhooks from Jellyfin and routes them to RabbitMQ
- Worker Service: Processes queued messages asynchronously for various media-related tasks
jellyhook/
├── api/ # API service
│ ├── src/api/ # API source code
│ │ ├── hooks/ # Webhook handlers
│ │ └── ...
│ └── tests/ # API tests
├── workers/ # Worker service
│ ├── src/workers/ # Worker source code
│ │ ├── clients/ # Client implementations (Jellyfin, RabbitMQ)
│ │ ├── config/ # Configuration handling
│ │ ├── services/ # Service implementations
│ │ └── ...
│ └── tests/ # Worker tests
│ ├── component/ # Component/integration tests
│ └── unit/ # Unit tests
└── docs/examples/ # Example configuration files
├── jellyhook.example.json # Example webhook configuration
└── jellyhook.example.yaml # Example webhook configuration
- Python 3.12+
- uv (Python package manager)
- Docker and Docker Compose (for containerized development)
- RabbitMQ (included in Docker Compose setup)
- Media processing tools: ffmpeg, mkvextract, mkvmerge, dovi_tool
- Clone the repository
- Navigate to the project directory
- Install dependencies:
cd api && uv pip install -e . cd workers && uv pip install -e .
The project uses a Makefile for common development tasks:
# Linting
make lint
# Code formatting
make format
# Type checking
make typecheck # Project is not yet type compliant
# Run tests
make test
# Run all checks
make allUsing Docker Compose (recommended):
docker-compose up -dOr run services individually:
Run workers:
cd workers
uv run python -m workers.mainThe project includes Makefile commands for building and pushing multi-architecture Docker images:
# Build API image
make build_api TAG=v1.0.0
# Build Worker image
make build_worker TAG=v1.0.0
# Tag images for GitHub Container Registry
make tag_api ACCOUNT=your-github-username TAG=v1.0.0
make tag_worker ACCOUNT=your-github-username TAG=v1.0.0
# Push images
make push_api ACCOUNT=your-github-username TAG=v1.0.0
make push_worker ACCOUNT=your-github-username TAG=v1.0.0
# Or do everything at once
make push_all ACCOUNT=your-github-username TAG=v1.0.0Configure the application using environment variables and configuration files:
FLASK_ENV: Set todevelopmentorproductionLOG_LEVEL: Logging level (default: "INFO" in production, "DEBUG" in development)RABBITMQ_HOST: RabbitMQ hostname (default: "rabbitmq")RABBITMQ_PASS: RabbitMQ password (default: "guest")RABBITMQ_USER: RabbitMQ username (default: "guest")RABBITMQ_VHOST: RabbitMQ virtual host (default: "/")SECRET_KEY: Secret key for Flask (required in production)
DEBUG: Enable debug logging (1 for enabled, 0 for disabled, default: 0)MOVIE_PATH: Path to movie files on your system (default: "/data/media/movies")RABBITMQ_HOST: RabbitMQ hostname (default: "rabbitmq")RABBITMQ_PASS: RabbitMQ password (default: "guest")RABBITMQ_USER: RabbitMQ username (default: "guest")STANDUP_PATH: Path to stand-up comedy files (default: "/data/media/stand-up")TEMP_DIR: Directory for temporary files (default: "/data/tmp")WORKER_ENV: Worker environment (default: "development")
The worker service can be configured using a YAML or JSON file:
worker:
webhooks:
item_added: # Webhook type
enabled: true # Enable/disable this webhook
queue: jellyfin:item_added # RabbitMQ queue name
services: # List of services to execute for this webhook
- name: metadata_update # Service name
enabled: true # Enable/disable this service
priority: 10 # Lower numbers run first
config: # Service-specific configuration
# Configuration options for metadata updates
- name: dovi_conversion
enabled: true
priority: 20
config:
# Configuration options for Dolby Vision conversion
- name: playlist_assignment
enabled: true
priority: 30
config:
rules:
- playlist_id: 1234567890abcdef1234567890abcdef
playlist_name: Under 2 Hours
conditions:
item_types:
- Movie
max_runtime_minutes: 120Place this file at ~/.config/jellyhook/jellyhook.yaml or specify a custom path with the JELLYHOOK_CONFIG_PATH environment variable.
- Python Version: 3.13+
- Linting: Ruff for linting and formatting
- Type Checking: MyPy for static type checking
- Testing: pytest for testing
- Use type annotations for all function parameters and return values
- Follow PEP 8 naming conventions
- Write meaningful docstrings for public functions and classes
- Handle errors appropriately with specific exception catching
This project is licensed under the MIT License - see the LICENSE file for details.
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the project
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request