You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
220 lines
5.2 KiB
220 lines
5.2 KiB
use serde::de::{self, Visitor}; |
|
use serde::{Deserialize, Deserializer}; |
|
use std::fmt; |
|
use std::io; |
|
use std::marker::PhantomData; |
|
use std::process::ExitStatus; |
|
use std::string::FromUtf8Error; |
|
use tokio::task::JoinError; |
|
use url::Url; |
|
extern crate quick_xml; |
|
extern crate reqwest; |
|
extern crate serde_json; |
|
|
|
pub type Result<T> = std::result::Result<T, Error>; |
|
|
|
#[derive(Deserialize, Debug)] |
|
#[serde(rename_all = "camelCase")] |
|
pub struct InvidiousVideo { |
|
pub video_id: String, |
|
} |
|
|
|
#[derive(Deserialize, Debug)] |
|
pub struct VideoURL { |
|
pub url: Url, |
|
} |
|
|
|
#[derive(Deserialize, Debug)] |
|
pub struct VideoData { |
|
#[serde(default)] |
|
pub requested_formats: Vec<VideoURL>, |
|
#[serde(default)] |
|
pub url: Option<Url>, |
|
#[serde(deserialize_with = "convert_to_u64")] |
|
pub duration: u64, |
|
pub id: String, |
|
pub title: String, |
|
#[serde(default)] |
|
pub thumbnail: Option<Url>, |
|
#[serde(default)] |
|
pub is_live: Option<bool>, |
|
#[serde(skip)] |
|
pub json: String, |
|
} |
|
|
|
#[derive(Debug)] |
|
pub struct YoutubeDLError { |
|
pub status: ExitStatus, |
|
pub stdout: String, |
|
pub stderr: String, |
|
} |
|
|
|
#[derive(Debug)] |
|
pub enum Error { |
|
IO(io::Error), |
|
JoinError(JoinError), |
|
Reqwest(reqwest::Error), |
|
QuickXML(quick_xml::Error), |
|
SerdeJSON(serde_json::Error), |
|
FromUtf8Error(FromUtf8Error), |
|
YoutubeDL(YoutubeDLError), |
|
} |
|
|
|
impl From<io::Error> for Error { |
|
#[inline] |
|
fn from(error: io::Error) -> Error { |
|
Error::IO(error) |
|
} |
|
} |
|
|
|
impl From<JoinError> for Error { |
|
#[inline] |
|
fn from(error: JoinError) -> Error { |
|
Error::JoinError(error) |
|
} |
|
} |
|
|
|
impl From<reqwest::Error> for Error { |
|
#[inline] |
|
fn from(error: reqwest::Error) -> Error { |
|
Error::Reqwest(error) |
|
} |
|
} |
|
|
|
impl From<quick_xml::Error> for Error { |
|
#[inline] |
|
fn from(error: quick_xml::Error) -> Error { |
|
Error::QuickXML(error) |
|
} |
|
} |
|
|
|
impl From<serde_json::Error> for Error { |
|
#[inline] |
|
fn from(error: serde_json::Error) -> Error { |
|
Error::SerdeJSON(error) |
|
} |
|
} |
|
|
|
impl From<FromUtf8Error> for Error { |
|
#[inline] |
|
fn from(error: FromUtf8Error) -> Error { |
|
Error::FromUtf8Error(error) |
|
} |
|
} |
|
|
|
fn convert_to_u64<'de, D>(deserializer: D) -> std::result::Result<u64, D::Error> |
|
where |
|
D: Deserializer<'de>, |
|
{ |
|
struct ConvertToU64<T>(PhantomData<fn() -> T>); |
|
|
|
impl<'de> Visitor<'de> for ConvertToU64<u64> { |
|
type Value = u64; |
|
|
|
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { |
|
formatter.write_str("an integer between 0 and 2^63") |
|
} |
|
|
|
fn visit_i8<E>(self, value: i8) -> std::result::Result<Self::Value, E> |
|
where |
|
E: de::Error, |
|
{ |
|
use std::u64; |
|
if value >= 0 { |
|
Ok(value as u64) |
|
} else { |
|
Err(E::custom(format!("u64 out of range: {}", value))) |
|
} |
|
} |
|
|
|
fn visit_i16<E>(self, value: i16) -> std::result::Result<Self::Value, E> |
|
where |
|
E: de::Error, |
|
{ |
|
use std::u64; |
|
if value >= 0 { |
|
Ok(value as u64) |
|
} else { |
|
Err(E::custom(format!("u64 out of range: {}", value))) |
|
} |
|
} |
|
|
|
fn visit_i32<E>(self, value: i32) -> std::result::Result<Self::Value, E> |
|
where |
|
E: de::Error, |
|
{ |
|
use std::u64; |
|
if value >= 0 { |
|
Ok(value as u64) |
|
} else { |
|
Err(E::custom(format!("u64 out of range: {}", value))) |
|
} |
|
} |
|
|
|
fn visit_i64<E>(self, value: i64) -> std::result::Result<Self::Value, E> |
|
where |
|
E: de::Error, |
|
{ |
|
use std::u64; |
|
if value >= 0 { |
|
Ok(value as u64) |
|
} else { |
|
Err(E::custom(format!("u64 out of range: {}", value))) |
|
} |
|
} |
|
|
|
fn visit_u8<E>(self, value: u8) -> std::result::Result<Self::Value, E> |
|
where |
|
E: de::Error, |
|
{ |
|
Ok(u64::from(value)) |
|
} |
|
|
|
fn visit_u16<E>(self, value: u16) -> std::result::Result<Self::Value, E> |
|
where |
|
E: de::Error, |
|
{ |
|
Ok(u64::from(value)) |
|
} |
|
|
|
fn visit_u32<E>(self, value: u32) -> std::result::Result<Self::Value, E> |
|
where |
|
E: de::Error, |
|
{ |
|
Ok(u64::from(value)) |
|
} |
|
|
|
fn visit_u64<E>(self, value: u64) -> std::result::Result<Self::Value, E> |
|
where |
|
E: de::Error, |
|
{ |
|
Ok(value) |
|
} |
|
|
|
fn visit_f32<E>(self, value: f32) -> std::result::Result<Self::Value, E> |
|
where |
|
E: de::Error, |
|
{ |
|
let value = value.ceil(); |
|
if value >= 0.0 { |
|
Ok(value as u64) |
|
} else { |
|
Err(E::custom(format!("u64 out of range: {}", value))) |
|
} |
|
} |
|
|
|
fn visit_f64<E>(self, value: f64) -> std::result::Result<Self::Value, E> |
|
where |
|
E: de::Error, |
|
{ |
|
let value = value.ceil(); |
|
if value >= 0.0 { |
|
Ok(value as u64) |
|
} else { |
|
Err(E::custom(format!("u64 out of range: {}", value))) |
|
} |
|
} |
|
} |
|
|
|
deserializer.deserialize_any(ConvertToU64(PhantomData)) |
|
}
|
|
|