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
565 lines
16 KiB
Markdown
565 lines
16 KiB
Markdown
# Meelo Evaluation
|
|
|
|
## Strengths
|
|
|
|
### Data Model Sophistication
|
|
|
|
Meelo's data model is the most mature among self-hosted music servers. The Album/Release and Song/Track distinctions accurately represent real-world music organization.
|
|
|
|
**Album vs Release**:
|
|
- Albums are abstract concepts (e.g., "Abbey Road")
|
|
- Releases are physical/digital manifestations (original, 2019 remaster, deluxe edition)
|
|
- One album can have multiple releases with different track listings, mastering, labels
|
|
|
|
This mirrors how music collectors think. A remaster is not a different album, it's a different release of the same album.
|
|
|
|
**Song vs Track**:
|
|
- Songs are compositions (e.g., "Come Together")
|
|
- Tracks are recordings (studio version, live version, acoustic version)
|
|
- One song can have multiple tracks across different releases
|
|
|
|
This enables tracking different performances of the same composition without creating duplicate songs.
|
|
|
|
**Song Groups**:
|
|
- Group versions of the same composition (original, covers, remixes)
|
|
- Example: "Hallelujah" by Leonard Cohen, Jeff Buckley, Pentatonix
|
|
- Enables discovering different interpretations
|
|
|
|
No other self-hosted music server implements this level of versioning.
|
|
|
|
### Multi-Provider Metadata
|
|
|
|
Meelo queries 8 external providers:
|
|
1. **MusicBrainz**: Primary database, most accurate
|
|
2. **Genius**: Lyrics and song descriptions
|
|
3. **Wikipedia**: Artist/album context
|
|
4. **Wikidata**: Structured data
|
|
5. **Discogs**: Release details
|
|
6. **AllMusic**: Editorial reviews
|
|
7. **Metacritic**: Critic scores
|
|
8. **LrcLib**: Synced lyrics
|
|
|
|
**Aggregation Strategy**:
|
|
- Priority-based merging (MusicBrainz > Genius > Wikipedia)
|
|
- Concatenate descriptions from multiple sources
|
|
- Average ratings across providers
|
|
- Prefer synced lyrics over plain
|
|
|
|
**Result**: Richer metadata than single-provider systems. Descriptions combine MusicBrainz facts, Wikipedia context, and Genius annotations.
|
|
|
|
### Music Video Support
|
|
|
|
Videos are first-class citizens, not afterthoughts.
|
|
|
|
**Video Types**:
|
|
- Official music videos
|
|
- Live performances
|
|
- Lyric videos
|
|
- Behind the scenes
|
|
- Interviews
|
|
- Documentaries
|
|
|
|
**Integration**:
|
|
- Videos link to songs (same as audio tracks)
|
|
- Kyoo transcoder handles adaptive streaming
|
|
- UI treats videos equally with audio
|
|
|
|
**Comparison**:
|
|
- **Navidrome**: No video support
|
|
- **Jellyfin**: Videos are separate media type, not linked to songs
|
|
- **Plex**: Similar to Jellyfin
|
|
|
|
Meelo is the only self-hosted music server with proper music video integration.
|
|
|
|
### Event-Driven Architecture
|
|
|
|
RabbitMQ decouples scanning from enrichment.
|
|
|
|
**Flow**:
|
|
1. Scanner registers file with Server
|
|
2. Scanner publishes event to RabbitMQ
|
|
3. Matcher consumes event asynchronously
|
|
4. Matcher queries providers in parallel
|
|
5. Matcher pushes enriched metadata to Server
|
|
|
|
**Benefits**:
|
|
- Scanning doesn't block on provider queries
|
|
- Matcher can retry failed providers without re-scanning
|
|
- Multiple matchers can process events in parallel
|
|
- Provider failures don't stop scanning
|
|
|
|
**Comparison**:
|
|
- **Navidrome**: Synchronous metadata fetching blocks scanning
|
|
- **Airsonic**: No external metadata providers
|
|
|
|
### Scrobbling Built-In
|
|
|
|
Last.fm and ListenBrainz integration is native, not a plugin.
|
|
|
|
**Features**:
|
|
- OAuth flow for Last.fm
|
|
- Token-based auth for ListenBrainz
|
|
- Automatic scrobbling on track play
|
|
- "Now playing" updates
|
|
|
|
**Comparison**:
|
|
- **Navidrome**: Last.fm only, requires external scrobbler
|
|
- **Airsonic**: No built-in scrobbling
|
|
|
|
### Mobile App
|
|
|
|
Expo/React Native app shares code with web frontend.
|
|
|
|
**Shared**:
|
|
- Components (ArtistCard, AlbumCard, TrackList)
|
|
- Hooks (useArtists, useAlbums, usePlayback)
|
|
- State management (Jotai atoms)
|
|
|
|
**Mobile-Specific**:
|
|
- React Navigation instead of Next.js router
|
|
- AsyncStorage instead of localStorage
|
|
- expo-av for media playback
|
|
- expo-notifications for background playback
|
|
|
|
**Result**: Feature parity between web and mobile without duplicating code.
|
|
|
|
**Comparison**:
|
|
- **Navidrome**: Third-party mobile apps (Substreamer, Subtracks)
|
|
- **Jellyfin**: Official mobile app, but music is secondary
|
|
|
|
### Search Performance
|
|
|
|
MeiliSearch provides sub-100ms search across large libraries.
|
|
|
|
**Features**:
|
|
- Typo tolerance (handles misspellings)
|
|
- Faceted search (filter by genre, year, type)
|
|
- Instant results (as-you-type)
|
|
- Relevance ranking
|
|
|
|
**Indexed Entities**:
|
|
- Artists (name, sort name)
|
|
- Albums (name, artist name, type, release date)
|
|
- Songs (name, artist name, type)
|
|
- Videos (name, artist name, type)
|
|
|
|
**Comparison**:
|
|
- **Navidrome**: Database full-text search (slower, no typo tolerance)
|
|
- **Airsonic**: Basic SQL LIKE queries
|
|
|
|
### Active Development
|
|
|
|
**Indicators**:
|
|
- 40 releases (consistent iteration)
|
|
- 1,095 stars (healthy community)
|
|
- GitHub Actions CI/CD per service
|
|
- SonarCloud quality gates
|
|
- Regular commits (weekly)
|
|
|
|
**Comparison**:
|
|
- **Navidrome**: Active (single maintainer)
|
|
- **Airsonic**: Stagnant (last release 2020)
|
|
- **Funkwhale**: Active but slower
|
|
|
|
### Geographic Context
|
|
|
|
Areas (countries, cities, regions) are first-class entities.
|
|
|
|
**Features**:
|
|
- ISO 3166 codes
|
|
- Parent/child hierarchy (city → state → country)
|
|
- Artist associations (birthplace, formation location)
|
|
|
|
**Use Case**:
|
|
- Browse artists by location
|
|
- Discover local music scenes
|
|
- Understand artist context
|
|
|
|
**Comparison**: No other self-hosted music server has area support.
|
|
|
|
### Code Quality
|
|
|
|
**Measures**:
|
|
- SonarCloud enforces 80% coverage, no critical bugs
|
|
- Biome linting for TypeScript
|
|
- Pyright type checking for Python
|
|
- golangci-lint for Go
|
|
- Jest, pytest, Go testing
|
|
|
|
**Result**: High code quality, low bug rate.
|
|
|
|
## Weaknesses
|
|
|
|
### Complex Deployment
|
|
|
|
8+ containers required:
|
|
1. Server (NestJS)
|
|
2. Scanner (Go)
|
|
3. Matcher (Python)
|
|
4. Front (Next.js)
|
|
5. PostgreSQL
|
|
6. MeiliSearch
|
|
7. RabbitMQ
|
|
8. Kyoo Transcoder
|
|
9. Nginx
|
|
|
|
**Challenges**:
|
|
- Docker Compose orchestration
|
|
- Health check dependencies
|
|
- Volume management
|
|
- Network configuration
|
|
- Resource allocation
|
|
|
|
**Comparison**:
|
|
- **Navidrome**: Single binary, no dependencies
|
|
- **Airsonic**: Single JAR, embedded database option
|
|
|
|
**Impact**: High barrier to entry for non-technical users.
|
|
|
|
### Multi-Language Stack
|
|
|
|
4 languages across services:
|
|
- TypeScript (Server, Front)
|
|
- Go (Scanner)
|
|
- Python (Matcher)
|
|
- TypeScript again (Front mobile)
|
|
|
|
**Challenges**:
|
|
- Different toolchains (npm, go, pip)
|
|
- Different testing frameworks (Jest, Go testing, pytest)
|
|
- Different linting tools (Biome, golangci-lint, Ruff)
|
|
- Harder to contribute (need expertise in multiple languages)
|
|
|
|
**Comparison**:
|
|
- **Navidrome**: Single language (Go)
|
|
- **Airsonic**: Single language (Java)
|
|
|
|
**Impact**: Steeper learning curve for contributors.
|
|
|
|
### Heavy Infrastructure
|
|
|
|
Required services:
|
|
- **PostgreSQL**: Relational database
|
|
- **MeiliSearch**: Search engine
|
|
- **RabbitMQ**: Message queue
|
|
- **Kyoo Transcoder**: Video transcoding
|
|
|
|
**Resource Requirements**:
|
|
- Minimum: 4GB RAM, 2 CPU cores
|
|
- Recommended: 8GB RAM, 4 CPU cores
|
|
- Storage: 10GB + library size
|
|
|
|
**Comparison**:
|
|
- **Navidrome**: 512MB RAM, 1 CPU core, SQLite
|
|
- **Airsonic**: 1GB RAM, 1 CPU core, embedded database
|
|
|
|
**Impact**: Not suitable for low-power devices (Raspberry Pi 3, old NAS).
|
|
|
|
### Requires Clean Collection
|
|
|
|
Meelo works best with well-organized music:
|
|
- Embedded metadata (ID3 tags, Vorbis comments)
|
|
- Standard folder structure (Artist/Album/Track)
|
|
- Consistent naming
|
|
|
|
**Challenges**:
|
|
- Messy collections require manual cleanup
|
|
- Missing tags need filename regex
|
|
- Inconsistent naming breaks matching
|
|
|
|
**Comparison**:
|
|
- **Navidrome**: More forgiving, uses folder structure
|
|
- **Jellyfin**: Handles messy collections better
|
|
|
|
**Impact**: Not suitable for users with poorly organized libraries.
|
|
|
|
### GPL-3.0 License
|
|
|
|
**Restrictions**:
|
|
- Derivative works must be GPL-3.0
|
|
- Source code must be disclosed
|
|
- No proprietary forks
|
|
|
|
**Impact**:
|
|
- Prevents commercial SaaS offerings
|
|
- Limits corporate adoption
|
|
- Acceptable for self-hosters, restrictive for businesses
|
|
|
|
**Comparison**:
|
|
- **Navidrome**: GPL-3.0 (same restrictions)
|
|
- **Jellyfin**: GPL-2.0 (similar restrictions)
|
|
- **Airsonic**: GPL-3.0 (same restrictions)
|
|
|
|
### Kyoo Transcoder Dependency
|
|
|
|
Video transcoding relies on external project (Kyoo).
|
|
|
|
**Risks**:
|
|
- Kyoo development stalls
|
|
- Breaking changes in Kyoo API
|
|
- Meelo must maintain compatibility
|
|
|
|
**Comparison**:
|
|
- **Jellyfin**: Built-in transcoder (FFmpeg wrapper)
|
|
- **Plex**: Built-in transcoder
|
|
|
|
**Impact**: Video support is fragile.
|
|
|
|
### No Prometheus Metrics
|
|
|
|
No built-in metrics for monitoring.
|
|
|
|
**Missing**:
|
|
- Request rates
|
|
- Error rates
|
|
- Latency percentiles
|
|
- Queue depths
|
|
- Provider response times
|
|
|
|
**Workaround**: Parse logs or use external monitoring.
|
|
|
|
**Comparison**:
|
|
- **Navidrome**: Prometheus metrics endpoint
|
|
- **Jellyfin**: No metrics
|
|
|
|
**Impact**: Harder to monitor in production.
|
|
|
|
## Integration Potential
|
|
|
|
### Data Model
|
|
|
|
**Applicability**: Excellent reference for metadata aggregator.
|
|
|
|
**Lessons**:
|
|
- Separate abstract entities (Album, Song) from concrete instances (Release, Track)
|
|
- Use song groups for versioning
|
|
- Store external metadata separately from core entities
|
|
- Use local identifiers for cross-referencing
|
|
|
|
**Adoption**:
|
|
- Implement Album/Release distinction
|
|
- Implement Song/Track distinction
|
|
- Implement song groups for covers/remixes
|
|
- Separate ExternalMetadata table
|
|
|
|
### Provider Pattern
|
|
|
|
**Applicability**: Directly applicable to metadata aggregator.
|
|
|
|
**Architecture**:
|
|
- Base provider interface (search, fetch)
|
|
- Per-provider modules (musicbrainz.py, genius.py)
|
|
- Factory pattern for provider instantiation
|
|
- Parallel queries with asyncio
|
|
- Rate limiting per provider
|
|
- Priority-based aggregation
|
|
|
|
**Adoption**:
|
|
- Copy provider interface design
|
|
- Implement factory pattern
|
|
- Use asyncio for parallel queries
|
|
- Implement per-provider rate limiters
|
|
- Use priority-based merging
|
|
|
|
### Event-Driven Enrichment
|
|
|
|
**Applicability**: Scalable approach for metadata aggregator.
|
|
|
|
**Architecture**:
|
|
- Scanner publishes events to queue
|
|
- Matcher consumes events asynchronously
|
|
- Server receives enriched metadata via API
|
|
- Decouples scanning from enrichment
|
|
|
|
**Adoption**:
|
|
- Use message queue (RabbitMQ, Redis Streams)
|
|
- Separate scanner and matcher services
|
|
- Enable retries without re-scanning
|
|
|
|
### Search Integration
|
|
|
|
**Applicability**: Fast search is critical for metadata aggregator.
|
|
|
|
**Architecture**:
|
|
- MeiliSearch for full-text search
|
|
- Index on entity creation/update
|
|
- Typo tolerance and faceted search
|
|
- Sub-100ms response times
|
|
|
|
**Adoption**:
|
|
- Integrate MeiliSearch or Typesense
|
|
- Index artists, albums, songs
|
|
- Implement as-you-type search
|
|
|
|
## Relevance to Metadata Aggregator
|
|
|
|
### High Relevance
|
|
|
|
**Data Model**:
|
|
- Album/Release and Song/Track distinctions are essential for accurate metadata
|
|
- Song groups enable tracking versions and covers
|
|
- External metadata separation keeps provider data clean
|
|
|
|
**Provider Architecture**:
|
|
- Factory pattern simplifies adding new providers
|
|
- Parallel queries optimize performance
|
|
- Rate limiting prevents API bans
|
|
- Priority-based aggregation ensures quality
|
|
|
|
**Event-Driven Design**:
|
|
- Decouples metadata fetching from file scanning
|
|
- Enables retries without re-processing
|
|
- Scales horizontally (multiple matchers)
|
|
|
|
### Medium Relevance
|
|
|
|
**Search Integration**:
|
|
- Fast search improves user experience
|
|
- Typo tolerance handles misspellings
|
|
- Faceted search enables filtering
|
|
|
|
**Scrobbling**:
|
|
- OAuth flows are reusable patterns
|
|
- Token management is standard practice
|
|
|
|
**Mobile App**:
|
|
- Code sharing between web and mobile reduces duplication
|
|
- Monorepo structure simplifies version coordination
|
|
|
|
### Low Relevance
|
|
|
|
**Video Support**:
|
|
- Metadata aggregator may not handle videos
|
|
- Transcoding is out of scope
|
|
|
|
**Geographic Context**:
|
|
- Areas are nice-to-have, not essential
|
|
- ISO 3166 codes are useful for standardization
|
|
|
|
**Deployment Complexity**:
|
|
- Metadata aggregator may use simpler deployment (single service)
|
|
- Docker Compose is overkill for smaller projects
|
|
|
|
## Comparison with Alternatives
|
|
|
|
### vs Navidrome
|
|
|
|
**Meelo Advantages**:
|
|
- Richer data model (Album/Release, Song/Track)
|
|
- Multi-provider metadata (8 vs 1)
|
|
- Music video support
|
|
- Built-in scrobbling
|
|
- Search performance (MeiliSearch vs SQL)
|
|
|
|
**Navidrome Advantages**:
|
|
- Simpler deployment (single binary)
|
|
- Lower resource requirements (512MB vs 4GB)
|
|
- Faster startup (no dependencies)
|
|
- More mature (older project)
|
|
|
|
**Verdict**: Meelo for metadata richness, Navidrome for simplicity.
|
|
|
|
### vs Jellyfin
|
|
|
|
**Meelo Advantages**:
|
|
- Music-focused (not general media server)
|
|
- Better music metadata (Album/Release, Song/Track)
|
|
- Multi-provider enrichment
|
|
- Faster search (MeiliSearch)
|
|
|
|
**Jellyfin Advantages**:
|
|
- Handles all media types (movies, TV, music)
|
|
- Larger community
|
|
- More mature
|
|
- Better transcoding (built-in)
|
|
|
|
**Verdict**: Meelo for music collectors, Jellyfin for general media.
|
|
|
|
### vs Airsonic
|
|
|
|
**Meelo Advantages**:
|
|
- Modern stack (NestJS, Next.js vs Java)
|
|
- Active development (40 releases vs stagnant)
|
|
- Better metadata (multi-provider)
|
|
- Search performance
|
|
|
|
**Airsonic Advantages**:
|
|
- Simpler deployment (single JAR)
|
|
- Subsonic API compatibility
|
|
- Larger ecosystem (mobile apps)
|
|
|
|
**Verdict**: Meelo for modern features, Airsonic for stability.
|
|
|
|
### vs Funkwhale
|
|
|
|
**Meelo Advantages**:
|
|
- Better metadata model
|
|
- Multi-provider enrichment
|
|
- Faster search
|
|
|
|
**Funkwhale Advantages**:
|
|
- Federated (share music across instances)
|
|
- Social features (follows, favorites)
|
|
- Podcast support
|
|
|
|
**Verdict**: Meelo for personal use, Funkwhale for communities.
|
|
|
|
## Recommendations for Metadata Aggregator
|
|
|
|
### Adopt
|
|
|
|
1. **Data Model**:
|
|
- Implement Album/Release distinction
|
|
- Implement Song/Track distinction
|
|
- Implement song groups for versions
|
|
- Separate ExternalMetadata table
|
|
|
|
2. **Provider Pattern**:
|
|
- Base provider interface
|
|
- Per-provider modules
|
|
- Factory pattern
|
|
- Parallel queries with asyncio
|
|
- Rate limiting per provider
|
|
- Priority-based aggregation
|
|
|
|
3. **Event-Driven Architecture**:
|
|
- Message queue for decoupling
|
|
- Separate scanner and matcher services
|
|
- Retry logic without re-scanning
|
|
|
|
### Adapt
|
|
|
|
1. **Search Integration**:
|
|
- Use MeiliSearch or Typesense
|
|
- Index on entity creation/update
|
|
- Implement typo tolerance
|
|
|
|
2. **Scrobbling**:
|
|
- OAuth flows for Last.fm
|
|
- Token-based auth for ListenBrainz
|
|
|
|
3. **Code Quality**:
|
|
- Linting (Biome, Ruff)
|
|
- Type checking (TypeScript, Pyright)
|
|
- Testing (Jest, pytest)
|
|
- SonarCloud quality gates
|
|
|
|
### Avoid
|
|
|
|
1. **Complex Deployment**:
|
|
- Prefer single service or fewer containers
|
|
- Avoid heavy infrastructure (PostgreSQL, RabbitMQ) if possible
|
|
- Use SQLite for smaller deployments
|
|
|
|
2. **Multi-Language Stack**:
|
|
- Stick to one or two languages
|
|
- Avoid mixing TypeScript, Go, Python unless necessary
|
|
|
|
3. **Kyoo Dependency**:
|
|
- If video support needed, use built-in transcoder (FFmpeg)
|
|
- Avoid external dependencies for core features
|
|
|
|
## Summary
|
|
|
|
Meelo excels at data modeling, multi-provider metadata enrichment, and music video support. The Album/Release and Song/Track distinctions are the most accurate representation of real-world music organization among self-hosted servers. The provider pattern with parallel queries and priority-based aggregation is directly applicable to metadata aggregators. The event-driven architecture scales well and decouples concerns. However, deployment complexity (8+ containers), multi-language stack (TypeScript, Go, Python), and heavy infrastructure (PostgreSQL, MeiliSearch, RabbitMQ) limit accessibility. The GPL-3.0 license restricts commercial use. For a metadata aggregator, adopt the data model and provider architecture, adapt the search integration and scrobbling patterns, but avoid the deployment complexity and multi-language stack. Meelo is an excellent reference for sophisticated metadata handling in a self-hosted context.
|