aboutsummaryrefslogtreecommitdiffstats
path: root/yt_dlp/src/wrapper
diff options
context:
space:
mode:
Diffstat (limited to 'yt_dlp/src/wrapper')
-rw-r--r--yt_dlp/src/wrapper/info_json.rs526
-rw-r--r--yt_dlp/src/wrapper/mod.rs12
-rw-r--r--yt_dlp/src/wrapper/yt_dlp_options.rs62
3 files changed, 600 insertions, 0 deletions
diff --git a/yt_dlp/src/wrapper/info_json.rs b/yt_dlp/src/wrapper/info_json.rs
new file mode 100644
index 0000000..aceeeb8
--- /dev/null
+++ b/yt_dlp/src/wrapper/info_json.rs
@@ -0,0 +1,526 @@
+// yt - A fully featured command line YouTube client
+//
+// Copyright (C) 2024 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+// This file is part of Yt.
+//
+// You should have received a copy of the License along with this program.
+// If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+
+use std::{collections::HashMap, path::PathBuf};
+
+use pyo3::{types::PyDict, Bound, PyResult, Python};
+use serde::{Deserialize, Deserializer, Serialize};
+use serde_json::Value;
+use url::Url;
+
+use crate::json_loads_str;
+
+type Todo = String;
+
+// TODO: Change this to map `_type` to a structure of values, instead of the options <2024-05-27>
+// And replace all the strings with better types (enums or urls)
+#[derive(Debug, Deserialize, Serialize, PartialEq)]
+#[serde(deny_unknown_fields)]
+pub struct InfoJson {
+ pub __last_playlist_index: Option<u32>,
+ pub __post_extractor: Option<String>,
+ pub __x_forwarded_for_ip: Option<String>,
+ pub _filename: Option<PathBuf>,
+ pub _format_sort_fields: Option<Vec<String>>,
+ pub _has_drm: Option<Todo>,
+ pub _type: Option<InfoType>,
+ pub _version: Option<Version>,
+ pub abr: Option<f64>,
+ pub acodec: Option<String>,
+ pub age_limit: Option<u32>,
+ pub aspect_ratio: Option<f64>,
+ pub asr: Option<u32>,
+ pub audio_channels: Option<u32>,
+ pub audio_ext: Option<String>,
+ pub automatic_captions: Option<HashMap<String, Vec<Caption>>>,
+ pub availability: Option<String>,
+ pub average_rating: Option<String>,
+ pub categories: Option<Vec<String>>,
+ pub channel: Option<String>,
+ pub channel_follower_count: Option<u32>,
+ pub channel_id: Option<String>,
+ pub channel_is_verified: Option<bool>,
+ pub channel_url: Option<String>,
+ pub chapters: Option<Vec<Chapter>>,
+ pub comment_count: Option<u32>,
+ pub comments: Option<Vec<Comment>>,
+ pub concurrent_view_count: Option<u32>,
+ pub description: Option<String>,
+ pub display_id: Option<String>,
+ pub downloader_options: Option<DownloaderOptions>,
+ pub duration: Option<f64>,
+ pub duration_string: Option<String>,
+ pub dynamic_range: Option<String>,
+ pub entries: Option<Vec<InfoJson>>,
+ pub episode: Option<String>,
+ pub episode_number: Option<u32>,
+ pub epoch: Option<u32>,
+ pub ext: Option<String>,
+ pub extractor: Option<Extractor>,
+ pub extractor_key: Option<ExtractorKey>,
+ pub filename: Option<PathBuf>,
+ pub filesize: Option<u64>,
+ pub filesize_approx: Option<u64>,
+ pub format: Option<String>,
+ pub format_id: Option<String>,
+ pub format_note: Option<String>,
+ pub formats: Option<Vec<Format>>,
+ pub fps: Option<f64>,
+ pub fulltitle: Option<String>,
+ pub has_drm: Option<bool>,
+ pub heatmap: Option<Vec<HeatMapEntry>>,
+ pub height: Option<u32>,
+ pub http_headers: Option<HttpHeader>,
+ pub id: Option<String>,
+ pub ie_key: Option<ExtractorKey>,
+ pub is_live: Option<bool>,
+ pub language: Option<String>,
+ pub language_preference: Option<i32>,
+ pub license: Option<Todo>,
+ pub like_count: Option<u32>,
+ pub live_status: Option<String>,
+ pub location: Option<Todo>,
+ pub modified_date: Option<String>,
+ pub n_entries: Option<u32>,
+ pub original_url: Option<String>,
+ pub playable_in_embed: Option<bool>,
+ pub playlist: Option<Todo>,
+ pub playlist_autonumber: Option<u32>,
+ pub playlist_channel: Option<Todo>,
+ pub playlist_channel_id: Option<Todo>,
+ pub playlist_count: Option<u32>,
+ pub playlist_id: Option<Todo>,
+ pub playlist_index: Option<u64>,
+ pub playlist_title: Option<Todo>,
+ pub playlist_uploader: Option<Todo>,
+ pub playlist_uploader_id: Option<Todo>,
+ pub preference: Option<Todo>,
+ pub protocol: Option<String>,
+ pub quality: Option<f64>,
+ pub release_date: Option<String>,
+ pub release_timestamp: Option<u64>,
+ pub release_year: Option<u32>,
+ pub requested_downloads: Option<Vec<RequestedDownloads>>,
+ pub requested_entries: Option<Vec<u32>>,
+ pub requested_formats: Option<Vec<Format>>,
+ pub requested_subtitles: Option<HashMap<String, Subtitle>>,
+ pub resolution: Option<String>,
+ pub season: Option<String>,
+ pub season_number: Option<u32>,
+ pub series: Option<String>,
+ pub source_preference: Option<i32>,
+ pub sponsorblock_chapters: Option<Vec<SponsorblockChapter>>,
+ pub stretched_ratio: Option<Todo>,
+ pub subtitles: Option<HashMap<String, Vec<Caption>>>,
+ pub tags: Option<Vec<String>>,
+ pub tbr: Option<f64>,
+ pub thumbnail: Option<Url>,
+ pub thumbnails: Option<Vec<ThumbNail>>,
+ pub timestamp: Option<u64>,
+ pub title: Option<String>,
+ pub upload_date: Option<String>,
+ pub uploader: Option<String>,
+ pub uploader_id: Option<String>,
+ pub uploader_url: Option<String>,
+ pub url: Option<Url>,
+ pub vbr: Option<f64>,
+ pub vcodec: Option<String>,
+ pub video_ext: Option<String>,
+ pub view_count: Option<u32>,
+ pub was_live: Option<bool>,
+ pub webpage_url: Option<Url>,
+ pub webpage_url_basename: Option<String>,
+ pub webpage_url_domain: Option<String>,
+ pub width: Option<u32>,
+}
+
+#[derive(Debug, Deserialize, Serialize, PartialEq)]
+#[serde(deny_unknown_fields)]
+pub struct RequestedDownloads {
+ pub __files_to_merge: Option<Vec<Todo>>,
+ pub __finaldir: PathBuf,
+ pub __infojson_filename: PathBuf,
+ pub __postprocessors: Vec<Todo>,
+ pub __real_download: bool,
+ pub __write_download_archive: bool,
+ pub _filename: PathBuf,
+ pub _type: InfoType,
+ pub _version: Version,
+ pub abr: f64,
+ pub acodec: String,
+ pub aspect_ratio: f64,
+ pub asr: u32,
+ pub audio_channels: u32,
+ pub chapters: Option<Vec<SponsorblockChapter>>,
+ pub duration: Option<f64>,
+ pub dynamic_range: String,
+ pub ext: String,
+ pub filename: PathBuf,
+ pub filepath: PathBuf,
+ pub filesize_approx: u64,
+ pub format: String,
+ pub format_id: String,
+ pub format_note: String,
+ pub fps: f64,
+ pub height: u32,
+ pub infojson_filename: PathBuf,
+ pub language: Option<String>,
+ pub protocol: String,
+ pub requested_formats: Vec<Format>,
+ pub resolution: String,
+ pub tbr: f64,
+ pub vbr: f64,
+ pub vcodec: String,
+ pub width: u32,
+}
+
+#[derive(Debug, Deserialize, Serialize, PartialEq, PartialOrd, Ord, Eq)]
+#[serde(deny_unknown_fields)]
+pub struct Subtitle {
+ pub ext: SubtitleExt,
+ pub filepath: PathBuf,
+ pub name: String,
+ pub url: Url,
+}
+
+#[derive(Debug, Deserialize, Serialize, PartialEq, PartialOrd, Ord, Eq)]
+#[serde(deny_unknown_fields)]
+pub enum SubtitleExt {
+ #[serde(alias = "vtt")]
+ Vtt,
+
+ #[serde(alias = "json")]
+ Json,
+ #[serde(alias = "json3")]
+ Json3,
+
+ #[serde(alias = "ttml")]
+ Ttml,
+
+ #[serde(alias = "srv1")]
+ Srv1,
+ #[serde(alias = "srv2")]
+ Srv2,
+ #[serde(alias = "srv3")]
+ Srv3,
+}
+
+#[derive(Debug, Deserialize, Serialize, PartialEq, Eq, PartialOrd, Ord)]
+#[serde(deny_unknown_fields)]
+pub struct Caption {
+ pub ext: SubtitleExt,
+ pub name: Option<String>,
+ pub protocol: Option<String>,
+ pub url: String,
+ pub filepath: Option<PathBuf>,
+ pub video_id: Option<String>,
+}
+
+#[derive(Debug, Deserialize, Serialize, PartialEq, PartialOrd)]
+#[serde(deny_unknown_fields)]
+pub struct Chapter {
+ pub end_time: f64,
+ pub start_time: f64,
+ pub title: String,
+}
+
+#[derive(Debug, Deserialize, Serialize, PartialEq)]
+#[serde(deny_unknown_fields)]
+pub struct SponsorblockChapter {
+ /// This is an utterly useless field, and should thus be ignored
+ pub _categories: Option<Vec<Vec<Value>>>,
+
+ pub categories: Option<Vec<SponsorblockChapterCategory>>,
+ pub category: Option<SponsorblockChapterCategory>,
+ pub category_names: Option<Vec<String>>,
+ pub end_time: f64,
+ pub name: Option<String>,
+ pub r#type: Option<SponsorblockChapterType>,
+ pub start_time: f64,
+ pub title: String,
+}
+
+pub fn get_none<'de, D, T>(_: D) -> Result<Option<T>, D::Error>
+where
+ D: Deserializer<'de>,
+{
+ Ok(None)
+}
+
+#[derive(Debug, Deserialize, Serialize, PartialEq, PartialOrd, Ord, Eq)]
+#[serde(deny_unknown_fields)]
+pub enum SponsorblockChapterType {
+ #[serde(alias = "skip")]
+ Skip,
+
+ #[serde(alias = "chapter")]
+ Chapter,
+}
+#[derive(Debug, Deserialize, Serialize, PartialEq, PartialOrd, Ord, Eq)]
+#[serde(deny_unknown_fields)]
+pub enum SponsorblockChapterCategory {
+ #[serde(alias = "filler")]
+ Filler,
+
+ #[serde(alias = "sponsor")]
+ Sponsor,
+
+ #[serde(alias = "selfpromo")]
+ SelfPromo,
+
+ #[serde(alias = "chapter")]
+ Chapter,
+
+ #[serde(alias = "intro")]
+ Intro,
+
+ #[serde(alias = "outro")]
+ Outro,
+}
+
+#[derive(Debug, Deserialize, Serialize, PartialEq, PartialOrd)]
+#[serde(deny_unknown_fields)]
+pub struct HeatMapEntry {
+ pub start_time: f64,
+ pub end_time: f64,
+ pub value: f64,
+}
+
+#[derive(Debug, Deserialize, Serialize, PartialEq, PartialOrd, Ord, Eq)]
+#[serde(deny_unknown_fields)]
+pub enum Extractor {
+ #[serde(alias = "generic")]
+ Generic,
+
+ #[serde(alias = "SVTSeries")]
+ SVTSeries,
+
+ #[serde(alias = "youtube")]
+ YouTube,
+
+ #[serde(alias = "youtube:tab")]
+ YouTubeTab,
+}
+
+#[derive(Debug, Deserialize, Serialize, PartialEq, PartialOrd, Ord, Eq)]
+#[serde(deny_unknown_fields)]
+pub enum ExtractorKey {
+ #[serde(alias = "Generic")]
+ Generic,
+
+ #[serde(alias = "SVTSeries")]
+ SVTSeries,
+
+ #[serde(alias = "Youtube")]
+ YouTube,
+
+ #[serde(alias = "YoutubeTab")]
+ YouTubeTab,
+}
+
+#[derive(Debug, Deserialize, Serialize, PartialEq, PartialOrd, Ord, Eq)]
+#[serde(deny_unknown_fields)]
+pub enum InfoType {
+ #[serde(alias = "playlist")]
+ Playlist,
+
+ #[serde(alias = "url")]
+ Url,
+
+ #[serde(alias = "video")]
+ Video,
+}
+
+#[derive(Debug, Deserialize, Serialize, Eq, PartialEq, PartialOrd, Ord)]
+#[serde(deny_unknown_fields)]
+pub struct Version {
+ pub current_git_head: Option<String>,
+ pub release_git_head: String,
+ pub repository: String,
+ pub version: String,
+}
+
+#[derive(Debug, Deserialize, Serialize, Clone, Eq, PartialEq, PartialOrd, Ord)]
+#[serde(from = "String")]
+#[serde(deny_unknown_fields)]
+pub enum Parent {
+ Root,
+ Id(String),
+}
+
+impl Parent {
+ pub fn id(&self) -> Option<&str> {
+ if let Self::Id(id) = self {
+ Some(id)
+ } else {
+ None
+ }
+ }
+}
+
+impl From<String> for Parent {
+ fn from(value: String) -> Self {
+ if value == "root" {
+ Self::Root
+ } else {
+ Self::Id(value)
+ }
+ }
+}
+
+#[derive(Debug, Deserialize, Serialize, Clone, Eq, PartialEq, PartialOrd, Ord)]
+#[serde(from = "String")]
+#[serde(deny_unknown_fields)]
+pub struct Id {
+ pub id: String,
+}
+impl From<String> for Id {
+ fn from(value: String) -> Self {
+ Self {
+ // Take the last element if the string is split with dots, otherwise take the full id
+ id: value.split('.').last().unwrap_or(&value).to_owned(),
+ }
+ }
+}
+
+#[derive(Debug, Deserialize, Serialize, Clone, Eq, PartialEq, PartialOrd, Ord)]
+#[serde(deny_unknown_fields)]
+pub struct Comment {
+ pub id: Id,
+ pub text: String,
+ #[serde(default = "zero")]
+ pub like_count: u32,
+ pub is_pinned: bool,
+ pub author_id: String,
+ #[serde(default = "unknown")]
+ pub author: String,
+ pub author_is_verified: bool,
+ pub author_thumbnail: Url,
+ pub parent: Parent,
+ #[serde(deserialize_with = "edited_from_time_text", alias = "_time_text")]
+ pub edited: bool,
+ // Can't also be deserialized, as it's already used in 'edited'
+ // _time_text: String,
+ pub timestamp: i64,
+ pub author_url: Url,
+ pub author_is_uploader: bool,
+ pub is_favorited: bool,
+}
+fn unknown() -> String {
+ "<Unknown>".to_string()
+}
+fn zero() -> u32 {
+ 0
+}
+fn edited_from_time_text<'de, D>(d: D) -> Result<bool, D::Error>
+where
+ D: Deserializer<'de>,
+{
+ let s = String::deserialize(d)?;
+ if s.contains(" (edited)") {
+ Ok(true)
+ } else {
+ Ok(false)
+ }
+}
+
+#[derive(Debug, Deserialize, Serialize, PartialEq, Eq, PartialOrd, Ord)]
+#[serde(deny_unknown_fields)]
+pub struct ThumbNail {
+ pub id: Option<String>,
+ pub preference: Option<i32>,
+ /// in the form of "[`height`]x[`width`]"
+ pub resolution: Option<String>,
+ pub url: Url,
+ pub width: Option<u32>,
+ pub height: Option<u32>,
+}
+
+#[derive(Debug, Deserialize, Serialize, PartialEq, PartialOrd)]
+#[serde(deny_unknown_fields)]
+pub struct Format {
+ pub __needs_testing: Option<bool>,
+ pub __working: Option<bool>,
+ pub abr: Option<f64>,
+ pub acodec: Option<String>,
+ pub aspect_ratio: Option<f64>,
+ pub asr: Option<f64>,
+ pub audio_channels: Option<u32>,
+ pub audio_ext: Option<String>,
+ pub columns: Option<u32>,
+ pub container: Option<String>,
+ pub downloader_options: Option<DownloaderOptions>,
+ pub dynamic_range: Option<String>,
+ pub ext: String,
+ pub filepath: Option<PathBuf>,
+ pub filesize: Option<u64>,
+ pub filesize_approx: Option<u64>,
+ pub format: Option<String>,
+ pub format_id: String,
+ pub format_index: Option<String>,
+ pub format_note: Option<String>,
+ pub fps: Option<f64>,
+ pub fragment_base_url: Option<Todo>,
+ pub fragments: Option<Vec<Fragment>>,
+ pub has_drm: Option<bool>,
+ pub height: Option<u32>,
+ pub http_headers: Option<HttpHeader>,
+ pub is_dash_periods: Option<bool>,
+ pub language: Option<String>,
+ pub language_preference: Option<i32>,
+ pub manifest_stream_number: Option<u32>,
+ pub manifest_url: Option<Url>,
+ pub preference: Option<i32>,
+ pub protocol: Option<String>,
+ pub quality: Option<f64>,
+ pub resolution: Option<String>,
+ pub rows: Option<u32>,
+ pub source_preference: Option<i32>,
+ pub tbr: Option<f64>,
+ pub url: Url,
+ pub vbr: Option<f64>,
+ pub vcodec: String,
+ pub video_ext: Option<String>,
+ pub width: Option<u32>,
+}
+
+#[derive(Debug, Deserialize, Serialize, Eq, PartialEq, PartialOrd, Ord)]
+#[serde(deny_unknown_fields)]
+pub struct DownloaderOptions {
+ http_chunk_size: u64,
+}
+
+#[derive(Debug, Deserialize, Serialize, Eq, PartialEq, PartialOrd, Ord)]
+#[serde(deny_unknown_fields)]
+pub struct HttpHeader {
+ #[serde(alias = "User-Agent")]
+ pub user_agent: Option<String>,
+ #[serde(alias = "Accept")]
+ pub accept: Option<String>,
+ #[serde(alias = "Accept-Language")]
+ pub accept_language: Option<String>,
+ #[serde(alias = "Sec-Fetch-Mode")]
+ pub sec_fetch_mode: Option<String>,
+}
+
+#[derive(Debug, Deserialize, Serialize, PartialEq, PartialOrd)]
+#[serde(deny_unknown_fields)]
+pub struct Fragment {
+ pub url: Option<Url>,
+ pub duration: Option<f64>,
+ pub path: Option<PathBuf>,
+}
+
+impl InfoJson {
+ pub fn to_py_dict(self, py: Python) -> PyResult<Bound<PyDict>> {
+ let output: Bound<PyDict> = json_loads_str(py, self)?;
+ Ok(output)
+ }
+}
diff --git a/yt_dlp/src/wrapper/mod.rs b/yt_dlp/src/wrapper/mod.rs
new file mode 100644
index 0000000..3fe3247
--- /dev/null
+++ b/yt_dlp/src/wrapper/mod.rs
@@ -0,0 +1,12 @@
+// yt - A fully featured command line YouTube client
+//
+// Copyright (C) 2024 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+// This file is part of Yt.
+//
+// You should have received a copy of the License along with this program.
+// If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+
+pub mod info_json;
+// pub mod yt_dlp_options;
diff --git a/yt_dlp/src/wrapper/yt_dlp_options.rs b/yt_dlp/src/wrapper/yt_dlp_options.rs
new file mode 100644
index 0000000..c2a86df
--- /dev/null
+++ b/yt_dlp/src/wrapper/yt_dlp_options.rs
@@ -0,0 +1,62 @@
+// yt - A fully featured command line YouTube client
+//
+// Copyright (C) 2024 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+// This file is part of Yt.
+//
+// You should have received a copy of the License along with this program.
+// If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+
+use pyo3::{types::PyDict, Bound, PyResult, Python};
+use serde::Serialize;
+
+use crate::json_loads;
+
+#[derive(Serialize, Clone)]
+pub struct YtDlpOptions {
+ pub playliststart: u32,
+ pub playlistend: u32,
+ pub noplaylist: bool,
+ pub extract_flat: ExtractFlat,
+ // pub extractor_args: ExtractorArgs,
+ // pub format: String,
+ // pub fragment_retries: u32,
+ // #[serde(rename(serialize = "getcomments"))]
+ // pub get_comments: bool,
+ // #[serde(rename(serialize = "ignoreerrors"))]
+ // pub ignore_errors: bool,
+ // pub retries: u32,
+ // #[serde(rename(serialize = "writeinfojson"))]
+ // pub write_info_json: bool,
+ // pub postprocessors: Vec<serde_json::Map<String, serde_json::Value>>,
+}
+
+#[derive(Serialize, Copy, Clone)]
+pub enum ExtractFlat {
+ #[serde(rename(serialize = "in_playlist"))]
+ InPlaylist,
+
+ #[serde(rename(serialize = "discard_in_playlist"))]
+ DiscardInPlaylist,
+}
+
+#[derive(Serialize, Clone)]
+pub struct ExtractorArgs {
+ pub youtube: YoutubeExtractorArgs,
+}
+
+#[derive(Serialize, Clone)]
+pub struct YoutubeExtractorArgs {
+ comment_sort: Vec<String>,
+ max_comments: Vec<String>,
+}
+
+impl YtDlpOptions {
+ pub fn to_py_dict(self, py: Python) -> PyResult<Bound<PyDict>> {
+ let string = serde_json::to_string(&self).expect("This should always work");
+
+ let output: Bound<PyDict> = json_loads(py, string)?;
+ Ok(output)
+ }
+}