Create PLAYLIST_DOWNLOAD_IMPLEMENTATION.md

This commit is contained in:
Broque Thomas 2025-07-20 18:15:39 -07:00
parent 321d632b6a
commit decd1f3939

View file

@ -0,0 +1,368 @@
# Playlist Download Implementation Plan
## Overview
This document outlines the implementation plan for the "Download Missing Tracks" feature that enables intelligent playlist downloads with Plex integration and Soulseek source matching.
## Feature Requirements
### Core Functionality
- **Plex Integration**: Check track existence before downloading (if Plex connected)
- **Fallback Mode**: Download all tracks if no Plex connection
- **Intelligent Matching**: Use Spotify metadata to match Soulseek results
- **File Organization**: Create playlist-based folder structure in Transfer directory
- **Metadata Enhancement**: Apply Spotify metadata to downloaded tracks
- **Progress Tracking**: Real-time download progress and status updates
### User Experience Flow
1. User clicks "Download Missing Tracks" in playlist details modal
2. System checks Plex connection status
3. If Plex connected: analyze playlist and identify missing tracks
4. If no Plex: queue all tracks for download
5. Use existing Spotify matching for artist/track identification
6. Download tracks to Transfer/[PLAYLIST_NAME]/ folder structure
7. Apply Spotify metadata to downloaded files
## Current Architecture Analysis
### Downloads.py Strengths
- **Robust download workflow** with `start_download()`, `start_matched_download()` patterns
- **Sophisticated track matching** via `SpotifyMatchingModal` and `MusicMatchingEngine`
- **File organization system** with Transfer folder structure (Artist/Album/Track)
- **Queue management** with progress tracking and status monitoring
- **Metadata enhancement** from Spotify API integration
- **Thread-safe operations** with background workers
### Plex Integration Capabilities
- **Track existence checking** via `PlexClient._find_track()` method
- **Bulk track retrieval** with `search_tracks()` (up to 10K tracks)
- **Matching engine integration** with confidence scoring (0.7+ threshold)
- **Lazy connection pattern** prevents UI blocking
### Extension Points Identified
- `start_matched_download()` pattern can be extended for playlist context
- Existing `SpotifyMatchingModal` can handle batch artist selection
- Current Transfer folder organization supports playlist grouping
- Thread management patterns support playlist-scale operations
## Implementation Plan
### Phase 1: Core Infrastructure
#### 1. Create Playlist Download Service
**File**: `/services/playlist_download_service.py` (NEW)
**Purpose**: Orchestrate playlist-wide download operations
**Key Components**:
```python
class PlaylistDownloadService:
def __init__(self, spotify_client, plex_client, soulseek_client, downloads_page):
self.spotify_client = spotify_client
self.plex_client = plex_client
self.soulseek_client = soulseek_client
self.downloads_page = downloads_page
async def download_missing_tracks(self, playlist: PlaylistInfo):
"""Main entry point for playlist downloads"""
def _check_plex_existence(self, tracks: List[SpotifyTrack]) -> List[SpotifyTrack]:
"""Check which tracks are missing from Plex"""
def _queue_playlist_downloads(self, tracks: List[SpotifyTrack], playlist_name: str):
"""Add tracks to download queue with playlist organization"""
def _create_playlist_folder_structure(self, playlist_name: str) -> str:
"""Create Transfer/[PLAYLIST_NAME]/ directory structure"""
```
**Estimated Lines**: 300-400
#### 2. Extend Downloads Page Integration
**File**: `/ui/pages/downloads.py` (MODIFY)
**Changes Required**:
- Add `start_playlist_download()` method following existing patterns
- Extend download queue to support playlist containers
- Add playlist-level progress tracking
- Integrate with existing `SpotifyMatchingModal` for batch operations
**New Methods**:
```python
def start_playlist_download(self, playlist: PlaylistInfo):
"""Initiate playlist-wide download operation"""
def _handle_playlist_download_progress(self, playlist_id: str, progress: dict):
"""Track progress across multiple playlist downloads"""
def _organize_playlist_download(self, track_result: TrackResult, playlist_name: str):
"""Apply playlist-specific folder organization"""
```
**Estimated Lines Added**: 100-150
#### 3. Enhance Sync Page Modal
**File**: `/ui/pages/sync.py` (MODIFY)
**Changes Required**:
- Connect "Download Missing Tracks" button to playlist download service
- Add playlist download progress indicators
- Handle user feedback for Plex vs. non-Plex scenarios
**New Methods**:
```python
def on_download_missing_tracks_clicked(self):
"""Handle Download Missing Tracks button click"""
def _show_playlist_download_progress(self, playlist_id: str):
"""Display download progress in modal"""
def _handle_download_completion(self, playlist_id: str, results: dict):
"""Process playlist download completion"""
```
**Button Integration**:
```python
# In create_buttons() method
download_btn.clicked.connect(self.on_download_missing_tracks_clicked)
```
**Estimated Lines Added**: 50-75
### Phase 2: Plex Integration Optimization
#### 1. Enhance Track Existence Checking
**File**: `/core/plex_client.py` (MODIFY)
**New Methods**:
```python
def check_tracks_existence_batch(self, spotify_tracks: List[SpotifyTrack]) -> Dict[str, PlexTrackInfo]:
"""Efficiently check existence of multiple tracks"""
def _build_track_cache(self) -> Dict[str, PlexTrackInfo]:
"""Create in-memory cache of all Plex tracks for fast lookup"""
def _normalize_track_key(self, title: str, artist: str) -> str:
"""Create normalized key for track matching"""
```
**Optimization Strategy**:
- Cache all Plex tracks in memory for O(1) lookup
- Use normalized title+artist keys for matching
- Leverage existing `MusicMatchingEngine` for confidence scoring
- Implement batch processing for large playlists
**Estimated Lines Added**: 75-100
#### 2. Smart Download Logic Implementation
**Core Algorithm**:
```python
def determine_tracks_to_download(self, playlist_tracks: List[SpotifyTrack]) -> Tuple[List[SpotifyTrack], Dict[str, str]]:
"""
Returns:
- List of tracks to download
- Dictionary of skipped tracks with reasons
"""
if not self.plex_client.is_connected():
return playlist_tracks, {}
tracks_to_download = []
skipped_tracks = {}
plex_cache = self.plex_client._build_track_cache()
for track in playlist_tracks:
existing_track = self._find_in_cache(track, plex_cache)
if existing_track and self._calculate_confidence(track, existing_track) >= 0.8:
skipped_tracks[track.id] = f"Already exists in Plex: {existing_track.title}"
else:
tracks_to_download.append(track)
return tracks_to_download, skipped_tracks
```
### Phase 3: Enhanced Matching & Metadata
#### 1. Leverage Existing Spotify Matching
**Files**: `/ui/pages/downloads.py`, `/core/matching_engine.py` (MODIFY)
**Extensions Required**:
- Extend `SpotifyMatchingModal` for playlist context
- Add batch artist suggestion for album-heavy playlists
- Use existing confidence scoring algorithms
- Apply Spotify metadata to matched downloads
**Playlist Context Enhancement**:
```python
class PlaylistSpotifyMatchingModal(SpotifyMatchingModal):
def __init__(self, track_results: List[TrackResult], playlist_context: PlaylistInfo):
super().__init__(track_results)
self.playlist_context = playlist_context
def _generate_playlist_aware_suggestions(self):
"""Generate suggestions considering playlist context (album groupings, etc.)"""
```
#### 2. Soulseek Result Matching Integration
**Leverage Existing Systems**:
- Use existing search and matching algorithms from downloads.py
- Apply track info from Spotify API (title, artist, album, duration)
- Leverage existing `TrackResult` parsing and scoring
- Maintain current quality filtering and selection logic
### Phase 4: File Organization & Metadata
#### 1. Transfer Folder Structure
**Pattern Implementation**:
```
Transfer/
[PLAYLIST_NAME]/
ARTIST_NAME/
ARTIST_NAME - ALBUM_NAME/
01 - TRACK_NAME.flac
cover.jpg
ARTIST_NAME/
ARTIST_NAME - SINGLE_NAME/
SINGLE_NAME.flac
cover.jpg
```
**Organization Logic**:
```python
def _create_playlist_folder_structure(self, playlist_name: str, track: SpotifyTrack) -> str:
"""Create appropriate folder structure within playlist directory"""
base_path = os.path.join(config_manager.get('soulseek.transfer_path'), playlist_name)
if self._is_part_of_album(track):
return os.path.join(base_path, track.artist, f"{track.artist} - {track.album}")
else:
return os.path.join(base_path, track.artist, f"{track.artist} - {track.title}")
```
#### 2. Metadata Enhancement
**Integration with Existing Systems**:
- Apply Spotify track metadata to downloaded files
- Use existing metadata writing functionality from downloads.py
- Preserve track numbers, album info, cover art
- Maintain existing file naming conventions
## Technical Implementation Details
### Performance Optimizations
- **Plex Track Caching**: Load all Plex tracks once, cache in memory for O(1) lookups
- **Batch Processing**: Process large playlists (>100 tracks) in chunks
- **Progressive Downloads**: Use existing thread pool patterns for concurrent downloads
- **Background Processing**: Leverage existing worker thread architecture
### Error Handling Strategies
- **Plex Connection Failures**: Graceful fallback to "download all tracks" mode
- **Soulseek Search Failures**: Use existing retry mechanisms and error handling
- **Download Failures**: Leverage existing queue management and retry logic
- **Metadata Failures**: Graceful degradation with basic file naming
### User Experience Enhancements
- **Progress Indicators**: Real-time progress for playlist analysis and downloads
- **Skip Existing Option**: Clear feedback when tracks exist in Plex
- **Batch Operations**: Efficient handling of large playlists
- **Status Feedback**: Clear messaging for Plex vs. non-Plex scenarios
### Thread Safety Considerations
- **Queue Thread Safety**: Use existing `ThreadSafeQueueManager` patterns
- **UI Updates**: Leverage existing signal/slot patterns for thread-safe UI updates
- **Resource Management**: Follow existing thread cleanup and management patterns
## Integration Points
### Existing Service Integration
- **SyncService**: Extend existing playlist sync workflows
- **Downloads Page**: Integrate with current download management
- **Spotify Client**: Use existing OAuth and API integration
- **Plex Client**: Extend current track lookup and library access
### Signal/Slot Connections
```python
# In sync.py - PlaylistDetailsModal
self.download_btn.clicked.connect(self.on_download_missing_tracks_clicked)
# Progress tracking
self.playlist_download_service.progress_updated.connect(self.update_download_progress)
self.playlist_download_service.download_completed.connect(self.on_playlist_download_complete)
```
## Testing Strategy
### Unit Testing
- **Track Existence Checking**: Test Plex integration with various track matching scenarios
- **Download Logic**: Test playlist download workflow with and without Plex
- **File Organization**: Test folder structure creation and metadata application
### Integration Testing
- **End-to-End Workflow**: Test complete playlist download from button click to completion
- **Error Scenarios**: Test Plex disconnection, Soulseek failures, network issues
- **Large Playlist Handling**: Test performance with 100+ track playlists
### User Acceptance Testing
- **Plex Integration**: Verify correct skip behavior for existing tracks
- **Progress Tracking**: Ensure clear feedback during long download operations
- **File Organization**: Verify correct Transfer folder structure and metadata
## Future Enhancements (Post-MVP)
### Database Integration
- **Download History**: Track download history and statistics
- **Playlist Sync Status**: Store playlist sync timestamps and status
- **Quality Tracking**: Record download quality and source information
- **User Preferences**: Store user settings and preferences
### Advanced Features
- **Selective Download**: Allow users to manually select tracks from playlist
- **Quality Preferences**: Automatic quality selection based on user preferences
- **Duplicate Detection**: Advanced duplicate handling across playlists
- **Sync Scheduling**: Automatic periodic playlist synchronization
## Estimated Implementation Timeline
### Phase 1: Core Infrastructure (2-3 days)
- Create `PlaylistDownloadService`
- Basic downloads.py integration
- Sync page button connection
### Phase 2: Plex Integration (1-2 days)
- Optimize track existence checking
- Implement smart download logic
- Add playlist analysis
### Phase 3: Enhanced Matching (1-2 days)
- Extend Spotify matching for playlists
- Integrate with existing Soulseek matching
- Add metadata enhancement
### Phase 4: File Organization & Polish (1-2 days)
- Implement Transfer folder structure
- Add progress tracking and error handling
- User experience refinements
**Total Estimated Effort**: 5-9 days
## Success Criteria
### Functional Requirements
- ✅ Playlist tracks download to Transfer/[PLAYLIST_NAME]/ structure
- ✅ Plex integration skips existing tracks (confidence ≥ 0.8)
- ✅ Fallback to download all tracks when Plex not connected
- ✅ Spotify metadata applied to downloaded files
- ✅ Progress tracking and error handling
### Performance Requirements
- ✅ Playlist analysis completes within 10 seconds for 100-track playlists
- ✅ Download initiation within 5 seconds of button click
- ✅ UI remains responsive during playlist processing
### User Experience Requirements
- ✅ Clear feedback for Plex vs. non-Plex scenarios
- ✅ Progress indicators for long-running operations
- ✅ Intuitive folder organization in Transfer directory
- ✅ Error handling with clear user messaging
This implementation plan leverages the existing robust architecture while adding intelligent Plex checking and playlist-based download functionality in a maintainable and extensible way.