368 lines
No EOL
14 KiB
Markdown
368 lines
No EOL
14 KiB
Markdown
# 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. |