use std::path::{Path, PathBuf};
use chrono::{DateTime, Local, NaiveDate, offset::Utc};
use futures::stream::StreamExt;
use regex::Regex;
use serde::Deserialize;
use crate::{Result, error::Error};
#[derive(Debug, Clone)]
pub struct InfoFile {
pub youtube_id: String,
pub filepath: PathBuf,
}
impl InfoFile {
pub async fn parse(&self) -> Result<(Info, PathBuf)> {
let content = smol::fs::read_to_string(&self.filepath).await?;
let ch_info: Info = serde_json::from_str(&content)?;
let image_filepath = self
.filepath
.with_extension("")
.with_extension("jpg");
Ok((ch_info, image_filepath))
}
}
pub async fn available_channel<P: AsRef<Path>>(dirpath: P) -> Result<InfoFile> {
let mut files: Vec<InfoFile> = vec![];
let pattern = r#"NA--([a-zA-Z0-9-_]{18,34})--.*\.info\.json"#;
let matcher = Regex::new(pattern).unwrap();
let mut entries = smol::fs::read_dir(dirpath.as_ref()).await?;
while let Some(entry) = entries.next().await {
let entry = entry?;
let path = entry.path();
if let Some(captures) = matcher.captures(&path.to_string_lossy()) {
let youtube_id = &captures[1];
let channel = InfoFile {
youtube_id: youtube_id.into(),
filepath: path,
};
files.push(channel);
}
}
match files.len() {
0 => Err(Error::MissingChannelInfoFile(dirpath.as_ref().into())),
1 => Ok(files.first().unwrap().clone()),
_ => Err(Error::MultipleChannelInfoFiles(dirpath.as_ref().into())),
}
}
#[derive(Debug, Clone, Deserialize)]
pub struct Info {
#[serde(rename = "modified_date")]
pub upload_date: String,
pub title: String,
pub description: String,
#[serde(rename = "webpage_url")]
pub link: String,
#[serde(rename = "channel")]
pub author: String,
}
impl Info {
pub(crate) fn pub_date(&self) -> DateTime<Utc> {
let naived_date = NaiveDate::parse_from_str(&self.upload_date, "%Y%m%d")
.unwrap()
.and_hms_opt(9, 10, 11)
.unwrap();
DateTime::<Utc>::from_naive_utc_and_offset(naived_date, Utc)
}
pub(crate) fn language(&self) -> &'static str {
"en"
}
pub(crate) fn category(&self) -> &'static str {
"Technology & Science"
}
pub(crate) fn last_build_date(&self) -> DateTime<Local> {
Local::now()
}
pub(crate) fn generator(&self) -> &'static str {
"ytdlp"
}
pub(crate) fn explicit_content(&self) -> &'static str {
"false"
}
pub(crate) fn channel_type(&self) -> &'static str {
"Serial"
}
}