Skip to main content

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}