extensions icon indicating copy to clipboard operation
extensions copied to clipboard

Update `spotify-player` extension β€” 'Your Library' command: fetch ALL liked tracks - needed for proper functionality

Open inducingchaos opened this issue 1 year ago β€’ 1 comments

Problem

The 'Your Library' command needs your entire library dataset for accurate search results.

Spotify's API has limits:

This requires a balanced consideration of UX, performance, and API constraints. I implemented infinite fetching and caching to solve this.

Note: This PR only focuses on the "Liked Tracks" aspect of the 'Your Library' command, as well as the overlying architecture for other content types.

Future implementations needed for:

  • Podcasts
  • Albums
  • Artists
  • Playlists

Solution

Overview

  • Recursively fetch the entire library
  • Cross-instance caching with intelligent updates
  • API data minification to avoid memory issues
  • UX improvements to offset architectural challenges

Implementation Details

1. Data Fetching

  • Batched parallel requests
    • 5 parallel requests per batch
    • 50 tracks per request
    • Individual requests are too slow
    • Combined requests hit rate limits
  • Artificial delay between batches
    • 100ms to avoid rate limits
    • Exponential backoff (3 retries)

2. Caching

  • Persisted between Raycast runs using SWR-like strategy
  • Revalidation on every run
    • Uses total track count
    • Single track fetch from me/tracks
    • Instant search result updates
  • Manual refresh action
    • Re-fetches entire library
    • Default action for all list items
    • Accessible via cmd + r

3. Response Model + Memory Optimization

Minimized data structure:

interface MinimalTrack {
  id: string;
  name: string;
  artists: { name: string }[];
  album: {
    id: string;
    name: string;
    images: { url: string }[];
  };
  uri: string;
  duration_ms: number;
}

4. UX Improvements

  • Library loading/updating indicators
    • Progress percentage
    • Completion notifications

5. Miscellaneous

  • Enhanced action panel for songs
  • Standardized default actions
    • 'Enter' now plays instead of opening Spotify
  • Consistent Liked Tracks list item UI
    • Changed filtered results layout
    • Now shows: Title | Artists | Duration
  • Changed default behavior to close window after play actions

Known Issues

Memory Management

Data minimization limitations may require:

  1. Intra-fetch data-offloading to disk
  2. More sophisticated caching strategy
  3. 3rd party API for outsourcing operations

Current solution supports ~2,000 songs (verified)

  • Pre-allocated result array reduces memory pressure
  • Previous versions had memory heap overflow

Development Issues

  • Double-fetching observed
    • Possibly React dev mode behavior
    • Potential race condition:
    //  useYourLibrary.ts
    execute: options.execute !== false && !tracksLoading;
    //  useMySavedTracks.ts
    execute: options?.execute !== false;
    

UI/UX Challenges

  1. Progress/Loading State

    • Progress sometimes sticks at last percentage
    • May be dev-only issue during hot-reload
  2. Search Results

    • No pagination/limits due to Raycast list filtering
    • Categories complicate pagination
    • Potential solutions:
      • Custom filtering (reduces UX)
      • Programmatic pagination
      • Accept performance trade-off
  3. Performance

    • API-imposed fetch delays
    • Initial results flash
    • Search lag with large results
    • Cache hit latency
    • Progress indicator jitter

Error Handling

  • Not fully implemented
  • Current workaround: command re-run
  • User testing shows reliability

Cache Validation

Limitations:

  • Misses changes when total track count unchanged
  • Alternative approaches:
    1. 3rd party change observation
    2. Time-based invalidation
    3. Forward/backward validation
    4. Hybrid strategy

100% of the changes in this PR are AI-generated (except this one!). 🍾🀯 EDIT: not anymore!

Related

Closes #14663 and provides a half-solution for #7222.

Explore the full commit history here: https://github.com/inducingchaos/raycast-spotify-extension

Checklist

inducingchaos avatar Jan 13 '25 04:01 inducingchaos

Thank you for your first contribution! :tada:

πŸ”” @mattisssa @peduarte @sxn @dillionverma @andreaselia @stuart @tonka3000 @dancannon @pernielsentikaer @stevensd2m @erics118 @hjoelh @hobhouse @jatindotdev @the-devbear @rfaccio @badta5te @andyburris @thomaslombart @rhesamu @mpatel283 you might want to have a look.

You can use this guide to learn how to check out the Pull Request locally in order to test it.

You can expect an initial review within five business days.

raycastbot avatar Jan 13 '25 04:01 raycastbot

If more context is needed for any of these points, check this first: https://github.com/inducingchaos/raycast-spotify-extension/blob/d5ea9efddd76196df947ae1435424768f2cb0a51/DO_NOT_EDIT_PR_NOTES.md

It is the human-written version of the PR description before I optimized it for readability with Claude.

This update could probably use a bit of refactoring, my intent with it was to create a direction and working proof of concept.

I am now using this version as my go-to for playing songs but there is definitely room for improvement. Do as you wish with the code - to me, it has been proven to be reliable and production-worthy.

inducingchaos avatar Jan 14 '25 09:01 inducingchaos

Thanks for the PR @inducingchaos! So, all of the code changes are AI-generated, right? They seem to go well beyond the scope of the PR and remove some features, such as supporting Quicklinks when adding the playing song to the playlist.

We also have support for pagination in our @raycast/utils hooks, especially for useFetch and useCachedPromise, which I think is probably the solution that fits best to add more items in the Your Library command. While the implementation works here, the code may be harder to understand, increasing the maintenance costs for the future.

Let me know if you want to proceed with the changes I described, otherwise I'll take a look at it in the near future.

thomaslombart avatar Jan 17 '25 10:01 thomaslombart

Correct. I have to admit, it is a bit messy - my intent was to get a working POC while also kind of showing a direction for these problems. And I think you're right, it definitely deserves some slimming. Good catch on some of the features that were cut out unintentionally.

We also have support for pagination in our @raycast/utils hooks

I wish it was as easy as pagination... but the Spotify API doesn't support search queries (explained in the Problem section). Unless those hooks support custom recursive fetching logic - there's no other option.

the code may be harder to understand

In its current state I completely agree. However a refactored implementation (that could be shared across multiple commands) would be as simple as the addition of a fetching util, and the additional UX improvements to support it.

otherwise I'll take a look at it in the near future

Feel free! Anyone that wants to take over this, can. I want my initial commit to serve as a learning resource more than anything.

inducingchaos avatar Jan 17 '25 11:01 inducingchaos

I want to learn more about the Raycast development environment before finalizing this myself. I'm also building an extension at the moment, so reading the docs + implementing features from scratch should give me a better grip on properly structuring this

Maybe in a couple weeks' time I can revisit it

inducingchaos avatar Jan 17 '25 11:01 inducingchaos

This pull request has been automatically marked as stale because it did not have any recent activity.

It will be closed if no further activity occurs in the next 7 days to keep our backlog clean 😊

raycastbot avatar Jan 31 '25 13:01 raycastbot

This issue has been automatically closed due to inactivity.

Feel free to comment in the thread when you're ready to continue working on it πŸ™‚

You can also catch us in Slack if you want to discuss this.

raycastbot avatar Feb 07 '25 13:02 raycastbot