Closed aNNiMON closed 3 months ago
spotifyfolders
generates a json file with folder structure:{
"type": "folder", // root node is always a folder
"children": [
{
"type": "playlist", // playlist type (contains uri)
"uri": "spotify:playlist:00000000000000000000"
},
{
"name": "Folder",
"type": "folder", // folder type (contains name, uri and 0..N children)
"uri": "spotify:user:aaaaaaaaaaa:folder:bbbbbbbbb",
"children": [ /*...*/ ]
}
]
}
playlist: Playlist 1
folder: Folder 1
playlist: Playlist 1.1
folder: Folder 1A
playlist: Playlist 1A1
playlist: Playlist 1.2
folder: Folder 1B
playlist: Playlist 1B1
playlist: Playlist 1B2
folder: Folder 2
playlist: Playlist 2.1
playlist: Playlist 2.2
To represent it in a flat view like we have now, we need to introduce the following fields:
is_folder: bool
— false for playlist, true for foldercurrent_level: usize
— what level the playlist or the folder belongs totarget_level: usize
— for folders: what level the folder points to. Unique for each folder.3.1. Why must the target level be unique? To avoid ambiguity when navigating to a folder:
playlist: Playlist 1 // (0, 0)
folder: Folder 1 // (0, 1)
playlist: Playlist 1.1 // (1, 1)
folder: Folder 2 // (0, 1)
playlist: Playlist 2.1 // (1, 1)
playlist: Playlist 2.2 // (1, 1)
If we want to render playlists in Folder 1 on a level 1, we'll render:
playlist: Playlist 1.1 // (1, 1)
playlist: Playlist 2.1 // (1, 1)
playlist: Playlist 2.2 // (1, 1)
The last two playlists aren't what we want to see.
3.2 Correct representation using unique target levels:
playlist: Playlist 1 // (0, 0)
folder: Folder 1 // (0, 1)
playlist: Playlist 1.1 // (1, 1)
folder: Folder 1A // (1, 2)
playlist: Playlist 1A1 // (2, 2)
playlist: Playlist 1.2 // (1, 1)
folder: Folder 1B // (1, 3)
playlist: Playlist 1B1 // (3, 3)
playlist: Playlist 1B2 // (3, 3)
folder: Folder 2 // (0, 4)
playlist: Playlist 2.1 // (4, 4)
playlist: Playlist 2.2 // (4, 4)
3.3 To jump back to a previous folder, an additional playlist folder with the swapped target and source levels is needed:
playlist: Playlist 1 // (0, 0)
folder: Folder 1 // (0, 1)
folder: ← Folder 1 // (1, 0) here
playlist: Playlist 1.1 // (1, 1)
folder: Folder 1A // (1, 2)
folder: ← Folder 1A // (2, 1) here
playlist: Playlist 1A1 // (2, 2)
playlist: Playlist 1.2 // (1, 1)
folder: Folder 1B // (1, 3)
folder: ← Folder 1B // (3, 1) here
playlist: Playlist 1B1 // (3, 3)
playlist: Playlist 1B2 // (3, 3)
folder: Folder 2 // (0, 4)
folder: ← Folder 2 // (4, 0) here
playlist: Playlist 2.1 // (4, 4)
playlist: Playlist 2.2 // (4, 4)
By reading the PlaylistFolders_cache.json
and passing it together with the flat Playlists retrieved from the API to the structurize
function, we can get a playlist folder representation as a regular flat vector.
If the PlaylistFolders_cache doesn't contain some playlists, they'll go to a root folder level: (0, 0)
.
https://github.com/aome510/spotify-player/blob/e61b6cfbd17bc9179d91110efb3d46c8c8bb78b6/spotify_player/src/playlist_folders.rs#L12-L21
Same for newly created playlists.
A playlist folder uri is not supported by the application at this time, but it's needed for proper playlist folder structuring. Moreover, I prepend the id with f
for folders and u
for up node, so folder's uri
shouldn't be used in the API calls:
How it works
spotifyfolders
generates a json file with folder structure:{ "type": "folder", // root node is always a folder "children": [ { "type": "playlist", // playlist type (contains uri) "uri": "spotify:playlist:00000000000000000000" }, { "name": "Folder", "type": "folder", // folder type (contains name, uri and 0..N children) "uri": "spotify:user:aaaaaaaaaaa:folder:bbbbbbbbb", "children": [ /*...*/ ] } ] }
- Imagine we have a playlist folder structure like this:
playlist: Playlist 1 folder: Folder 1 playlist: Playlist 1.1 folder: Folder 1A playlist: Playlist 1A1 playlist: Playlist 1.2 folder: Folder 1B playlist: Playlist 1B1 playlist: Playlist 1B2 folder: Folder 2 playlist: Playlist 2.1 playlist: Playlist 2.2
To represent it in a flat view like we have now, we need to introduce the following fields:
is_folder: bool
— false for playlist, true for foldercurrent_level: usize
— what level the playlist or the folder belongs totarget_level: usize
— for folders: what level the folder points to. Unique for each folder.3.1. Why must the target level be unique? To avoid ambiguity when navigating to a folder:
playlist: Playlist 1 // (0, 0) folder: Folder 1 // (0, 1) playlist: Playlist 1.1 // (1, 1) folder: Folder 2 // (0, 1) playlist: Playlist 2.1 // (1, 1) playlist: Playlist 2.2 // (1, 1)
If we want to render playlists in Folder 1 on a level 1, we'll render:
playlist: Playlist 1.1 // (1, 1) playlist: Playlist 2.1 // (1, 1) playlist: Playlist 2.2 // (1, 1)
The last two playlists aren't what we want to see. 3.2 Correct representation using unique target levels:
playlist: Playlist 1 // (0, 0) folder: Folder 1 // (0, 1) playlist: Playlist 1.1 // (1, 1) folder: Folder 1A // (1, 2) playlist: Playlist 1A1 // (2, 2) playlist: Playlist 1.2 // (1, 1) folder: Folder 1B // (1, 3) playlist: Playlist 1B1 // (3, 3) playlist: Playlist 1B2 // (3, 3) folder: Folder 2 // (0, 4) playlist: Playlist 2.1 // (4, 4) playlist: Playlist 2.2 // (4, 4)
3.3 To jump back to a previous folder, an additional playlist folder with the swapped target and source levels is needed:
playlist: Playlist 1 // (0, 0) folder: Folder 1 // (0, 1) folder: ← Folder 1 // (1, 0) here playlist: Playlist 1.1 // (1, 1) folder: Folder 1A // (1, 2) folder: ← Folder 1A // (2, 1) here playlist: Playlist 1A1 // (2, 2) playlist: Playlist 1.2 // (1, 1) folder: Folder 1B // (1, 3) folder: ← Folder 1B // (3, 1) here playlist: Playlist 1B1 // (3, 3) playlist: Playlist 1B2 // (3, 3) folder: Folder 2 // (0, 4) folder: ← Folder 2 // (4, 0) here playlist: Playlist 2.1 // (4, 4) playlist: Playlist 2.2 // (4, 4)
- By reading the
PlaylistFolders_cache.json
and passing it together with the flat Playlists retrieved from the API to thestructurize
function, we can get a playlist folder representation as a regular flat vector.- If the PlaylistFolders_cache doesn't contain some playlists, they'll go to a root folder
level: (0, 0)
. https://github.com/aome510/spotify-player/blob/e61b6cfbd17bc9179d91110efb3d46c8c8bb78b6/spotify_player/src/playlist_folders.rs#L12-L21Same for newly created playlists.
- A playlist folder uri is not supported by the application at this time, but it's needed for proper playlist folder structuring. Moreover, I prepend the id with
f
for folders andu
for up node, so folder'suri
shouldn't be used in the API calls:
Thanks for the detailed explanation. I get the idea now.
Using level
is confusing and level is not meant to be unique. (current_level, target_level)
should be replaced (current_id, target_id)
and should include a comment that a folder points to another folder represented by target_id
.
In addition, I don't think it's a good idea to modify playlist to support representing folder because playlist and folder are separate identities. My suggestion is to define a separate struct for folder and another enum PlaylistFolderItem
to represent either a folder or a playlist for use in action/UI state. IMHO, separating the data representation of playlist and folder also improves the code clarity and simplifies the implementation.
@aome510 for me current_id
is misleading because we also have a Spotify id "spotify:user:xxx:folder:yyy". What about current_folder_id
/ target_folder_id
and comment that it's a local id, not just a global Spotify id? Or current_folder_index
/ target_folder_index
?
Separate struct is okay, will do so.
@aome510 for me
current_id
is misleading because we also have a Spotify id "spotify:user:xxx:folder:yyy". What aboutcurrent_folder_id
/target_folder_id
and comment that it's a local id, not just a global Spotify id? Orcurrent_folder_index
/target_folder_index
?
That also works. I mean Spotify ID doesn't really apply to folder, so we don't really need to differentiate here. Comments/documentations will definitely make it more clear though.
@aome510 done
@aNNiMON sorry for the delay (I've been kinda busy lately). I've reviewed the PR again and pushed a commit to simplify the codes and processing logic further. Can you review the commit and test it to ensure that nothing breaks in terms of functionalities? I don't have folders so couldn't really test the feature myself.
@aome510, everything works fine. 👍 Thanks
Resolves #453
Demo
https://github.com/user-attachments/assets/7cbf8029-f9a8-43f7-b2c8-37b169bab459
Configuration
Follow the mikez/spotify-folders installation instructions. Then run
It generates a full playlist folders structure and stores to a json file, like this: