a1f6701bac
- gRPC service with MusicBrainz provider - PostgreSQL schema with migrations - Service layer with database-first caching - Repository pattern for data access - YAML configuration support - Research documentation for 17 music metadata projects
375 lines
15 KiB
Markdown
375 lines
15 KiB
Markdown
# Meelo Overview
|
|
|
|
## Project Identity
|
|
|
|
**Repository**: https://github.com/Arthi-chaud/Meelo
|
|
**License**: GPL-3.0
|
|
**Stars**: 1,095
|
|
**Releases**: 40 (latest: v3.10.1)
|
|
**Primary Languages**: TypeScript, Go, Python
|
|
**Architecture**: Microservices monorepo
|
|
|
|
## Purpose
|
|
|
|
Meelo is a self-hosted music server designed for music collectors who need flexible metadata management. Unlike typical music servers that treat metadata as static, Meelo provides sophisticated versioning and relationship tracking. The system supports music videos as first-class citizens, not afterthoughts, and includes built-in scrobbling to Last.fm and ListenBrainz.
|
|
|
|
The project targets users with well-organized collections who want control over their metadata without sacrificing modern features like full-text search, mobile access, and streaming.
|
|
|
|
## Core Services
|
|
|
|
### Server (NestJS 11, TypeScript)
|
|
- **Port**: 4000
|
|
- **Role**: Central API and business logic
|
|
- **Stack**: NestJS framework, Prisma ORM, PostgreSQL
|
|
- **Responsibilities**: Authentication, data persistence, search coordination, streaming, scrobbling, event publishing
|
|
|
|
### Scanner (Go 1.25, Echo v5)
|
|
- **Port**: 8133
|
|
- **Role**: Filesystem monitoring and metadata extraction
|
|
- **Stack**: Echo HTTP framework, FFmpeg/FFprobe bindings
|
|
- **Responsibilities**: File watching, metadata parsing, AcoustID fingerprinting, filename regex parsing, file registration, match triggering
|
|
|
|
### Matcher (Python 3.14, FastAPI)
|
|
- **Port**: 6789
|
|
- **Role**: External metadata enrichment
|
|
- **Stack**: FastAPI, async HTTP clients
|
|
- **Responsibilities**: Consuming match events, querying 8 external providers, pushing enriched metadata to Server
|
|
|
|
### Front (Next.js 16, React)
|
|
- **Port**: 3000
|
|
- **Role**: User interface
|
|
- **Stack**: Next.js SSR, Material-UI, Jotai state management, TanStack Query
|
|
- **Variants**: Web (Next.js) and mobile (Expo/React Native)
|
|
|
|
## Infrastructure Dependencies
|
|
|
|
### PostgreSQL
|
|
Primary data store. Handles all persistent data through Prisma ORM. Stores users, artists, albums, songs, tracks, releases, files, playlists, external metadata, and relationships.
|
|
|
|
### MeiliSearch (v1.5)
|
|
Full-text search engine. Indexes artists, albums, songs, and videos for fast, typo-tolerant search. Provides instant results as users type.
|
|
|
|
### RabbitMQ (4.2-alpine)
|
|
Message queue for event-driven architecture. Decouples Scanner and Matcher from Server. Enables asynchronous metadata enrichment without blocking file scanning.
|
|
|
|
### Kyoo Transcoder
|
|
Video transcoding service. Handles music video streaming with adaptive bitrate. Converts source files to web-compatible formats on demand.
|
|
|
|
### Nginx (1.29.7-alpine)
|
|
Reverse proxy. Routes requests to appropriate services:
|
|
- `/` → Front
|
|
- `/api/` → Server
|
|
- `/scanner/` → Scanner
|
|
- `/matcher/` → Matcher
|
|
|
|
## Docker Images
|
|
|
|
All services ship as pre-built Docker images:
|
|
- `arthichaud/meelo-server`
|
|
- `arthichaud/meelo-front`
|
|
- `arthichaud/meelo-scanner`
|
|
- `arthichaud/meelo-matcher`
|
|
|
|
Images are built via GitHub Actions on every release. Development uses hot-reload containers with mounted source directories.
|
|
|
|
## Key Features
|
|
|
|
### Flexible Metadata Model
|
|
Albums can have multiple releases (original, remaster, deluxe). Songs can have multiple tracks (studio, live, acoustic). Tracks link to source files. This hierarchy mirrors real-world music organization.
|
|
|
|
### Music Video Support
|
|
Videos are not bolted on. They have dedicated types (official, live, lyric video, etc.), link to songs, and stream through the transcoder. The UI treats them as equals to audio tracks.
|
|
|
|
### Multi-Provider Metadata
|
|
Matcher queries 8 sources:
|
|
- MusicBrainz (primary database)
|
|
- Genius (lyrics, descriptions)
|
|
- Wikipedia (artist/album context)
|
|
- Wikidata (structured data)
|
|
- Discogs (release details)
|
|
- AllMusic (editorial reviews)
|
|
- Metacritic (critic scores)
|
|
- LrcLib (synced lyrics)
|
|
|
|
Users configure provider priority in settings.json.
|
|
|
|
### Scrobbling Integration
|
|
Built-in support for Last.fm and ListenBrainz. OAuth flow for Last.fm, token-based for ListenBrainz. Scrobbles track plays automatically.
|
|
|
|
### Geographic Context
|
|
Areas (countries, cities, regions) are first-class entities with ISO 3166 codes. Artists link to areas. Areas form parent/child trees (city → state → country).
|
|
|
|
### Search Performance
|
|
MeiliSearch provides sub-100ms search across thousands of tracks. Typo tolerance handles misspellings. Faceted search filters by genre, year, type.
|
|
|
|
## Development Activity
|
|
|
|
- **40 releases** show consistent iteration
|
|
- **1,095 stars** indicate healthy community interest
|
|
- **Active CI/CD** with GitHub Actions per service
|
|
- **SonarCloud integration** enforces quality gates
|
|
- **Multi-language testing**: Jest (TypeScript), pytest (Python), Go testing
|
|
|
|
## Configuration Approach
|
|
|
|
### Environment Variables (.env)
|
|
Deployment settings: ports, URLs, directories, credentials for external services (Genius, Discogs, Last.fm).
|
|
|
|
### Settings File (settings.json)
|
|
User preferences: track filename regex, metadata source priority, provider enable/disable, compilation detection rules.
|
|
|
|
This split keeps deployment config separate from user preferences. Docker Compose handles .env, users edit settings.json through the UI or manually.
|
|
|
|
## Target Use Case
|
|
|
|
Meelo fits users who:
|
|
- Maintain large, well-organized music collections
|
|
- Want metadata control without manual database editing
|
|
- Need music video support beyond YouTube links
|
|
- Value data accuracy over convenience
|
|
- Run home servers or NAS devices
|
|
- Prefer self-hosting to cloud services
|
|
|
|
It does not fit users who:
|
|
- Want plug-and-play setup (8+ containers, complex config)
|
|
- Have messy folder structures (requires clean metadata or standard naming)
|
|
- Need lightweight deployment (heavy infrastructure stack)
|
|
- Avoid GPL-3.0 licensing
|
|
|
|
## Architectural Philosophy
|
|
|
|
Meelo embraces microservices despite being a self-hosted app. Each service has a single responsibility:
|
|
- Scanner watches files
|
|
- Matcher enriches metadata
|
|
- Server manages state
|
|
- Front displays data
|
|
|
|
This separation enables:
|
|
- Independent scaling (run multiple scanners for large libraries)
|
|
- Language-specific optimization (Go for I/O, Python for HTTP scraping)
|
|
- Isolated failures (matcher crash doesn't stop playback)
|
|
- Parallel development (teams can work on different services)
|
|
|
|
The tradeoff is operational complexity. Users must manage 8 containers, 4 languages, and inter-service communication. For the target audience (technical music collectors), this is acceptable.
|
|
|
|
## Comparison Context
|
|
|
|
Among self-hosted music servers:
|
|
- **Navidrome**: Simpler (single binary), less metadata flexibility
|
|
- **Funkwhale**: Federated, social features, lighter metadata model
|
|
- **Airsonic**: Java monolith, basic metadata, stable but dated
|
|
- **Jellyfin**: General media server, music is secondary
|
|
- **Plex**: Proprietary, cloud-dependent, limited metadata control
|
|
|
|
Meelo occupies the "sophisticated metadata, self-hosted, open source" niche. It's more complex than Navidrome but more capable. It's more focused than Jellyfin but less mature.
|
|
|
|
## Technical Highlights
|
|
|
|
### Monorepo Structure
|
|
All services live in one repository with shared tooling (Biome, Docker Compose). This simplifies version coordination and cross-service changes.
|
|
|
|
### Event-Driven Enrichment
|
|
Scanner publishes "file added" events to RabbitMQ. Matcher consumes them asynchronously. Server receives enriched metadata via API. This decoupling prevents blocking and enables retries.
|
|
|
|
### Type Safety
|
|
TypeScript (Server, Front), Go (Scanner), Python with Pyright (Matcher). All services use static typing. Prisma generates TypeScript types from database schema.
|
|
|
|
### Health Monitoring
|
|
Every Docker service has health checks. Compose orchestrates startup order: database first, then message queue, then application services, finally nginx. This prevents race conditions.
|
|
|
|
### Mobile Parity
|
|
Front monorepo includes web (Next.js) and mobile (Expo). Shared components and state management. Mobile app is not an afterthought.
|
|
|
|
## Deployment Models
|
|
|
|
### Production (docker-compose.yml)
|
|
Pre-built images from Docker Hub. Fast startup. No build tools needed. Suitable for end users.
|
|
|
|
### Development (docker-compose.dev.yml)
|
|
Hot reload for all services. Exposed ports for debugging. Mounted source directories. Suitable for contributors.
|
|
|
|
### Local Build (docker-compose.local.yml)
|
|
Builds images from source. Tests Dockerfile changes. Suitable for CI or custom modifications.
|
|
|
|
All three share the same infrastructure services (PostgreSQL, MeiliSearch, RabbitMQ). Only application services differ.
|
|
|
|
## Data Flow Example
|
|
|
|
1. User adds music files to library folder
|
|
2. Scanner detects new files via filesystem watch
|
|
3. Scanner extracts metadata (tags, duration, bitrate) using FFmpeg
|
|
4. Scanner generates AcoustID fingerprint
|
|
5. Scanner registers file with Server API
|
|
6. Scanner publishes "file added" event to RabbitMQ
|
|
7. Matcher consumes event
|
|
8. Matcher queries MusicBrainz using AcoustID
|
|
9. Matcher queries Genius for lyrics
|
|
10. Matcher queries Wikipedia for artist bio
|
|
11. Matcher pushes enriched metadata to Server API
|
|
12. Server updates database
|
|
13. Server updates MeiliSearch index
|
|
14. Front queries Server API
|
|
15. User sees new track with complete metadata
|
|
|
|
This flow demonstrates the event-driven architecture and multi-provider enrichment.
|
|
|
|
## Quality Assurance
|
|
|
|
### Testing
|
|
- **Server**: Jest unit tests for NestJS modules
|
|
- **Matcher**: pytest with async support for provider modules
|
|
- **Scanner**: Go testing for file parsing and fingerprinting
|
|
- **Coverage**: SonarCloud tracks coverage per service
|
|
|
|
### Linting
|
|
- **TypeScript**: Biome (replaces ESLint + Prettier)
|
|
- **Python**: Ruff + Pyright
|
|
- **Go**: golangci-lint
|
|
|
|
### CI/CD
|
|
GitHub Actions per service:
|
|
1. Lint code
|
|
2. Run tests
|
|
3. Upload coverage to SonarCloud
|
|
4. Build Docker image
|
|
5. Push to Docker Hub (on release)
|
|
|
|
Quality gates block merges if coverage drops or bugs are introduced.
|
|
|
|
## Configuration Files
|
|
|
|
### biome.json
|
|
Formatting rules: tabs, double quotes, line width 100. Applies to TypeScript (Server, Front).
|
|
|
|
### settings.json
|
|
User-editable preferences:
|
|
- `trackRegex`: Filename parsing pattern
|
|
- `metadata.source`: Prefer embedded tags or external providers
|
|
- `metadata.order`: Provider priority list
|
|
- `providers`: Enable/disable specific providers
|
|
- `compilations`: Rules for detecting compilation albums
|
|
|
|
### .env
|
|
Deployment secrets:
|
|
- `JWT_SIGNATURE`: Auth token signing key
|
|
- `GENIUS_ACCESS_TOKEN`: Genius API key
|
|
- `DISCOGS_ACCESS_TOKEN`: Discogs API key
|
|
- `LASTFM_API_KEY`, `LASTFM_API_SECRET`: Last.fm OAuth
|
|
- `PUBLIC_URL`: External URL for OAuth callbacks
|
|
- `CONFIG_DIR`, `DATA_DIR`: Volume mount paths
|
|
|
|
## First-Time Setup
|
|
|
|
1. Clone repository
|
|
2. Copy `.env.example` to `.env`
|
|
3. Fill in required credentials (Genius, Discogs, Last.fm)
|
|
4. Create `settings.json` with track regex and provider preferences
|
|
5. Run `docker-compose up -d`
|
|
6. Wait for health checks to pass
|
|
7. Navigate to `http://localhost:3000`
|
|
8. Register admin user
|
|
9. Create library pointing to music folder
|
|
10. Trigger initial scan via Scanner API
|
|
|
|
The system will scan files, extract metadata, query providers, and populate the database. Initial scan time depends on library size and provider response times.
|
|
|
|
## Maintenance Operations
|
|
|
|
### Rescan Library
|
|
POST to `/scanner/scan/:libraryId` triggers full rescan. Useful after bulk file changes.
|
|
|
|
### Clean Orphans
|
|
POST to `/scanner/clean` removes database entries for deleted files.
|
|
|
|
### Refresh Metadata
|
|
POST to `/scanner/refresh` re-queries providers for existing tracks. Updates descriptions, ratings, lyrics.
|
|
|
|
### Backup Database
|
|
Standard PostgreSQL dump. Volume is `meelo_db` in Docker.
|
|
|
|
### Update Services
|
|
Pull new images, restart containers. Database migrations run automatically via Prisma.
|
|
|
|
## Extension Points
|
|
|
|
### Custom Providers
|
|
Add new provider modules to Matcher. Implement provider interface (search, fetch metadata). Register in factory. No Server changes needed.
|
|
|
|
### Additional Scrobblers
|
|
Implement scrobbler interface in Server. Add OAuth flow if needed. Store credentials in UserScrobbler table.
|
|
|
|
### Alternative Frontends
|
|
Server API is provider-agnostic. Build custom clients (CLI, desktop app, voice assistant) using REST API.
|
|
|
|
### Transcoding Profiles
|
|
Configure Kyoo transcoder with custom profiles. Adjust bitrates, codecs, resolutions for different devices.
|
|
|
|
## Performance Characteristics
|
|
|
|
### Scan Speed
|
|
Go scanner processes ~100 files/second on SSD. Bottleneck is FFprobe metadata extraction, not file I/O.
|
|
|
|
### Search Latency
|
|
MeiliSearch returns results in <100ms for libraries up to 100k tracks. Scales linearly beyond that.
|
|
|
|
### Streaming Startup
|
|
Direct file streaming (no transcoding) starts in <500ms. Transcoded streams add 2-5s for initial segment generation.
|
|
|
|
### Metadata Enrichment
|
|
Matcher processes ~10 tracks/second. Limited by external provider rate limits (MusicBrainz: 1 req/sec, Genius: 10 req/sec).
|
|
|
|
## Resource Requirements
|
|
|
|
### Minimum
|
|
- **CPU**: 2 cores
|
|
- **RAM**: 4GB
|
|
- **Storage**: 10GB + music library size
|
|
- **Network**: 10 Mbps upload for remote streaming
|
|
|
|
### Recommended
|
|
- **CPU**: 4 cores (for transcoding)
|
|
- **RAM**: 8GB (MeiliSearch benefits from memory)
|
|
- **Storage**: SSD for database and search index
|
|
- **Network**: 50 Mbps upload for multiple streams
|
|
|
|
## Security Considerations
|
|
|
|
### Authentication
|
|
JWT tokens with configurable expiration. Bcrypt password hashing. API keys for internal service communication.
|
|
|
|
### Anonymous Access
|
|
`ALLOW_ANONYMOUS=1` disables auth. Useful for private networks. Not recommended for internet-exposed instances.
|
|
|
|
### External Providers
|
|
Credentials stored in .env. Never logged or exposed via API. Matcher makes requests server-side, not from client.
|
|
|
|
### File Access
|
|
Scanner and Server run as non-root in Docker. File permissions must allow read access. No write operations on music files.
|
|
|
|
## Community and Support
|
|
|
|
### Documentation
|
|
README covers setup. Wiki has advanced topics (custom providers, troubleshooting). API docs at `/api/docs`.
|
|
|
|
### Issue Tracker
|
|
GitHub Issues for bugs and features. Active maintainer responses. Template for bug reports.
|
|
|
|
### Contributions
|
|
Pull requests welcome. CI checks must pass. SonarCloud quality gates enforced. Biome formatting required.
|
|
|
|
### Roadmap
|
|
GitHub Projects track planned features. Community votes on priorities. Regular releases (every 2-3 weeks).
|
|
|
|
## Licensing Implications
|
|
|
|
GPL-3.0 requires:
|
|
- Source code disclosure for modifications
|
|
- Same license for derivative works
|
|
- No proprietary forks
|
|
|
|
This prevents commercial services from using Meelo without open-sourcing their changes. Acceptable for self-hosters, restrictive for SaaS providers.
|
|
|
|
## Summary
|
|
|
|
Meelo is a sophisticated, microservices-based music server for technical users who value metadata accuracy and flexibility. It trades operational simplicity for data model richness and extensibility. The event-driven architecture, multi-provider metadata enrichment, and first-class video support distinguish it from simpler alternatives. The GPL-3.0 license and heavy infrastructure requirements limit its audience to self-hosting enthusiasts with technical skills and well-organized music collections.
|