-
Notifications
You must be signed in to change notification settings - Fork 1
feat: fetch playlists via Tidal API with favorites fallback #12
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,5 +1,5 @@ | ||||||||||||
| import { LunaUnload, Tracer } from "@luna/core"; | ||||||||||||
| import { MediaItem, redux, ContextMenu, Playlist } from "@luna/lib"; | ||||||||||||
| import { MediaItem, redux, ContextMenu, Playlist, TidalApi } from "@luna/lib"; | ||||||||||||
|
|
||||||||||||
| export const { trace, errSignal } = Tracer("[MultiplePlaylists]"); | ||||||||||||
|
|
||||||||||||
|
|
@@ -9,6 +9,9 @@ export { Settings } from "./Settings"; | |||||||||||
| // Functions in unloads are called when plugin is unloaded. | ||||||||||||
| export const unloads = new Set<LunaUnload>(); | ||||||||||||
|
|
||||||||||||
| // Local tracking of songs added to playlists (server data can be stale) | ||||||||||||
| const addedSongs = new Map<string, Set<string>>(); // playlistId -> Set<songId> | ||||||||||||
|
|
||||||||||||
| // Function to show playlist selector modal | ||||||||||||
| async function showPlaylistSelector(song: MediaItem) { | ||||||||||||
|
|
||||||||||||
|
|
@@ -122,19 +125,56 @@ async function showPlaylistSelector(song: MediaItem) { | |||||||||||
| }); | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| // Type definition for playlist object structure | ||||||||||||
| interface PlaylistObject { | ||||||||||||
| uuid: string; | ||||||||||||
| title?: string; | ||||||||||||
| numberOfTracks?: number; | ||||||||||||
| type: string; | ||||||||||||
| isEditable?: boolean; | ||||||||||||
| isAutoGenerated?: boolean; | ||||||||||||
| isSuggested?: boolean; | ||||||||||||
| isRecommended?: boolean; | ||||||||||||
| creator?: { | ||||||||||||
| id?: string; | ||||||||||||
| }; | ||||||||||||
| // Fetch user's playlists via Tidal API (proven approach from squadgazzz/PlaylistTools) | ||||||||||||
| async function fetchUserPlaylists(): Promise<Array<{uuid: string, title: string, numberOfTracks: number}>> { | ||||||||||||
| const state = redux.store.getState(); | ||||||||||||
| const userId = state.session?.userId; | ||||||||||||
| if (!userId) throw new Error("Not logged in"); | ||||||||||||
|
||||||||||||
| if (!userId) throw new Error("Not logged in"); | |
| if (!userId) { | |
| trace("fetchUserPlaylists: no userId in session; returning empty playlist list"); | |
| return []; | |
| } |
Copilot
AI
Mar 26, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
isSongInPlaylist() always calls getPlaylistTrackIds() which iterates the entire playlist. This is invoked during UI rendering (per playlist) and again in addToSelectedPlaylists, so the same playlist can be fully scanned twice per modal open, and again on subsequent opens. Consider caching trackIds per playlist for the lifetime of the modal/session (or reusing the earlier duplicate-check result) to avoid repeated full playlist loads and reduce API load/latency.
Copilot
AI
Mar 26, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Local duplicate tracking is updated immediately after dispatch(...) without any confirmation that the add actually succeeded. If the underlying async add fails, the song will be treated as "already added" for the rest of the session and the user may be unable to retry. Consider only updating addedSongs after a confirmed success signal (e.g., observing the relevant store update / awaited API result), or make the tracking explicitly optimistic and roll it back if failure can be detected.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
addedSongsis a long-lived global Map that never gets cleared. If the plugin is used heavily across many playlists/songs, this can grow unbounded over the app lifetime. Consider clearing it on plugin unload (e.g., add an unload callback) and/or pruning per-playlist sets after some reasonable size/age.