7 KiB
Matched Downloads: A Deep Dive This document provides a detailed, step-by-step explanation of the "Matched Downloads" feature in the downloads.py module. This feature intelligently uses the Spotify API to correctly identify, tag, and organize downloaded music into a clean, structured library.
Feature Overview The core purpose of the Matched Download feature is to solve the problem of inconsistent or messy filenames commonly found on peer-to-peer networks. Instead of relying on the original filename, it uses the track's metadata to find a definitive match on Spotify. This allows the application to retrieve the correct artist, album, and track title, and then use that information to save the downloaded file in a standardized Artist/Album/Track folder structure.
The process can be broken down into four main stages:
Initiation: The user triggers a matched download from a search result.
Artist Matching: A modal dialog appears, suggesting potential artist matches from Spotify.
Download with Enhanced Metadata: The download proceeds, but the chosen Spotify artist information is attached to the download job.
Post-Download Organization: After the file is downloaded, it is automatically moved and renamed into a clean folder structure based on the matched Spotify data.
- Initiation: Starting the Matched Download The process begins when the user decides to use the matching feature on a search result.
UI Trigger: In the SearchResultItem (for single tracks) and AlbumResultItem (for albums), there is a dedicated "Matched Download" button, visually represented by a phone icon (📱).
Signal Connection: Clicking this button calls one of two methods in the main DownloadsPage class:
start_matched_download(search_result): For single tracks.
start_matched_album_download(album_result): For full albums.
- The Matching Process: SpotifyMatchingModal Once initiated, the DownloadsPage hands control over to the SpotifyMatchingModal. This dialog is the heart of the matching logic.
SpotifyMatchingModal Class This QDialog is responsible for finding and confirming the correct artist with the user.
init(self, track_result, spotify_client, matching_engine, parent):
The modal is initialized with the specific track_result to be matched.
It receives the spotify_client to communicate with the Spotify API and the matching_engine to score the similarity between names.
Automatic Suggestions: Immediately upon opening, the modal kicks off a background thread to find likely matches without freezing the UI.
generate_auto_suggestions(): This method starts the ArtistSuggestionThread.
ArtistSuggestionThread Class This QThread performs the Spotify search in the background.
run(): The thread's main execution method. It calls generate_artist_suggestions() and emits a suggestions_ready signal with the results.
generate_artist_suggestions(): This is where the core matching intelligence lies. It employs two main strategies to find potential artists:
Direct Artist Search: It takes the artist name from the Soulseek result and searches for it directly on Spotify.
Track Search: It performs a search on Spotify using the "artist - title" combination from the Soulseek result. It then inspects the artists of the resulting Spotify tracks.
For each potential artist found, it uses the MusicMatchingEngine.similarity_score() method to calculate a confidence score, which represents how closely the Spotify artist's name matches the original metadata.
The results are returned as a list of ArtistMatch objects, sorted by confidence.
User Interaction in the Modal display_auto_suggestions(suggestions): This method populates the modal's UI with the top 5 suggestions from the ArtistSuggestionThread. Each suggestion shows the artist's name, the confidence score, and the reason for the match.
Manual Search: If the automatic suggestions are incorrect, the user can type in a name to perform a manual search, which uses the ArtistSearchThread to get results.
select_artist(artist): When the user is satisfied and clicks the "Select" button for an artist, this method is triggered. It emits the crucial artist_selected signal, passing the selected Artist object, and then closes the dialog.
- Download with Enhanced Metadata The DownloadsPage listens for the artist_selected signal from the modal to proceed with the download.
_handle_matched_download(self, search_result, artist): This slot is connected to the modal's signal.
It receives both the original search_result and the artist the user selected on Spotify.
Metadata Enhancement: This is a key step. It attaches the selected Spotify Artist object directly to the search_result object by creating a new attribute: search_result.matched_artist = artist.
For albums, the _handle_matched_album_download method does this for every single track in the album, ensuring each track carries the correct artist and album context.
Finally, it calls the standard start_download(search_result) method. The download proceeds as normal, but the search_result object is now enriched with the definitive Spotify data.
- Post-Download Organization The final step occurs after the file has been successfully downloaded from the Soulseek user.
update_download_status(): This method periodically checks the status of all active downloads. When it finds a download that has completed, it performs a critical check:
It inspects the download_item to see if it has the matched_artist attribute that was attached in the previous step.
If the attribute exists, it calls _organize_matched_download().
_organize_matched_download(self, download_item, original_file_path): This function orchestrates the final file organization.
Create Directory Structure: It creates a base Transfer folder, and inside it, a folder for the artist (e.g., Transfer/Daft Punk/). It uses _sanitize_filename() to ensure folder names are valid.
Detect Album vs. Single: It calls _detect_album_info() to determine if the track is part of a larger album or a standalone single.
_detect_album_info(): This helper function first checks if the download_item already has album information (from being part of an AlbumResultItem). If not, it queries the Spotify API with the track title and matched artist to find the official album data.
Move and Rename File:
If Album: It creates a subfolder for the album (e.g., Transfer/Daft Punk/Daft Punk - Discovery/) and renames the file to a clean format: 01 - One More Time.flac.
If Single: It creates a subfolder for the single (e.g., Transfer/Virtual Riot/Virtual Riot - Pray For Riddim/) and renames the file: Pray For Riddim.mp3.
Download Cover Art: If the track was identified as part of an album, the _download_cover_art() function is called. It fetches the album's cover image from the Spotify API and saves it as cover.jpg inside the album's folder.
This completes the matched download process, turning a potentially messy file from random_user/daft-punk-discovery-2001/01_daft_punk_-_one_more_time.flac into a perfectly organized and tagged file at Transfer/Daft Punk/Daft Punk - Discovery/01 - One More Time.flac with accompanying cover art.