Parse local file IDs

This commit is contained in:
ealasu 2022-11-27 20:03:36 -07:00
parent bf7cbbaadd
commit 5cfdb8c9dd
2 changed files with 35 additions and 9 deletions

View file

@ -84,6 +84,7 @@ https://github.com/librespot-org/librespot
It supports a lot of functionality, including audio previews and image
downloads even if librespot doesn't use that for playback itself.
- [core] Support downloading of lyrics
- [core] Support parsing `SpotifyId` for local files
- [main] Add all player events to `player_event_handler.rs`
- [main] Add an event worker thread that runs async to the main thread(s) but
sync to itself to prevent potential data races for event consumers

View file

@ -21,6 +21,7 @@ pub enum SpotifyItemType {
Playlist,
Show,
Track,
Local,
Unknown,
}
@ -33,6 +34,7 @@ impl From<&str> for SpotifyItemType {
"playlist" => Self::Playlist,
"show" => Self::Show,
"track" => Self::Track,
"local" => Self::Local,
_ => Self::Unknown,
}
}
@ -47,6 +49,7 @@ impl From<SpotifyItemType> for &str {
SpotifyItemType::Playlist => "playlist",
SpotifyItemType::Show => "show",
SpotifyItemType::Track => "track",
SpotifyItemType::Local => "local",
_ => "unknown",
}
}
@ -167,24 +170,30 @@ impl SpotifyId {
///
/// [Spotify URI]: https://developer.spotify.com/documentation/web-api/#spotify-uris-and-ids
pub fn from_uri(src: &str) -> SpotifyIdResult {
let mut uri_parts: Vec<&str> = src.split(':').collect();
// At minimum, should be `spotify:{type}:{id}`
if uri_parts.len() < 3 {
return Err(SpotifyIdError::InvalidFormat.into());
}
let (scheme, tail) = src.split_once(':').ok_or(SpotifyIdError::InvalidFormat)?;
let (item_type, id) = tail.split_once(':').ok_or(SpotifyIdError::InvalidFormat)?;
if uri_parts[0] != "spotify" {
if scheme != "spotify" {
return Err(SpotifyIdError::InvalidRoot.into());
}
let id = uri_parts.pop().unwrap_or_default();
let item_type = item_type.into();
// Local files have a variable-length ID: https://developer.spotify.com/documentation/general/guides/local-files-spotify-playlists/
// TODO: find a way to add this local file ID to SpotifyId.
// One possible solution would be to copy the contents of `id` to a new String field in SpotifyId,
// but then we would need to remove the derived Copy trait, which would be a breaking change.
if item_type == SpotifyItemType::Local {
return Ok(Self { item_type, id: 0 });
}
if id.len() != Self::SIZE_BASE62 {
return Err(SpotifyIdError::InvalidId.into());
}
Ok(Self {
item_type: uri_parts.pop().unwrap_or_default().into(),
item_type,
..Self::from_base62(id)?
})
}
@ -534,7 +543,7 @@ mod tests {
raw: &'static [u8],
}
static CONV_VALID: [ConversionCase; 4] = [
static CONV_VALID: [ConversionCase; 5] = [
ConversionCase {
id: 238762092608182713602505436543891614649,
kind: SpotifyItemType::Track,
@ -575,6 +584,14 @@ mod tests {
154, 27, 28, 251, 198, 242, 68, 86, 154, 224, 53, 108, 119, 187, 233, 216,
],
},
ConversionCase {
id: 0,
kind: SpotifyItemType::Local,
uri: "spotify:local:0000000000000000000000",
base16: "00000000000000000000000000000000",
base62: "0000000000000000000000",
raw: &[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
},
];
static CONV_INVALID: [ConversionCase; 3] = [
@ -676,6 +693,14 @@ mod tests {
}
}
#[test]
fn from_local_uri() {
let actual = SpotifyId::from_uri("spotify:local:xyz:123").unwrap();
assert_eq!(actual.id, 0);
assert_eq!(actual.item_type, SpotifyItemType::Local);
}
#[test]
fn to_uri() {
for c in &CONV_VALID {