use std::io::BufRead;
use std::time::Duration;
use mediatype::{names, MediaTypeBuf};
use crate::model::{Image, MediaCommunity, MediaContent, MediaCredit, MediaObject, MediaRating, MediaText, MediaThumbnail, Text};
use crate::parser::util::{if_ok_then_some, if_some_then, parse_npt};
use crate::parser::{util, ParseErrorKind, ParseFeedError, ParseFeedResult};
use crate::xml::{Element, NS};
pub(crate) fn handle_media_group<R: BufRead>(element: Element<R>) -> ParseFeedResult<Option<MediaObject>> {
let mut media_obj = MediaObject::default();
for child in element.children() {
let child = child?;
if child.ns_and_tag().0 == NS::MediaRSS {
handle_media_element(child, &mut media_obj)?;
}
}
Ok(Some(media_obj))
}
pub(crate) fn handle_media_element<R: BufRead>(element: Element<R>, media_obj: &mut MediaObject) -> ParseFeedResult<()> {
let mut rating = None;
match element.ns_and_tag() {
(NS::MediaRSS, "title") => media_obj.title = handle_text(element)?,
(NS::MediaRSS, "content") => handle_media_content(element, media_obj)?,
(NS::MediaRSS, "thumbnail") => if_some_then(handle_media_thumbnail(element), |thumbnail| media_obj.thumbnails.push(thumbnail)),
(NS::MediaRSS, "description") => media_obj.description = handle_text(element)?,
(NS::MediaRSS, "community") => media_obj.community = handle_media_community(element)?,
(NS::MediaRSS, "credit") => if_some_then(handle_media_credit(element), |credit| media_obj.credits.push(credit)),
(NS::MediaRSS, "text") => if_some_then(handle_media_text(element), |text| media_obj.texts.push(text)),
(NS::MediaRSS, "rating") => rating = handle_media_rating(element),
_ => {}
}
if let Some(rating) = rating {
media_obj.content.iter_mut().for_each(|content| {
if content.rating.is_none() {
content.rating = Some(rating.clone());
}
})
}
Ok(())
}
fn handle_media_community<R: BufRead>(element: Element<R>) -> ParseFeedResult<Option<MediaCommunity>> {
let mut community = MediaCommunity::new();
for child in element.children() {
let child = child?;
match child.ns_and_tag() {
(NS::MediaRSS, "starRating") => {
for attr in &child.attributes {
match attr.name.as_str() {
"average" => if_ok_then_some(attr.value.parse::<f64>(), |v| community.stars_avg = v),
"count" => if_ok_then_some(attr.value.parse::<u64>(), |v| community.stars_count = v),
"min" => if_ok_then_some(attr.value.parse::<u64>(), |v| community.stars_min = v),
"max" => if_ok_then_some(attr.value.parse::<u64>(), |v| community.stars_max = v),
_ => {}
}
}
}
(NS::MediaRSS, "statistics") => {
for attr in &child.attributes {
match attr.name.as_str() {
"views" => if_ok_then_some(attr.value.parse::<u64>(), |v| community.stats_views = v),
"favorites" => if_ok_then_some(attr.value.parse::<u64>(), |v| community.stats_favorites = v),
_ => {}
}
}
}
_ => {}
}
}
Ok(Some(community))
}
fn handle_media_content<R: BufRead>(element: Element<R>, media_obj: &mut MediaObject) -> ParseFeedResult<()> {
let mut content = MediaContent::new();
for attr in &element.attributes {
match attr.name.as_str() {
"url" => content.url = util::parse_uri(&attr.value, element.xml_base.as_ref()),
"type" => if_ok_then_some(attr.value.parse::<MediaTypeBuf>(), |v| content.content_type = v),
"width" => if_ok_then_some(attr.value.parse::<u32>(), |v| content.width = v),
"height" => if_ok_then_some(attr.value.parse::<u32>(), |v| content.height = v),
"fileSize" => if_ok_then_some(attr.value.parse::<u64>(), |v| content.size = v),
"duration" => if_ok_then_some(attr.value.parse::<u64>(), |v| content.duration = v.map(Duration::from_secs)),
_ => {}
}
}
for child in element.children() {
let child = child?;
match child.ns_and_tag() {
(NS::MediaRSS, "player") => {
if let Some(player_url) = child.attr_value("url") {
let player_url = util::parse_uri(&player_url, element.xml_base.as_ref());
if player_url.is_some() {
content.url = player_url;
}
}
}
(NS::MediaRSS, "rating") => content.rating = handle_media_rating(child),
(NS::MediaRSS, "title") => {
if media_obj.title.is_none() {
media_obj.title = handle_text(child)?
}
}
(NS::MediaRSS, "description") => {
if media_obj.description.is_none() {
media_obj.description = handle_text(child)?
}
}
(NS::MediaRSS, "text") => if_some_then(handle_media_text(child), |text| media_obj.texts.push(text)),
(NS::MediaRSS, "credit") => if_some_then(handle_media_credit(child), |credit| media_obj.credits.push(credit)),
(NS::MediaRSS, _) => handle_media_element(child, media_obj)?,
_ => {}
}
}
if content.url.is_some() {
media_obj.content.push(content);
}
Ok(())
}
fn handle_media_credit<R: BufRead>(element: Element<R>) -> Option<MediaCredit> {
element.child_as_text().map(MediaCredit::new)
}
fn handle_media_rating<R: BufRead>(element: Element<R>) -> Option<MediaRating> {
let scheme = element.attr_value("scheme").unwrap_or_else(|| "urn:simple".into());
element.child_as_text().map(|rating| MediaRating::new(rating).urn(scheme.as_str()))
}
fn handle_media_text<R: BufRead>(element: Element<R>) -> Option<MediaText> {
let mut start_time = None;
let mut end_time = None;
let mut mime = None;
for attr in &element.attributes {
match attr.name.as_str() {
"start" => if_some_then(parse_npt(&attr.value), |npt| start_time = Some(npt)),
"end" => if_some_then(parse_npt(&attr.value), |npt| end_time = Some(npt)),
"type" => {
mime = match attr.value.as_str() {
"plain" => Some(MediaTypeBuf::new(names::TEXT, names::PLAIN)),
"html" => Some(MediaTypeBuf::new(names::TEXT, names::HTML)),
_ => None,
}
}
_ => {}
}
}
element.child_as_text().map(|t| {
let mut text = Text::new(t);
text.content_type = mime.map_or(MediaTypeBuf::new(names::TEXT, names::PLAIN), |m| m);
let mut media_text = MediaText::new(text);
media_text.start_time = start_time;
media_text.end_time = end_time;
media_text
})
}
fn handle_media_thumbnail<R: BufRead>(element: Element<R>) -> Option<MediaThumbnail> {
let mut url = None;
let mut width = None;
let mut height = None;
let mut time = None;
for attr in &element.attributes {
match attr.name.as_str() {
"url" => url = Some(attr.value.clone()),
"width" => if_ok_then_some(attr.value.parse::<u32>(), |v| width = v),
"height" => if_ok_then_some(attr.value.parse::<u32>(), |v| height = v),
"time" => if_some_then(parse_npt(&attr.value), |npt| time = Some(npt)),
_ => {}
}
}
if let Some(url) = url {
let mut image = Image::new(url);
image.width = width;
image.height = height;
let mut thumbnail = MediaThumbnail::new(image);
thumbnail.time = time;
Some(thumbnail)
} else {
None
}
}
fn handle_text<R: BufRead>(element: Element<R>) -> ParseFeedResult<Option<Text>> {
let type_attr = element.attributes.iter().find(|a| &a.name == "type").map_or("plain", |a| a.value.as_str());
let mime = match type_attr {
"plain" => Ok(MediaTypeBuf::new(names::TEXT, names::PLAIN)),
"html" => Ok(MediaTypeBuf::new(names::TEXT, names::HTML)),
_ => Err(ParseFeedError::ParseError(ParseErrorKind::UnknownMimeType(type_attr.into()))),
}?;
element
.children_as_string()?
.map(|content| {
let mut text = Text::new(content);
text.content_type = mime;
Some(text)
})
.ok_or(ParseFeedError::ParseError(ParseErrorKind::MissingContent("text")))
}