kopuz-hooks 0.7.0

A modern, lightweight music player built with Rust and Dioxus.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PlaybackItemRef<'a> {
    Local(&'a str),
    Server {
        service: &'a str,
        item_id: &'a str,
        extra: Option<&'a str>,
    },
    Radio {
        station_id: &'a str,
        stream_id: &'a str,
    },
}

impl<'a> PlaybackItemRef<'a> {
    pub fn parse(value: &'a str) -> Self {
        let mut parts = value.split(':');
        let scheme = parts.next().unwrap_or_default();
        match scheme {
            "radio" => Self::Radio {
                station_id: parts.next().unwrap_or_default(),
                stream_id: parts.next().unwrap_or_default(),
            },
            "jellyfin" | "subsonic" | "custom" | "ytmusic" | "soundcloud" => Self::Server {
                service: scheme,
                item_id: parts.next().unwrap_or_default(),
                extra: parts.next(),
            },
            _ => Self::Local(value),
        }
    }

    pub fn is_radio(self) -> bool {
        matches!(self, Self::Radio { .. })
    }

    pub fn is_server(self) -> bool {
        matches!(self, Self::Server { .. })
    }

    pub fn primary_id(self) -> Option<&'a str> {
        match self {
            Self::Server { item_id, .. } => Some(item_id),
            Self::Radio { station_id, .. } => Some(station_id),
            Self::Local(_) => None,
        }
    }

    pub fn stream_id(self) -> Option<&'a str> {
        match self {
            Self::Radio { stream_id, .. } => Some(stream_id),
            Self::Server { extra, .. } => extra,
            Self::Local(_) => None,
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ResolvedStreamRef<'a> {
    Pending(&'a str),
    SoundCloudHls(&'a str),
    Direct(&'a str),
}

impl<'a> ResolvedStreamRef<'a> {
    pub fn pending_marker(item_id: &str) -> String {
        format!("__PENDING:{item_id}")
    }

    pub fn parse(value: &'a str) -> Self {
        if let Some(item_id) = value.strip_prefix("__PENDING:") {
            Self::Pending(item_id)
        } else if let Some(url) = value.strip_prefix("__SC_HLS:") {
            Self::SoundCloudHls(url)
        } else {
            Self::Direct(value)
        }
    }
}

#[cfg(test)]
mod tests {
    use super::{PlaybackItemRef, ResolvedStreamRef};

    #[test]
    fn parses_radio_item_refs() {
        assert_eq!(
            PlaybackItemRef::parse("radio:station:stream"),
            PlaybackItemRef::Radio {
                station_id: "station",
                stream_id: "stream",
            }
        );
    }

    #[test]
    fn parses_server_item_refs() {
        assert_eq!(
            PlaybackItemRef::parse("ytmusic:video_id:extra"),
            PlaybackItemRef::Server {
                service: "ytmusic",
                item_id: "video_id",
                extra: Some("extra"),
            }
        );
    }

    #[test]
    fn parses_stream_markers() {
        assert_eq!(
            ResolvedStreamRef::parse("__PENDING:abc"),
            ResolvedStreamRef::Pending("abc")
        );
        assert_eq!(
            ResolvedStreamRef::parse("__SC_HLS:https://example.invalid/x.m3u8"),
            ResolvedStreamRef::SoundCloudHls("https://example.invalid/x.m3u8")
        );
    }
}