yt_dlp/metadata/base.rs
1//! Base metadata trait and common operations.
2//!
3//! This module provides the BaseMetadata trait with methods to extract and format
4//! metadata from Video and Format objects.
5
6use chrono::DateTime;
7
8use crate::model::Video;
9use crate::model::format::Format;
10
11/// Common metadata operations shared across different file formats.
12///
13/// This trait provides methods to extract and format metadata from Video and Format objects.
14pub trait BaseMetadata {
15 /// Extract basic metadata from a video.
16 ///
17 /// Basic metadata includes: title, artist (channel), album, genre (from tags), date/year
18 ///
19 /// # Arguments
20 ///
21 /// * `video` - The video to extract metadata from
22 ///
23 /// # Returns
24 ///
25 /// Vector of (key, value) metadata pairs
26 fn extract_basic_metadata(video: &Video) -> Vec<(String, String)> {
27 let mut metadata = vec![("title".to_string(), video.title.clone())];
28
29 Self::add_metadata_if_some(&mut metadata, "artist", video.channel.clone());
30 Self::add_metadata_if_some(&mut metadata, "album_artist", video.channel.clone());
31 Self::add_metadata_if_some(&mut metadata, "album", video.channel.clone());
32
33 // Add tags as genre
34 if !video.tags.is_empty() {
35 metadata.push(("genre".to_string(), video.tags.join(", ")));
36 }
37
38 // Add dates
39 if let Some(timestamp) = video.upload_date.filter(|&t| t > 0) {
40 if let Some(date_str) = Self::format_timestamp(timestamp, "%Y-%m-%d") {
41 metadata.push(("date".to_string(), date_str));
42 }
43 if let Some(year_str) = Self::format_timestamp(timestamp, "%Y") {
44 metadata.push(("year".to_string(), year_str));
45 }
46 }
47
48 metadata
49 }
50
51 /// Extract video format metadata.
52 ///
53 /// Video format metadata includes: resolution, FPS, video codec, video bitrate
54 ///
55 /// # Arguments
56 ///
57 /// * `format` - The format to extract metadata from
58 ///
59 /// # Returns
60 ///
61 /// Vector of (key, value) metadata pairs
62 fn extract_video_format_metadata(format: &Format) -> Vec<(String, String)> {
63 let mut metadata = Vec::new();
64
65 // Resolution
66 if let (Some(width), Some(height)) = (format.video_resolution.width, format.video_resolution.height) {
67 metadata.push(("resolution".to_string(), format!("{}x{}", width, height)));
68 }
69
70 // FPS
71 Self::add_metadata_if_some(&mut metadata, "framerate", format.video_resolution.fps);
72
73 // Video codec
74 Self::add_metadata_if_some(&mut metadata, "video_codec", format.codec_info.video_codec.clone());
75
76 // Video bitrate
77 Self::add_metadata_if_some(&mut metadata, "video_bitrate", format.rates_info.video_rate);
78
79 metadata
80 }
81
82 /// Extract audio format metadata.
83 ///
84 /// Audio format metadata includes: audio bitrate, audio codec, audio channels, sample rate
85 ///
86 /// # Arguments
87 ///
88 /// * `format` - The format to extract metadata from
89 ///
90 /// # Returns
91 ///
92 /// Vector of (key, value) metadata pairs
93 fn extract_audio_format_metadata(format: &Format) -> Vec<(String, String)> {
94 let mut metadata = Vec::new();
95
96 // Audio bitrate
97 Self::add_metadata_if_some(&mut metadata, "audio_bitrate", format.rates_info.audio_rate);
98
99 // Audio codec
100 Self::add_metadata_if_some(&mut metadata, "audio_codec", format.codec_info.audio_codec.clone());
101
102 // Audio channels
103 Self::add_metadata_if_some(&mut metadata, "audio_channels", format.codec_info.audio_channels);
104
105 // Sample rate
106 Self::add_metadata_if_some(&mut metadata, "audio_sample_rate", format.codec_info.asr);
107
108 metadata
109 }
110
111 /// Format a timestamp into a string according to a specified format.
112 ///
113 /// # Arguments
114 ///
115 /// * `timestamp` - Unix timestamp to format
116 /// * `format_str` - Format string (e.g., "%Y-%m-%d" for date, "%Y" for year)
117 ///
118 /// # Returns
119 ///
120 /// Formatted string if the timestamp is valid, None otherwise
121 fn format_timestamp(timestamp: i64, format_str: &str) -> Option<String> {
122 DateTime::from_timestamp(timestamp, 0).map(|dt| dt.format(format_str).to_string())
123 }
124
125 /// Add metadata to a vector if the value exists.
126 ///
127 /// # Arguments
128 ///
129 /// * `metadata` - Vector to add the metadata to
130 /// * `key` - Metadata key
131 /// * `value` - Optional value to add
132 fn add_metadata_if_some<T: ToString>(metadata: &mut Vec<(String, String)>, key: &str, value: Option<T>) {
133 if let Some(value) = value {
134 let value_str = value.to_string();
135 metadata.push((key.to_string(), value_str));
136 }
137 }
138}