Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 41 additions & 1 deletion MIGRATION_STATUS.md
Original file line number Diff line number Diff line change
Expand Up @@ -205,10 +205,45 @@ Muine/
- Familiar API

## Known Issues
None currently. All 95 tests passing, code review clean, security scan pending.
None currently. All tests passing (note: some MusicBrainz API tests may fail intermittently due to rate limiting).

## New Features (Beyond Original Muine)

### Enhanced Metadata and MP3 Tagging (NEW)
Muine now includes comprehensive MusicBrainz integration for automatic metadata enhancement:
- **MusicBrainz API Integration**: Search and match songs to MusicBrainz database
- **Automatic Matching**: Match local files and YouTube songs to MusicBrainz entries
- **ID3 Tag Writing**: Write enhanced metadata to MP3, FLAC, OGG files including:
- Artist, Title, Album, Year, Track Number
- MusicBrainz Recording/Release/Artist IDs
- Genres/Tags from MusicBrainz
- **Album Artwork**: Download and embed cover art from Cover Art Archive
- **Disambiguation Support**: Handle multiple matches with confidence scoring
- **Background Queue**: Process metadata enhancement in background with rate limiting
- **Rate Limit Compliance**: Respects MusicBrainz 1 request/second limit
- **YouTube Enhancement**: Automatically enhance YouTube song metadata

**Services:**
- `MusicBrainzService`: Query MusicBrainz API with rate limiting
- `MetadataEnhancementService`: Orchestrate matching and enhancement
- `BackgroundTaggingQueue`: Background processing queue for metadata updates
- Extended `MetadataService`: Write tags and embed artwork

**Usage:**
```csharp
// Enhance a single song
var enhancementService = new MetadataEnhancementService();
var (enhancedSong, match) = await enhancementService.EnhanceSongAsync(song);

// Queue multiple songs for background processing
var taggingQueue = new BackgroundTaggingQueue();
taggingQueue.EnqueueSongs(songs);

// Auto-enhance during library scan
var scanner = new LibraryScannerService(metadata, database, coverArt, taggingQueue);
await scanner.ScanDirectoryAsync(path, progress, autoEnhanceMetadata: true);
```

### Internet Radio Support
Muine now includes comprehensive internet radio station support:
- **Stream Support**: Play HTTP audio streams (MP3, OGG, AAC, etc.)
Expand All @@ -235,6 +270,7 @@ Muine now includes comprehensive internet radio station support:
<PackageReference Include="Microsoft.Data.Sqlite" Version="10.0.1" />
<PackageReference Include="LibVLCSharp" Version="3.9.0" />
<PackageReference Include="RadioBrowser" Version="0.7.0" />
<PackageReference Include="MetaBrainz.MusicBrainz" Version="7.0.0" />
<PackageReference Include="VideoLAN.LibVLC.Windows" Version="3.0.21" />
<PackageReference Include="VideoLAN.LibVLC.Mac" Version="3.0.21" />

Expand All @@ -254,15 +290,19 @@ Muine now includes comprehensive internet radio station support:
- Database operations are async to avoid UI blocking
- File scanning uses async I/O
- Large music libraries should be scanned in background
- MusicBrainz queries are rate-limited (1 req/sec) and processed in background queue
- Consider implementing caching for album artwork

## Testing Strategy
- Unit tests for all models and services
- Integration tests for database operations
- MusicBrainz API tests (may require internet connection)
- UI tests (pending Avalonia UI completion)
- Manual testing on Linux (primary target platform)

## Resources
- [Avalonia Documentation](https://docs.avaloniaui.net/)
- [TagLib-Sharp API](https://github.com/mono/taglib-sharp)
- [SQLite .NET Provider](https://docs.microsoft.com/en-us/dotnet/standard/data/sqlite/)
- [MusicBrainz API](https://musicbrainz.org/doc/MusicBrainz_API)
- [Cover Art Archive](https://coverartarchive.org/)
243 changes: 243 additions & 0 deletions docs/MUSICBRAINZ_INTEGRATION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,243 @@
# MusicBrainz Metadata Enhancement

This document describes the MusicBrainz integration features added to Muine.

## Overview

Muine now includes comprehensive MusicBrainz integration for automatic metadata enhancement. This allows you to:
- Match local music files to MusicBrainz database entries
- Enhance YouTube song metadata with accurate artist/album information
- Write ID3 tags to MP3/FLAC/OGG files
- Download and embed album artwork from Cover Art Archive
- Process metadata updates in the background with rate limiting

## Features

### 1. MusicBrainz Service
The `MusicBrainzService` provides rate-limited access to the MusicBrainz API:

```csharp
using var service = new MusicBrainzService();

// Search for recordings
var matches = await service.SearchRecordingsAsync("The Beatles", "Let It Be", maxResults: 10);

// Get detailed recording information
var recording = await service.GetRecordingAsync(recordingId);

// Download cover art
await service.DownloadCoverArtAsync(releaseId, outputPath);
```

**Rate Limiting:** The service automatically enforces MusicBrainz's 1 request per second limit for unauthenticated access.

### 2. Metadata Enhancement Service
The `MetadataEnhancementService` orchestrates the matching and enhancement process:

```csharp
using var enhancer = new MetadataEnhancementService();

// Find matches for a song
var matches = await enhancer.FindMatchesAsync(song);

// Enhance with best match (writes to file)
var (enhancedSong, match) = await enhancer.EnhanceSongAsync(song,
writeToFile: true,
downloadCoverArt: true);

// Enhance with specific match (manual disambiguation)
var enhancedSong = await enhancer.EnhanceSongWithMatchAsync(song, match);

// Enhance YouTube song (no file writing)
var (ytEnhanced, ytMatch) = await enhancer.EnhanceYouTubeSongAsync(youtubeSong);
```

**Match Scoring:** Songs are matched based on artist, title, album, and year. Matches with confidence scores below 70% are rejected automatically.

### 3. Background Tagging Queue
The `BackgroundTaggingQueue` processes metadata updates in the background:

```csharp
using var queue = new BackgroundTaggingQueue();

// Subscribe to events
queue.WorkCompleted += (sender, args) =>
{
Console.WriteLine($"Tagged: {args.EnhancedSong.Title}");
};

queue.WorkFailed += (sender, args) =>
{
Console.WriteLine($"Failed: {args.Song.Title} - {args.ErrorMessage}");
};

// Queue single song
queue.EnqueueSong(song, downloadCoverArt: true);

// Queue multiple songs
queue.EnqueueSongs(songs, downloadCoverArt: true);

// Check status
Console.WriteLine($"Queue size: {queue.QueueSize}");
Console.WriteLine($"Processing: {queue.IsProcessing}");
```

**Background Processing:** The queue automatically respects rate limits and processes items one at a time. Events are raised when work completes or fails.

### 4. Library Scanner Integration
The library scanner now supports automatic metadata enhancement:

```csharp
var scanner = new LibraryScannerService(
metadataService,
databaseService,
coverArtService,
taggingQueue);

// Scan with auto-enhancement enabled
await scanner.ScanDirectoryAsync(
directory,
progress,
autoEnhanceMetadata: true);
```

When enabled, newly imported songs are automatically queued for metadata enhancement in the background.

## Metadata Written

The following metadata fields are written to audio files:

### Basic Tags
- Title
- Artist(s)
- Album
- Year
- Track Number
- Total Tracks
- Genres/Tags

### MusicBrainz IDs
For MP3 files (ID3v2):
- MusicBrainz Recording Id (TXXX frame)
- MusicBrainz Release Id (TXXX frame)
- MusicBrainz Artist Id (TXXX frame)

For FLAC/OGG files (Xiph comments):
- MUSICBRAINZ_TRACKID
- MUSICBRAINZ_ALBUMID
- MUSICBRAINZ_ARTISTID

### Album Artwork
Cover art is embedded as front cover picture with appropriate MIME type.

## Rate Limiting

MusicBrainz enforces rate limits:
- **Unauthenticated**: 1 request per second
- **Authenticated**: Higher limits (requires MusicBrainz account)

The services automatically enforce these limits. For large libraries, use the `BackgroundTaggingQueue` to process songs over time without blocking.

## Authentication (Optional)

While not currently exposed in the UI, MusicBrainz authentication can be configured programmatically:

```csharp
var service = new MusicBrainzService(
applicationName: "Muine",
applicationVersion: "1.0",
contactEmail: "your-email@example.com",
username: "your-username", // Optional
password: "your-password" // Optional
);
```

Authentication increases rate limits and provides access to additional features.

## API Documentation

For more information on the MusicBrainz API:
- [MusicBrainz API Documentation](https://musicbrainz.org/doc/MusicBrainz_API)
- [Cover Art Archive](https://coverartarchive.org/)
- [MusicBrainz Identifier Guidelines](https://musicbrainz.org/doc/MusicBrainz_Identifier)

## Error Handling

All services handle errors gracefully:
- API failures return empty results or null
- Network errors are logged but don't crash the application
- Rate limiting is enforced automatically
- Background queue continues processing even if individual items fail

## Performance Considerations

- MusicBrainz queries are rate-limited, so bulk operations take time
- Use the background queue for large libraries
- Cover art is downloaded once and cached
- Existing metadata is preserved if enhancement fails

## Examples

### Example 1: Enhance a Single Song
```csharp
using var enhancer = new MetadataEnhancementService();

var song = new Song
{
Title = "Hey Jude",
Artists = new[] { "Beatles" },
Filename = "/path/to/song.mp3"
};

var (enhanced, match) = await enhancer.EnhanceSongAsync(song);

if (enhanced != null)
{
Console.WriteLine($"Enhanced: {enhanced.Artist} - {enhanced.Title}");
Console.WriteLine($"Album: {enhanced.Album} ({enhanced.Year})");
}
```

### Example 2: Batch Process Library
```csharp
using var queue = new BackgroundTaggingQueue();

int completedCount = 0;
queue.WorkCompleted += (s, e) => completedCount++;

var allSongs = await database.GetAllSongsAsync();
queue.EnqueueSongs(allSongs);

Console.WriteLine($"Queued {allSongs.Count} songs for processing...");
// Processing continues in background
```

### Example 3: Manual Disambiguation
```csharp
using var enhancer = new MetadataEnhancementService();

var song = new Song { Title = "Time", Artists = new[] { "Pink Floyd" } };

// Get multiple matches
var matches = await enhancer.FindMatchesAsync(song, maxResults: 10);

// Display to user and let them choose
foreach (var match in matches)
{
Console.WriteLine($"{match.Title} - {match.Album} ({match.Year}) [Score: {match.MatchScore:P0}]");
}

// User selects match at index 2
var selectedMatch = matches[2];
var enhanced = await enhancer.EnhanceSongWithMatchAsync(song, selectedMatch);
```

## Future Enhancements

Planned improvements include:
- UI for manual metadata enhancement
- UI for disambiguation when multiple matches exist
- Settings page for MusicBrainz credentials
- Queue status indicator in main window
- Context menu "Enhance Metadata" option
- Bulk enhancement for entire albums
Loading