Commit Graph
2 Commits
  • Add bulk import, and a bounded queue to make it safe
    Requesting songs one at a time does not scale past a handful, so this adds a
    paste-a-list import. It accepts "Artist - Title", en dashes, "Title by
    Artist", numbered lists, bare titles, and Deezer album/playlist URLs, which
    get expanded into their tracks.
    
    The queue is the load-bearing part. Every request previously spawned its own
    asyncio task, so a 50-track paste would have launched 50 concurrent Soulseek
    searches -- each 10-40s of polling and real load on slskd -- and drowned the
    client. Work now goes through an asyncio.Queue drained by MUSE_WORKERS
    consumers, default 3. Requests return immediately and the batch drains at a
    civil rate. The queue is in memory, so jobs left 'queued' or 'searching' are
    re-queued at startup rather than stranded by a restart.
    
    Ranking needed two fixes found by testing against the real API:
    
    Import was calling deezer_search(limit=1), which fetches two rows, so the
    ranker was choosing between a demo and a rehearsal take while the studio
    version -- six times the popularity -- sat unseen further down. It now pulls
    a real candidate pool.
    
    More seriously, the Deezer ranker had no title-relevance term at all: only
    artist match, popularity and penalties. A different song by the right artist
    therefore competed on equal footing, and "Oasis - Live Forever" resolved to
    "Cloudburst" as soon as a variant penalty tipped the balance. Title overlap
    now dominates, and the variant penalty is small enough only to break ties
    between versions of the same song.
    
    Derivative terms match on word boundaries rather than substrings, so a bare
    "live" catches "Come As You Are (Live)" without penalising "Deliver Me" or
    "Olive", and a term is ignored when the user typed it -- "oasis live forever"
    still works.
    
    Verified against live slskd: six tracks pasted, six resolved to the correct
    song, five imported with correct album/year/track and link count 2, in about
    two minutes. The sixth returned an empty Soulseek search and is retryable
    from the UI.
    
    Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
  • muse: request a single song, get it tagged in your library
    Lidarr's smallest unit is a MusicBrainz release, so asking for one track
    means monitoring a whole discography. Soulseek trades individual files but
    has no concept of a request. muse bridges the two: search, click, and the
    song lands in the library correctly tagged.
    
    The design point is that the user picks the exact track before anything
    downloads, so muse holds artist, album, year and track number as ground
    truth and tags deterministically rather than inferring them afterwards from
    whatever a stranger named the file.
    
    Search is Deezer rather than MusicBrainz, decided on measurement:
    MusicBrainz put eight covers above the real recording for "radiohead creep"
    and never surfaced Queen for "bohemian rhapsody" across 90 results, because
    its index matches a word in a cover's title as readily as in the artist
    field. Re-ranking cannot rescue a result set missing the right row.
    
    Imports are os.link(), so downloads keep seeding from the Soulseek share
    while the library gets a second name for the same bytes.