11//! Configuration models for YouTube uploader with validation.
22//!
33//! This module provides Serde-based models that mirror the Python Pydantic models,
4- //! supporting both legacy and batch YAML configuration formats.
4+ //! supporting both individual and batch YAML configuration formats.
55
66use anyhow:: { Result , anyhow} ;
77use futures:: future:: try_join_all;
@@ -14,7 +14,7 @@ use validator::{Validate, ValidationError};
1414/// Configuration format detection result
1515#[ derive( Debug , Clone , PartialEq ) ]
1616pub enum ConfigFormat {
17- Legacy ,
17+ Individual ,
1818 Batch ,
1919}
2020
@@ -67,6 +67,25 @@ pub struct VideoUploadOptions {
6767 pub category : u32 ,
6868 pub privacy_status : String ,
6969 pub playlist_id : String ,
70+ #[ serde( rename = "defaultAudioLanguage" ) ]
71+ pub default_audio_language : String ,
72+ #[ serde( rename = "defaultLanguage" ) ]
73+ pub default_language : String ,
74+ #[ serde( rename = "recordingDate" ) ]
75+ pub recording_date : String ,
76+ }
77+
78+ impl VideoUploadOptions {
79+ /// Convert recording_date from "YYYY-MM-DD" to YouTube API timestamp format "YYYY-MM-DDTHH:MM:SS.000Z"
80+ pub fn formatted_recording_date ( & self ) -> String {
81+ if self . recording_date . contains ( 'T' ) {
82+ // Already in timestamp format, return as-is
83+ self . recording_date . clone ( )
84+ } else {
85+ // Convert "YYYY-MM-DD" to "YYYY-MM-DDT00:00:00.000Z"
86+ format ! ( "{}T00:00:00.000Z" , self . recording_date)
87+ }
88+ }
7089}
7190
7291/// Valid YouTube video privacy status values.
@@ -227,6 +246,18 @@ pub struct CommonConfig {
227246 #[ validate( custom( function = "validate_playlist_id" ) ) ]
228247 #[ serde( rename = "playlistId" ) ]
229248 pub playlist_id : String ,
249+
250+ /// Default audio language for the video
251+ #[ serde( rename = "defaultAudioLanguage" ) ]
252+ pub default_audio_language : String ,
253+
254+ /// Default language for the video
255+ #[ serde( rename = "defaultLanguage" ) ]
256+ pub default_language : String ,
257+
258+ /// Recording date for the video
259+ #[ serde( rename = "recordingDate" ) ]
260+ pub recording_date : String ,
230261}
231262
232263impl CommonConfig {
@@ -239,7 +270,7 @@ impl CommonConfig {
239270 }
240271}
241272
242- /// Configuration for a single video (legacy format).
273+ /// Configuration for a single video (individual format).
243274#[ derive( Debug , Clone , Serialize , Deserialize , Validate ) ]
244275pub struct VideoConfig {
245276 /// Video title
@@ -269,11 +300,23 @@ pub struct VideoConfig {
269300 #[ validate( custom( function = "validate_playlist_id" ) ) ]
270301 #[ serde( rename = "playlistId" ) ]
271302 pub playlist_id : String ,
303+
304+ /// Default audio language for the video
305+ #[ serde( rename = "defaultAudioLanguage" ) ]
306+ pub default_audio_language : String ,
307+
308+ /// Default language for the video
309+ #[ serde( rename = "defaultLanguage" ) ]
310+ pub default_language : String ,
311+
312+ /// Recording date for the video
313+ #[ serde( rename = "recordingDate" ) ]
314+ pub recording_date : String ,
272315}
273316
274- /// Root model for legacy YAML format with videos array.
317+ /// Root model for individual YAML format with videos array.
275318#[ derive( Debug , Clone , Serialize , Deserialize , Validate ) ]
276- pub struct LegacyConfigRoot {
319+ pub struct IndividualConfigRoot {
277320 /// Test mode flag - if true, delete videos after upload
278321 #[ serde( default = "bool::default" ) ]
279322 pub test : bool ,
@@ -365,4 +408,36 @@ mod tests {
365408 assert ! ( validate_playlist_id( "invalid" ) . is_err( ) ) ;
366409 assert ! ( validate_playlist_id( "PL123" ) . is_err( ) ) ; // too short
367410 }
411+
412+ #[ test]
413+ fn test_formatted_recording_date ( ) {
414+ let options = VideoUploadOptions {
415+ file : "test.mp4" . to_string ( ) ,
416+ title : "Test" . to_string ( ) ,
417+ description : "Test" . to_string ( ) ,
418+ keywords : "test" . to_string ( ) ,
419+ category : 22 ,
420+ privacy_status : "private" . to_string ( ) ,
421+ playlist_id : "PL1234567890123456" . to_string ( ) ,
422+ default_audio_language : "en" . to_string ( ) ,
423+ default_language : "en" . to_string ( ) ,
424+ recording_date : "2026-01-24" . to_string ( ) ,
425+ } ;
426+
427+ // Should convert YYYY-MM-DD to YYYY-MM-DDT00:00:00.000Z
428+ assert_eq ! (
429+ options. formatted_recording_date( ) ,
430+ "2026-01-24T00:00:00.000Z"
431+ ) ;
432+
433+ // Already in timestamp format should remain unchanged
434+ let options_with_timestamp = VideoUploadOptions {
435+ recording_date : "2026-01-24T12:30:45.000Z" . to_string ( ) ,
436+ ..options
437+ } ;
438+ assert_eq ! (
439+ options_with_timestamp. formatted_recording_date( ) ,
440+ "2026-01-24T12:30:45.000Z"
441+ ) ;
442+ }
368443}
0 commit comments