soulsync/PLAYLIST_DOWNLOAD_IMPLEMENTATION.md
2025-07-20 18:15:39 -07:00

14 KiB

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:

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:

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:

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:

# 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:

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:

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:

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:

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

# 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.