use client::*;
use error::*;
use std::collections::HashMap;
use video::YoutubeVideo;
#[derive(Default)]
pub struct YoutubeClient<T> { connector: T }
impl<T: ClientConnector> YoutubeClient<T> {
pub fn new(connector: T) -> YoutubeClient<T> {
YoutubeClient { connector }
}
}
impl<T: ClientConnector> Client for YoutubeClient<T> {
type Video = YoutubeVideo;
fn query(&self, identifier: &str) -> Result<Self::Video> {
let uri = format!("http://youtube.com/get_video_info?video_id={}", identifier);
let info = self.connector.download_string(&uri)
.map_err(|e| Error::network("Unable to download resource", e))?;
parse_videos(&info)
}
}
fn parse_videos(info: &str) -> Result<YoutubeVideo> {
let map = map_url(info);
let title = map.get("title").ok_or(Error::video("title not found"))?.to_string();
let streams = map.get("url_encoded_fmt_stream_map").ok_or(Error::video("playback restricted"))?
.split(',')
.map(|s| {
let map = map_url(s);
map.get("quality").map(|q| q.to_lowercase()).ok_or(Error::video("quality not present"))
.and_then(|q| map.get("url").map(|u| (q, u.to_string())).ok_or(Error::video("url not present")))
})
.collect::<Result<HashMap<_, _>>>()?;
Ok(YoutubeVideo::new(title, streams))
}
fn map_url(url: &str) -> HashMap<String, String> {
use url::Url;
let fake_url = format!("http://thanks-smoqadam.com?{}", url);
let parsed_url = Url::parse(&fake_url).expect("invalid url");
parsed_url.query_pairs().into_owned().collect()
}