Skip to content

Commit

Permalink
Deserialize metadata from MEDIA_STATUS messages
Browse files Browse the repository at this point in the history
Also, introduce the TryFrom trait and new error types.
This removes all uses of unwrap from media.rs.
  • Loading branch information
g2p committed Mar 7, 2024
1 parent a34d92c commit 3e16742
Show file tree
Hide file tree
Showing 2 changed files with 140 additions and 25 deletions.
159 changes: 135 additions & 24 deletions src/channels/media.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,75 @@ impl Metadata {
}
}

impl TryFrom<&proxies::media::Metadata> for Metadata {
type Error = Error;

fn try_from(m: &proxies::media::Metadata) -> Result<Self, Error> {
Ok(match m.metadata_type {
0 => Self::Generic(GenericMediaMetadata {
title: m.title.clone(),
subtitle: m.subtitle.clone(),
images: m.images.iter().map(Image::from).collect(),
release_date: m.release_date.clone(),
}),
1 => Self::Movie(MovieMediaMetadata {
title: m.title.clone(),
subtitle: m.subtitle.clone(),
studio: m.studio.clone(),
images: m.images.iter().map(Image::from).collect(),
release_date: m.release_date.clone(),
}),
2 => Self::TvShow(TvShowMediaMetadata {
series_title: m.series_title.clone(),
episode_title: m.subtitle.clone(),
season: m.season,
episode: m.episode,
images: m.images.iter().map(Image::from).collect(),
original_air_date: m.original_air_date.clone(),
}),
3 => Self::MusicTrack(MusicTrackMediaMetadata {
album_name: m.album_name.clone(),
title: m.title.clone(),
album_artist: m.album_artist.clone(),
artist: m.artist.clone(),
composer: m.composer.clone(),
track_number: m.track_number,
disc_number: m.disc_number,
images: m.images.iter().map(Image::from).collect(),
release_date: m.release_date.clone(),
}),
4 => {
let mut dimensions = None;
let mut latitude_longitude = None;
if let Some(width) = m.width {
if let Some(height) = m.height {
dimensions = Some((width, height))
}
}
if let Some(lat) = m.latitude {
if let Some(long) = m.longitude {
latitude_longitude = Some((lat, long))
}
}
Self::Photo(PhotoMediaMetadata {
title: m.title.clone(),
artist: m.artist.clone(),
location: m.location.clone(),
latitude_longitude,
dimensions,
creation_date_time: m.creation_date_time.clone(),
})
}
_ => {
return Err(Error::Parsing(format!(
"Bad metadataType {}",
m.metadata_type
)))
}
})
}
}

/// Generic media metadata.
///
/// See also the [`GenericMediaMetadata` Cast reference](https://developers.google.com/cast/docs/reference/messages#GenericMediaMetadata).
Expand Down Expand Up @@ -255,6 +324,21 @@ impl Image {
}
}

impl From<&proxies::media::Image> for Image {
fn from(i: &proxies::media::Image) -> Self {
let mut dimensions = None;
if let Some(width) = i.width {
if let Some(height) = i.height {
dimensions = Some((width, height));
}
};
Self {
url: i.url.clone(),
dimensions,
}
}
}

/// Describes possible player states.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum PlayerState {
Expand Down Expand Up @@ -468,15 +552,17 @@ impl Media {
}
}

impl From<&proxies::media::Media> for Media {
fn from(m: &proxies::media::Media) -> Self {
Self {
impl TryFrom<&proxies::media::Media> for Media {
type Error = Error;

fn try_from(m: &proxies::media::Media) -> Result<Self, Error> {
Ok(Self {
content_id: m.content_id.to_string(),
stream_type: StreamType::from_str(m.stream_type.as_ref()).unwrap(),
stream_type: StreamType::from_str(m.stream_type.as_ref())?,
content_type: m.content_type.to_string(),
metadata: None, // TODO
metadata: m.metadata.as_ref().map(TryInto::try_into).transpose()?,
duration: m.duration,
}
})
}
}

Expand Down Expand Up @@ -548,6 +634,18 @@ pub struct ExtendedStatus {
pub media: Option<Media>,
}

impl TryFrom<&proxies::media::ExtendedStatus> for ExtendedStatus {
type Error = Error;

fn try_from(es: &proxies::media::ExtendedStatus) -> Result<Self, Error> {
Ok(Self {
player_state: ExtendedPlayerState::from_str(&es.player_state)?,
media_session_id: es.media_session_id,
media: es.media.as_ref().map(Media::try_from).transpose()?,
})
}
}

/// Detailed status of the media artifact with respect to the session.
#[derive(Clone, Debug)]
pub struct StatusEntry {
Expand Down Expand Up @@ -589,6 +687,31 @@ pub struct StatusEntry {
pub supported_media_commands: u32,
}

impl TryFrom<&proxies::media::Status> for StatusEntry {
type Error = Error;

fn try_from(x: &proxies::media::Status) -> Result<Self, Error> {
Ok(Self {
media_session_id: x.media_session_id,
media: x.media.as_ref().map(TryInto::try_into).transpose()?,
playback_rate: x.playback_rate,
player_state: PlayerState::from_str(x.player_state.as_ref())?,
idle_reason: x
.idle_reason
.as_ref()
.map(|reason| IdleReason::from_str(reason))
.transpose()?,
extended_status: x
.extended_status
.as_ref()
.map(ExtendedStatus::try_from)
.transpose()?,
current_time: x.current_time,
supported_media_commands: x.supported_media_commands,
})
}
}

/// Describes the load cancelled error.
#[derive(Copy, Clone, Debug)]
pub struct LoadCancelled {
Expand Down Expand Up @@ -1093,27 +1216,15 @@ where
MESSAGE_TYPE_MEDIA_STATUS => {
let reply: proxies::media::StatusReply = serde_json::value::from_value(reply)?;

let statuses_entries = reply.status.iter().map(|x| StatusEntry {
media_session_id: x.media_session_id,
media: x.media.as_ref().map(|m| m.into()),
playback_rate: x.playback_rate,
player_state: PlayerState::from_str(x.player_state.as_ref()).unwrap(),
idle_reason: x
.idle_reason
.as_ref()
.map(|reason| IdleReason::from_str(reason).unwrap()),
extended_status: x.extended_status.as_ref().map(|es| ExtendedStatus {
player_state: ExtendedPlayerState::from_str(&es.player_state).unwrap(),
media_session_id: es.media_session_id,
media: es.media.as_ref().map(|m| m.into()),
}),
current_time: x.current_time,
supported_media_commands: x.supported_media_commands,
});
let entries = reply
.status
.iter()
.map(StatusEntry::try_from)
.collect::<Result<_, _>>()?;

MediaResponse::Status(Status {
request_id: reply.request_id,
entries: statuses_entries.collect::<Vec<StatusEntry>>(),
entries,
})
}
MESSAGE_TYPE_LOAD_CANCELLED => {
Expand Down
6 changes: 5 additions & 1 deletion src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@ pub enum Error {
Io(IoError),
/// This variant includes all possible errors that come from Protobuf layer.
Protobuf(ProtobufError),
/// This variant includes everything related to (de)serialization of incoming and outgoing
/// Errors with JSON (de)serialization of incoming and outgoing
/// messages.
Serialization(SerializationError),
/// Errors parsing messages (valid JSON but bad semantics)
Parsing(String),
/// This variant is used to indicate invalid DNS name used to connect to Cast device.
Dns(InvalidDnsNameError),
/// This variant includes any error that comes from rustls.
Expand All @@ -34,6 +36,7 @@ impl Display for Error {
Error::Io(ref err) => Display::fmt(&err, f),
Error::Protobuf(ref err) => Display::fmt(&err, f),
Error::Serialization(ref err) => Display::fmt(&err, f),
Error::Parsing(ref message) => f.write_str(message),
Error::Tls(ref err) => Display::fmt(&err, f),
Error::Dns(ref err) => Display::fmt(&err, f),
Error::Namespace(ref err) => Display::fmt(&err, f),
Expand All @@ -50,6 +53,7 @@ impl StdError for Error {
Error::Dns(ref err) => Some(err),
Error::Serialization(ref err) => Some(err),
Error::Internal(_) => None,
Error::Parsing(_) => None,
Error::Namespace(_) => None,
}
}
Expand Down

0 comments on commit 3e16742

Please sign in to comment.