use std::path::{Path, PathBuf};
use async_std::{fs, stream::StreamExt};
use chrono::{DateTime, NaiveDate, offset::Utc};
use regex::Regex;
use serde::Deserialize;
use crate::Result;
#[derive(Debug, Clone)]
pub struct InfoFile {
pub pub_date: NaiveDate,
pub youtube_id: String,
pub filepath: PathBuf,
}
impl InfoFile {
pub async fn parse(&self) -> Result<(Info, Enclosure, PathBuf)> {
let content = async_std::fs::read_to_string(&self.filepath).await?;
let ep_info: Info = serde_json::from_str(&content)?;
let video_filepath: async_std::path::PathBuf = self
.filepath
.with_extension("")
.with_extension("mp4")
.into();
let video_filelength = video_filepath.metadata().await?.len();
let image_filepath = self
.filepath
.with_extension("")
.with_extension("png");
let enclosure = Enclosure {
video_filepath: video_filepath.into(),
video_filelength,
video_filetype: "mp4".into(),
};
Ok((ep_info, enclosure, image_filepath))
}
}
pub async fn available_episodes<P: AsRef<Path>>(dirpath: P) -> Result<Vec<InfoFile>> {
let mut episodes: Vec<InfoFile> = vec![];
let pattern = r#"(\d{8})--(.{11})--.*\.info\.json"#;
let matcher = Regex::new(pattern).unwrap();
let mut entries = 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 date_str = &captures[1];
let pub_date = NaiveDate::parse_from_str(date_str, "%Y%m%d").unwrap();
let youtube_id = &captures[2];
let episode = InfoFile {
pub_date,
youtube_id: youtube_id.into(),
filepath: path.into(),
};
episodes.push(episode);
}
}
episodes.sort_unstable_by_key(|b| b.pub_date);
Ok(episodes)
}
#[derive(Debug, Clone, Deserialize)]
pub struct Info {
#[serde(rename = "id")]
pub guid: String,
#[serde(rename = "upload_date")]
pub upload_date: String,
pub playlist_index: u32,
pub title: String,
#[serde(rename = "webpage_url")]
pub link: String,
pub description: String,
#[serde(rename = "channel")]
pub author: String,
#[serde(rename = "duration")]
pub duration_seconds: u32,
}
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)
}
}
#[derive(Debug, Clone)]
pub struct Enclosure {
pub video_filepath: PathBuf,
pub video_filelength: u64,
pub video_filetype: String,
}