use crate::Streamable;
use regex::Regex;
use serde_json;
use stream_lib::StreamType;
use crate::utils::downloaders::DownloadClient;
use crate::utils::error::RsgetError;
use crate::utils::error::StreamError;
use chrono::prelude::*;
use serde_json::Value;
#[derive(Debug, Clone)]
pub struct DLive {
client: DownloadClient,
url: String,
apollo_state: Value,
}
impl Streamable for DLive {
fn new(url: String) -> Result<Box<DLive>, StreamError> {
let dc = DownloadClient::new()?;
let room_id_re = Regex::new(r"^(?:https?://)?(?:www\.)?dlive\.tv/([a-zA-Z0-9]+)")?;
let cap = match room_id_re.captures(&url) {
Some(capture) => capture,
None => return Err(StreamError::Rsget(RsgetError::new("No capture found"))),
};
let site_url = format!("https://dlive.tv/{}", &cap[1]);
let site_req = dc.make_request(&site_url, None)?;
let res: Result<String, StreamError> = dc.download_to_string(site_req);
match res {
Ok(some) => {
let apollo_state_re = Regex::new(r"__APOLLO_STATE__=(.*);\(function\(\)")?;
let apollo_state_cap = apollo_state_re.captures(&some).ok_or_else(|| {
StreamError::Rsget(RsgetError::new("Regex did not find any hostinfo"))
})?;
let apollo_state: Value = match serde_json::from_str(&apollo_state_cap[1]) {
Ok(state) => state,
Err(why) => return Err(StreamError::Json(why)),
};
let aps = apollo_state["defaultClient"]
.as_object()
.ok_or(RsgetError::Offline)?
.into_iter()
.find(|e| e.0.starts_with("user:"))
.ok_or(RsgetError::Offline)?
.1
.clone();
let xy = DLive {
client: dc,
url: url.clone(),
apollo_state: aps,
};
debug!("{:#?}", &xy);
Ok(Box::new(xy))
}
Err(why) => Err(why),
}
}
fn get_title(&self) -> Option<String> {
None
}
fn get_author(&self) -> Option<String> {
Some(
self.apollo_state["displayname"]
.as_str()
.unwrap()
.trim_end_matches('"')
.to_string(),
)
}
fn is_online(&self) -> bool {
!self.apollo_state["livestream"].is_null()
}
fn get_stream(&self) -> Result<StreamType, StreamError> {
Ok(StreamType::NamedPlaylist(
self.client
.rclient
.get(&format!(
"https://live.prd.dlive.tv/hls/live/{}.m3u8",
&self.apollo_state["username"]
.as_str()
.unwrap()
.trim_start_matches("%22")
.trim_end_matches("%22")
))
.build()?,
String::from("src"),
))
}
fn get_ext(&self) -> String {
String::from("mp4")
}
fn get_default_name(&self) -> String {
let local: DateTime<Local> = Local::now();
format!(
"{}-{:04}-{:02}-{:02}-{:02}-{:02}.{}",
self.get_author().unwrap(),
local.year(),
local.month(),
local.day(),
local.hour(),
local.minute(),
self.get_ext()
)
}
fn get_reqwest_client(&self) -> &reqwest::Client {
&self.client.rclient
}
}