/* This file is part of jellything (https://codeberg.org/metamuffin/jellything) which is licensed under the GNU Affero General Public License (version 3); see /COPYING. Copyright (C) 2025 metamuffin */ use anyhow::Context; use jellycommon::chrono::{format::Parsed, Utc}; use serde::{Deserialize, Serialize}; use std::collections::HashMap; #[derive(Debug, Serialize, Deserialize)] pub struct YVideo { pub id: String, pub title: String, pub formats: Option>, pub thumbnails: Vec, pub thumbnail: Option, pub description: String, pub channel_id: String, pub duration: Option, pub view_count: Option, pub average_rating: Option, pub age_limit: Option, pub webpage_url: String, pub categories: Option>, pub tags: Vec, pub playable_in_embed: Option, pub aspect_ratio: Option, pub width: Option, pub height: Option, pub automatic_captions: Option>>, pub comment_count: Option, pub chapters: Option>, pub heatmap: Option>, pub like_count: Option, pub channel: Option, pub channel_follower_count: Option, pub channel_is_verified: Option, pub uploader: Option, pub uploader_id: Option, pub uploader_url: Option, pub upload_date: Option, pub availability: Option, // "public" | "private" | "unlisted", pub original_url: Option, pub webpage_url_basename: String, pub webpage_url_domain: String, pub extractor: String, pub extractor_key: String, pub playlist_count: Option, pub playlist: Option, pub playlist_id: Option, pub playlist_title: Option, pub playlist_uploader: Option, pub playlist_uploader_id: Option, pub n_entries: Option, pub playlist_index: Option, pub display_id: Option, pub fulltitle: Option, pub duration_string: Option, pub is_live: Option, pub was_live: Option, pub epoch: usize, } #[derive(Debug, Serialize, Deserialize)] pub struct YCaption { pub url: Option, pub ext: String, //"vtt" | "json3" | "srv1" | "srv2" | "srv3" | "ttml", pub protocol: Option, pub name: Option, } #[derive(Debug, Serialize, Deserialize)] pub struct YFormat { pub format_id: String, pub format_note: Option, pub ext: String, pub protocol: String, pub acodec: Option, pub vcodec: Option, pub url: Option, pub width: Option, pub height: Option, pub fps: Option, pub columns: Option, pub fragments: Option>, pub resolution: String, pub dynamic_range: Option, pub aspect_ratio: Option, pub http_headers: HashMap, pub audio_ext: String, pub video_ext: String, pub vbr: Option, pub abr: Option, pub format: String, } #[derive(Debug, Serialize, Deserialize)] pub struct YFragment { pub url: Option, pub duration: Option, } #[derive(Debug, Serialize, Deserialize)] pub struct YThumbnail { pub url: String, pub preference: Option, pub id: String, pub height: Option, pub width: Option, pub resolution: Option, } #[derive(Debug, Serialize, Deserialize)] pub struct YChapter { pub start_time: f64, pub end_time: f64, pub title: String, } #[derive(Debug, Serialize, Deserialize)] pub struct YHeatmapSample { pub start_time: f64, pub end_time: f64, pub value: f64, } pub fn parse_upload_date(d: &str) -> anyhow::Result { let (year, month, day) = (&d[0..4], &d[4..6], &d[6..8]); let (year, month, day) = ( year.parse().context("parsing year")?, month.parse().context("parsing month")?, day.parse().context("parsing day")?, ); let mut p = Parsed::new(); p.year = Some(year); p.month = Some(month); p.day = Some(day); p.hour_div_12 = Some(0); p.hour_mod_12 = Some(0); p.minute = Some(0); p.second = Some(0); Ok(p.to_datetime_with_timezone(&Utc)?.timestamp_millis()) }